2014-01-17 5 views
8

В настоящее время я пытаюсь создать приложение AngularJS, где я использую jQuery UI-аккордеон.Заполнение jQuery UI-аккордеона после вызова службы AngularJS

Проблема заключается в том, что инициируется аккордеон jQuery UI до мой сервис AngularJS выполняется с загрузкой данных с сервера. Другими словами: аккордеон не имеет никаких данных, когда он инициирован и, следовательно, не показывает, когда данные из AngularJS заселены.

мнение выглядит следующим образом:

<!-- Pretty standard accordion markup omitted --> 
$("#b2b-line-accordion").togglepanels(); 

Мой AngularJS контроллер выглядит следующим образом:

app.controller('orderController', function ($scope, orderService, userService) { 
// Constructor for this controller 
init(); 

function init() { 
    $scope.selected = {}; 
    $scope.totalSum = 0.00; 
    $scope.shippingDate = ""; 
    $scope.selectedShippingAddress = ""; 
    $scope.orderComment = ""; 
    $scope.agreements = false; 
    $scope.passwordResetSuccess = false; 
    $scope.passwordResetError = true; 

    userService.getCurrentUser(2).then(function (response) { 
     $scope.user = response.data; 

     orderService.getProductCategoriesWithProducts($scope.user).then(function (d) { 
      $scope.categories = d.data; 
     }); 
    }); 
} 

// Other methods omitted 
}); 

И мои AngularJS услуги выглядит следующим образом:

app.service('orderService', function ($http) { 
    this.getProductCategoriesWithProducts = function (user) { 
     return $http.post('url to my service', user); 
    }; 
}); 

app.service('userService', function ($http) { 
    this.getCurrentUser = function(companyId) { 
     return $http.get('url to my service' + companyId + '.aspx'); 
    }; 

    this.resetPassword = function() { 
     return true; 
    }; 
}); 

Есть ли способ сказать аккордеон «ждать», чтобы инициализировать, пока данные не будут возвращены из службы? :-)

Заранее благодарен!

Update

Я попытался цепочки методов и добавил некоторые протоколирования и, кажется, что аккордеон фактически инициированного после того, как JSON возвращается из службы.

userService.getCurrentUser(2).then(function(response) { 
     $scope.user = response.data; 
    }).then(function() { 
     orderService.getProductCategoriesWithProducts($scope.user).then(function(d) { 
      $scope.categories = d.data; 
      console.log("categories loaded"); 
     }).then(function() { 
      $("#b2b-line-accordion").accordion(); 
      console.log("accordion loaded"); 
     }); 
    }); 

Однако, не отображается на аккордеоне :-(Первый аккордеона DIV выглядит хорошо в сгенерированном DOM:

<div id="b2b-line-accordion" class="ui-accordion ui-widget ui-helper-reset" role="tablist"> 
    ... 
</div> 

Но остальная часть разметки (которая DataBound с угловой) itsn't инициировал

Полная разметка:.

<div id="b2b-line-accordion"> 
    <div ng-repeat="productCategory in categories"> 
     <h3>{{ productCategory.CategoryName }}</h3> 
     <div class="b2b-line-wrapper"> 
      <table> 
       <tr> 
         <th>Betegnelse</th> 
         <th>Str.</th> 
         <th>Enhed</th> 
         <th>HF varenr.</th> 
         <th>Antal</th> 
         <th>Bemærkninger</th> 
         <th>Beløb</th> 
       </tr> 
       <tr ng-repeat="product in productCategory.Products"> 
        <td>{{ product.ItemGroupName }}</td> 
        <td>{{ product.ItemAttribute }}</td> 
        <td> 
         <select ng-model="product.SelectedVariant" 
           ng-options="variant as variant.VariantUnit for variant in product.Variants" 
           ng-init="product.SelectedVariant = product.Variants[0]" 
           ng-change="calculateLinePrice(product); calculateTotalPrice();"> 
         </select> 
        </td> 
        <td>{{ product.ItemNumber }}</td> 
        <td class="line-amount"> 
         <span class="ensure-number-label" ng-show="product.IsNumOfSelectedItemsValid">Indtast venligst et tal</span> 
         <input type="number" class="line-amount" name="amount" min="0" ng-change="ensureNumber(product); calculateLinePrice(product); calculateTotalPrice();" ng-model="product.NumOfSelectedItems" value="{{ product.NumOfSelectedItems }}" /> 
        <td> 
         <input type="text" name="line-comments" ng-model="product.UserComment" value="{{ product.UserComment }}" /></td> 
        <td><span class="line-sum">{{ product.LinePrice | currency:"" }}</span></td> 
       </tr> 
      </table> 
    </div> 
</div> 
</div> 

Soluti ON

Наконец-то я нашел способ обойти это! Я не совсем уверен, если это так красиво и, если это радиально-способ делать вещи (я предполагаю, что это не так)

Сделано директиву со следующим кодом:

app.directive('accordion', function() { 
    return { 
     restrict: 'A', 
     link: function ($scope, $element, attrs) { 
      $(document).ready(function() { 
       $scope.$watch('categories', function() { 
        if ($scope.categories != null) { 
         $element.accordion(); 
        } 
       }); 
      }); 
     } 
    }; 
}); 

Так в основном, когда DOM готов, и когда изменяется массив категорий (который он делает, когда данные были загружены), я запускаю аккордеон jQuery UI.

Большое спасибо t @Sgoldy за то, что указали мне в правильном направлении!

+0

Вы извлекаете из [адаптера Jquery UI] УГЛОВОГО (в https://github.com/angular/angular-jquery-ui)? Я использовал один, сделанный другой группой для [jQuery Mobile] (https://github.com/opitzconsulting/jquery-mobile-angular-adapter), который предоставил угловую директиву, и он работал хорошо. Я вижу, что вы нашли решение, но это может следовать за угловым соглашением немного лучше. – cazzer

ответ

10

Да, вам нужен directive, и вы можете справиться с этим более угловатым способом!

В HTML Определим директиву

<div ui-accordion="accordionData" ></div> 

Возвращение promise из вашего service и передать promise в директиве.

В контроллере

$scope.accordionData = myService.getAccordionData(); 

директива ui-accordion выглядит

.directive('uiAccordion', function($timeout) { 
return { 
    scope:{ 
    myAccordionData: '=uiAccordion' 
    }, 
    template: '<div ng-repeat="item in myData"><h3 ng-bind="item.title"></h3><div><p ng-bind="item.data"></p></div></div>', 
    link: function(scope, element) { 
    scope.myAccordionData.then(function(data) { 
     scope.myData = data; 
     generateAccordion(); 
    }); 

    var generateAccordion = function() { 
     $timeout(function() { //<--- used $timeout to make sure ng-repeat is REALLY finished 
     $(element).accordion({ 
      header: "> div > h3" 
     }); 
     }); 
    } 
    } 
    } 
}) 

Когда вызов службы успеха then вы создаете свой аккордеон. Здесь вы можете определить свой собственный accordion-template как

<div ng-repeat="item in myData"> 
    <h3 ng-bind="item.title"></h3> 
    <div> 
    <p ng-bind="item.data"></p> 
    </div> 
</div> 

Шаблон связывается с модельными данными myData. Я использую ng-repeat внутри шаблона для создания accordion-header и accordion-bodyHTML.

В методе generateAccordion я использую $timeout, чтобы убедиться, что ng-repeat действительно закончил рендеринг, потому что $timeout будет выполняться в конце текущего цикла переваривать.

Проверить Demo

+0

Привет, Reza, большое спасибо за ваш пример, это очень ценится и имеет смысл! :-) Мне тоже нравится это решение. Единственное, что я всегда находил немного «шатким» (из-за отсутствия лучшего слова), использует тайм-аут, чтобы ждать завершения асинхронных вызовов. Что, если сервер по какой-то причине медленный и занимает больше времени, чем таймаут? :-) Кроме этого, я думаю, что это блестящий пример! – bomortensen

