te')); return $arr; } /* 遍历用户所有主题 * @param $uid 用户ID * @param int $page 页数 * @param int $pagesize 每页记录条数 * @param bool $desc 排序方式 TRUE降序 FALSE升序 * @param string $key 返回的数组用那一列的值作为 key * @param array $col 查询哪些列 */ function thread_tid_find_by_uid($uid, $page = 1, $pagesize = 1000, $desc = TRUE, $key = 'tid', $col = array()) { if (empty($uid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('uid' => $uid), array('tid' => $orderby), $page, $pagesize, $key, $col); return $arr; } // 遍历栏目下tid 支持数组 $fid = array(1,2,3) function thread_tid_find_by_fid($fid, $page = 1, $pagesize = 1000, $desc = TRUE) { if (empty($fid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('fid' => $fid), array('tid' => $orderby), $page, $pagesize, 'tid', array('tid', 'verify_date')); return $arr; } function thread_tid_delete($tid) { if (empty($tid)) return FALSE; $r = thread_tid__delete(array('tid' => $tid)); return $r; } function thread_tid_count() { $n = thread_tid__count(); return $n; } // 统计用户主题数 大数量下严谨使用非主键统计 function thread_uid_count($uid) { $n = thread_tid__count(array('uid' => $uid)); return $n; } // 统计栏目主题数 大数量下严谨使用非主键统计 function thread_fid_count($fid) { $n = thread_tid__count(array('fid' => $fid)); return $n; } ?>javascript - Populate jQuery UI accordion after AngularJS service call - Stack Overflow
最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Populate jQuery UI accordion after AngularJS service call - Stack Overflow

programmeradmin1浏览0评论

I'm currently trying to build an AngularJS app where I'm using a jQuery UI accordion control.

The problem is, that the jQuery UI accordion is initiated before my AngularJS service is done loading data from the server. In other words: the accordion doesn't have any data when it's initiated and thus does not show when the data from AngularJS is populated.

The view looks like this:

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

My AngularJS controller looks like this:

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
});

And my AngularJS services looks like this:

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(panyId) {
        return $http.get('url to my service' + panyId + '.aspx');
    };

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

Is there any way to tell the accordion to "wait" to initialise until the data is returned from the service? :-)

Thanks in advance!

Update

I tried chaining the methods and added some logging and it seems that the accordion is in fact initiated after the JSON is returned from the service.

    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");
        });
    });

However, it doesn't display the accordion :-( The first accordion div looks fine in the generated DOM:

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

But the rest of the markup (which is databound with angular) itsn't initiated.

Complete markup:

<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-ments" ng-model="product.UserComment" value="{{ product.UserComment }}" /></td>
                    <td><span class="line-sum">{{ product.LinePrice | currency:"" }}</span></td>
                 </tr>
           </table>
   </div>
 </div>
</div>

SOLUTION

Finally I found a way around this! I'm not entirely sure if it's that pretty and if it's the Angular-way of doing stuff (I guess it isn't)

Made a directive with the following code:

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();
                    }
                });
            });
        }
    };
});

So basically when the DOM is ready and when the categories array changes (which it does when the data has been loaded), I'm initiating the jQuery UI accordion.

Thanks a lot t @Sgoldy for pointing me in the right direction here!

I'm currently trying to build an AngularJS app where I'm using a jQuery UI accordion control.

The problem is, that the jQuery UI accordion is initiated before my AngularJS service is done loading data from the server. In other words: the accordion doesn't have any data when it's initiated and thus does not show when the data from AngularJS is populated.

The view looks like this:

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

My AngularJS controller looks like this:

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
});

And my AngularJS services looks like this:

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(panyId) {
        return $http.get('url to my service' + panyId + '.aspx');
    };

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

Is there any way to tell the accordion to "wait" to initialise until the data is returned from the service? :-)

Thanks in advance!

Update

I tried chaining the methods and added some logging and it seems that the accordion is in fact initiated after the JSON is returned from the service.

    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");
        });
    });

However, it doesn't display the accordion :-( The first accordion div looks fine in the generated DOM:

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

But the rest of the markup (which is databound with angular) itsn't initiated.

Complete markup:

<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-ments" ng-model="product.UserComment" value="{{ product.UserComment }}" /></td>
                    <td><span class="line-sum">{{ product.LinePrice | currency:"" }}</span></td>
                 </tr>
           </table>
   </div>
 </div>
</div>

SOLUTION

Finally I found a way around this! I'm not entirely sure if it's that pretty and if it's the Angular-way of doing stuff (I guess it isn't)

Made a directive with the following code:

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();
                    }
                });
            });
        }
    };
});

So basically when the DOM is ready and when the categories array changes (which it does when the data has been loaded), I'm initiating the jQuery UI accordion.

Thanks a lot t @Sgoldy for pointing me in the right direction here!

Share Improve this question edited Jan 19, 2014 at 19:03 bomortensen asked Jan 17, 2014 at 16:14 bomortensenbomortensen 3,39610 gold badges57 silver badges77 bronze badges 1
  • Have you checkout out Angular's jQuery UI adapter? I used one made by another group for jQuery Mobile that provided an angular directive and it worked well. I see that you've found a solution but this may follow angular convention a bit better. – cazzer Commented Jan 24, 2014 at 19:27
Add a ment  | 

2 Answers 2

Reset to default 12 +100

Yes you need a directive and you can handle this more angular way !

In HTML define the directive

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

Return promise from your service and pass the promise to the directive.

In controller

$scope.accordionData = myService.getAccordionData();

The ui-accordion directive looks like

.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"
        });
       });
     }
   }
  }
})

When your service call succeed then you create your accordion. Here you can define your own accordion-template like

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

Template binds with your model data myData. I use ng-repeat inside the template to create accordion-header and accordion-body HTML.

In the generateAccordion method i use $timeout to make sure the ng-repeat is really finished rendering because $timeout will execute at the end of the current digest cycle.

Check the Demo

My best practice is to resolve your asynchronous services before controller is initiated.

As you can see in the document, http://docs.angularjs/api/ngRoute.$routeProvider

resolve - {Object.=} - An optional map of dependencies which should be injected into the controller. If any of these dependencies are promises, the router will wait for them all to be resolved or one to be rejected before the controller is instantiated. If all the promises are resolved successfully, the values of the resolved promises are injected and $routeChangeSuccess event is fired. If any of the promises are rejected the $routeChangeError event is fired.

Your controller and view won't be even started before your service is resolved or rejected.

There is a good video tutorial about this, https://egghead.io/lessons/angularjs-resolve

In your case, you can config routes like the following

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();
      }
    }
  });

Then, with your controller

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

I have also found a similar question and answer here

发布评论

评论列表(0)

  1. 暂无评论