我正在尝试构建一个AngularJS应用程序,我正在使用jQuery UI手风琴控件。
问题是,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;
};
});
有没有办法告诉手风琴“等待”初始化,直到从服务返回数据? : - )
提前致谢!
更新
我尝试链接方法并添加了一些日志记录,看起来手风琴实际上是在从服务返回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>
但是标记的其余部分(用角度数据绑定)并没有启动。
完成标记:
<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>
解
最后我找到了解决这个问题的方法!我不完全确定它是否漂亮,如果它是Angular方式的做事(我猜它不是)
使用以下代码制作指令:
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手风琴。
非常感谢@Sgoldy将我指向正确的方向!
答案 0 :(得分:12)
是的,你需要一个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-body
HTML
。
在generateAccordion
方法中,我使用$timeout
确保ng-repeat
真正完成呈现,因为$timeout
将在当前摘要周期结束时执行。
检查 Demo
答案 1 :(得分:1)
我的最佳做法是在启动控制器之前解决异步服务。
正如您在文档中看到的那样,http://docs.angularjs.org/api/ngRoute。$ routeProvider
resolve - {Object。=} - 可选的地图 应该注入控制器的依赖项。如果有的话 这些依赖是承诺,路由器将等待它们全部到 在控制器之前被解决或被拒绝 实例化。如果所有承诺都成功解决了,那么 注入的Promises的值被注入并且$ 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