+0

@bomortensen может быть вам понятен, я использую '$ timeout' в моем сервисе для имитации ** поддельного вызова $ http **. Этот кодовый блок '$ timeout (function() { deferred.resolve (data); }, 1000);' будет заменен вашей реальной реализацией. И проверьте мой обновленный ответ, почему я использую '$ timeout' внутри моей директивы – Reza

1

Моя лучшая практика заключается в том, чтобы разрешить ваши асинхронные службы до запуска контроллера. .

Как вы можете видеть в этом документе, http://docs.angularjs.org/api/ngRoute $ routeProvider

решительность - {Object.=} - необязательная карта зависимостей, которые должны быть введены в контроллер. Если какой-либо из этих зависимостей является обещанием, маршрутизатор будет ждать их всех до или будет отклонен до того, как будет создан экземпляр контроллера . Если все обещания успешно разрешены, значения объявленных обещаний вводятсяи запускается событие $ routeChangeSuccess . Если какое-либо из обещаний отклонено, будет запущено событие $ routeChangeError.

Ваш контроллер и просмотр не будут запущены до того, как ваша служба будет разрешена или отклонена.

Существует хороший видео-учебник об этом, https://egghead.io/lessons/angularjs-resolve

В вашем случае, вы можете сконфигурировать маршруты как следующий

var myApp = angular.module('myApp', ['ngRoute']); 
myApp.config(function($routeProvider) { 
    $routeProvider.when('/', { 
    templateUrl: 'main.html', 
    controller: orderController, 
    resolve: { 
     categories: function(orderService) { 
     return orderService.getProductCategoriesWithProducts(); 
     }, 
     user: function(userService) { 
     return userService.getCurrentUser(); 
     } 
    } 
    }); 

Затем, с вашим контроллером

app.controller('orderController', function($scope, categories, user) { 
    //categories and user is always here, so use it. 
}); 

У меня есть также нашли аналогичный вопрос и ответ here

+0

Привет, allenhwkim, большое спасибо за ваш ответ!Я многому научился у него :-) Не знал о методе разрешения, он уверен, что это лучший способ обеспечить загрузку данных до того, как контроллер будет использоваться. Единственное препятствие, которое у меня есть сейчас, это то, что мои взгляды динамичны и происходят из CMS Umbraco, поэтому настройка templateUrl - это еще одна проблема, о которой мне нужно позаботиться сейчас ;-) – bomortensen

+0

Для стороннего шаблона шаблона вы можете установить свой собственный сервер, чтобы предоставить URL-адрес, который позволяет CORS (не пробовал этот путь) или [динамический url] (http://stackoverflow.com/questions/18423054/dynamically-loading-the-controller-in-angularjs-routeprovider). – allenhwkim

Смежные вопросы