【问题标题】:Populate jQuery UI accordion after AngularJS service call在 AngularJS 服务调用后填充 jQuery UI 手风琴
【发布时间】:2014-01-17 16:14:13
【问题描述】:

我目前正在尝试构建一个使用 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;
    };
});

有没有办法告诉手风琴“等待”初始化,直到数据从服务返回? :-)

提前致谢!

更新

我尝试链接方法并添加一些日志记录,似乎手风琴实际上是在从服务返回 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 为我指明了正确的方向!

【问题讨论】:

  • 你查看过 Angular 的 jQuery UI adapter 吗?我使用了另一组为jQuery Mobile 制作的一个,它提供了一个角度指令并且效果很好。我看到您找到了解决方案,但这可能会更好地遵循角度约定。

标签: javascript jquery jquery-ui angularjs


【解决方案1】:

是的,你需要一个directive,你可以处理这种更有棱角的方式!

HTML 中定义指令

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

从您的service 返回promise 并将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-templatelike

<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-headeraccordion-body HTML

generateAccordion 方法中,我使用$timeout 来确保ng-repeat 真正完成渲染,因为$timeout 将在当前摘要周期结束时执行。

检查Demo

【讨论】:

  • 嗨,Reza,非常感谢您的示例,非常感谢您,这很有意义! :-) 我也喜欢那个解决方案。我一直觉得有点“不稳定”(因为没有更好的词)的唯一一件事是使用超时来等待异步调用完成。如果服务器由于某种原因很慢并且花费的时间比超时时间长怎么办? :-) 除此之外,我认为这是一个很好的例子!
  • @bomortensen 可能是你的误解,我在我的服务中使用$timeout 来模拟 fake $http 调用。此代码块$timeout(function() { deferred.resolve(data); }, 1000); 将替换为您的实际实现。并检查我更新的答案为什么我在指令中使用$timeout
【解决方案2】:

我的最佳做法是在控制器启动之前解决您的异步服务。

正如您在文档中看到的,http://docs.angularjs.org/api/ngRoute.$routeProvider

resolve - {Object.=} - 可选映射 应该注入控制器的依赖项。如果有任何一个 这些依赖是承诺,路由器将等待它们全部 在控制器被解决之前被解决或被拒绝 实例化。如果所有的 Promise 都成功解决, 已解决的承诺的值被注入并 $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

【讨论】:

  • 您好 allenhwkim,非常感谢您的回答!我从中学到了很多 :-) 不知道 resolve 方法,这似乎是确保在控制器投入使用之前加载数据的最佳方法。我现在唯一的障碍是,我的观点是动态的并且来自 Umbraco CMS,所以设置 templateUrl 是我现在需要处理的另一个“问题”;-)
  • 对于 3rd 方模板 url,您可以设置自己的服务器提供允许 CORS 的 url(没有尝试过这种方式)或dynamic url
猜你喜欢
  • 1970-01-01
  • 2011-01-31
  • 2016-06-13
  • 1970-01-01
  • 1970-01-01
  • 2012-07-25
  • 1970-01-01
  • 2014-03-20
  • 1970-01-01
相关资源
最近更新 更多