2014-01-17 9 views
8

Ich versuche gerade eine AngularJS App zu erstellen, wo ich eine jQuery UI Akkordeonsteuerung verwende.Füllen Sie jQuery UI Akkordeon nach AngularJS Service Anruf

Das Problem ist, dass die jQuery UI Akkordeon initiiert wird vor mein AngularJS-Dienst ist Laden von Daten vom Server fertig. Mit anderen Worten: Das Akkordeon hat beim Initiieren keine Daten und zeigt somit nicht an, wenn die Daten von AngularJS aufgefüllt sind.

Der Blick sieht wie folgt aus:

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

Und meine AngularJS Dienste sieht wie folgt aus:

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

Gibt es eine Möglichkeit

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

Mein AngularJS Controller sieht wie folgt aus dem Akkordeon zu sagen, dass es "warten" soll, bis die Daten vom Dienst zurückgegeben werden? :-)

Vielen Dank im Voraus!

aktualisieren

Ich habe versucht, die Methoden Verkettungs und einige Protokollierung hinzugefügt und es scheint, dass das Akkordeon in der Tat nach dem JSON vom Dienst zurück eingeleitet ist.

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

Es ist jedoch nicht das Akkordeon nicht angezeigt :-(Das erste Akkordeon div sieht in der generierten DOM fein:

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

Aber der Rest des Markup (die mit Winkeldatengebundene wird) itsn't initiiert

komplette 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-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

Endlich habe ich einen Weg gefunden! Ich bin nicht ganz sicher, ob es das ist ziemlich ist und wenn es die Angular-Wege-Sachen zu tun (ich denke, es ist nicht)

eine Richtlinie mit dem folgenden Code gemacht:

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

Also im Grunde, wenn Das DOM ist fertig und wenn sich das Kategorien-Array ändert (was es tut, wenn die Daten geladen wurden), initiiere ich das jQuery UI-Akkordeon.

Vielen Dank t @Sgoldy für mich in die richtige Richtung hier!

+0

Haben Sie Prüfung aus Angular des [jQuery UI-Adapter] (https://github.com/angular/angular-jquery-ui)? Ich habe einen von einer anderen Gruppe für [jQuery Mobile] (https://github.com/opitzconsulting/jquery-mobile-angular-adapter) verwendet, der eine eckige Anweisung lieferte und es hat gut funktioniert. Ich sehe, dass Sie eine Lösung gefunden haben, aber dies kann der Winkelkonvention ein bisschen besser folgen. – cazzer

Antwort

10

Ja, Sie brauchen eine directive und Sie können mit diesem mehr eckigen Weg umgehen!

In HTML definieren die Richtlinie

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

Zurück promise von Ihrem service und die promise der Richtlinie übergeben.

In Controller

$scope.accordionData = myService.getAccordionData(); 

Die ui-accordion Richtlinie sieht aus wie

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

Wenn Ihr Service-Aufruf then gelingt es Ihnen, Ihr Akkordeon erstellen. Hier können Sie Ihren eigenen accordion-template wie

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

Template bindet mit Ihren Modelldaten myData definieren. Ich verwende ng-repeat innerhalb der Vorlage, um accordion-header und accordion-bodyHTML zu erstellen.

In generateAccordion Methode i $timeout sicherzustellen, dass die ng-repeat wirklich Rendering ist abgeschlossen, weil $timeout am Ende des aktuellen verdauen Zyklus ausgeführt wird.

Überprüfen Sie die Demo

+0

Hi Reza, vielen Dank für Ihr Beispiel, es ist sehr geschätzt und macht Sinn! :-) Ich mag diese Lösung auch. Das einzige, was ich immer etwas "wackelig" gefunden habe (wegen des Fehlens eines besseren Wortes), ist die Verwendung von Timeout, um auf asynchrone Anrufe zu warten, um zu beenden. Was ist, wenn der Server aus irgendeinem Grund langsam ist und länger als das Timeout dauert? :-) Ansonsten denke ich, es ist ein brillantes Beispiel! – bomortensen

+0

@bomortensen können Sie Miss-verstanden werden, verwende ich '$ Timeout' in meinem Dienst zu simulieren ** gefälschte $ http ** Anruf. Dieser Codeblock '$ timeout (function() { deferred.resolve (data); }, 1000);' wird durch Ihre reale Implementierung ersetzt. Und überprüfe meine aktualisierte Antwort, warum ich '$ timeout' in meiner Direktive verwende – Reza

1

Meine beste Vorgehensweise besteht darin, Ihre asynchronen Dienste vor der Initialisierung des Controllers aufzulösen. .

Wie Sie in dem Dokument sehen können, http://docs.angularjs.org/api/ngRoute $ routeProvider

Entschlossenheit - {Objekt.=} - Eine optionale Karte von Abhängigkeiten, die in den Controller injiziert werden sollen. Wenn diese Abhängigkeiten versprechen, wird der Router darauf warten, dass sie alle aufgelöst werden oder eine zurückgewiesen wird, bevor der Controller instanziiert wird. Wenn alle Versprechungen erfolgreich gelöst wurden, werden die Werte der aufgelösten Versprechungen injiziert und das Ereignis $ routeChangeSuccess wird ausgelöst. Wenn eines der Versprechen abgelehnt wird, wird das $ routeChangeError-Ereignis ausgelöst.

Ihr Controller und Ihre Ansicht werden nicht einmal gestartet, bevor Ihr Dienst aufgelöst oder zurückgewiesen wird.

Es gibt eine gute Video-Tutorial darüber, https://egghead.io/lessons/angularjs-resolve

In Ihrem Fall können Sie Routen Config wie folgt

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

Dann mit dem Controller

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

Ich habe fand auch eine ähnliche Frage und Antwort here

+0

Hallo Allenhwkim, vielen Dank für deine Antwort!Ich habe eine Menge daraus gelernt :-) Da mir die Auflösungsmethode nicht bekannt war, scheint es sicher der beste Weg zu sein, sicherzustellen, dass Daten geladen werden, bevor der Controller verwendet wird. Die einzige Hürde, die ich jetzt habe, ist, dass meine Ansichten dynamisch sind und von Umbraco CMS stammen, also ist das Einstellen der templateUrl ein weiteres "Problem", um das ich mich jetzt kümmern muss ;-) – bomortensen

+0

Für die URL der Drittanbieter-Vorlage können Sie Ihre eigener Server zur Bereitstellung von URLs, die CORS (nicht auf diese Weise versucht) oder [dynamische URL] (http://stackoverflow.com/questions/18423054/dynamically-loading-the-controller-in-angularjs-routeprovider) ermöglichen. – allenhwkim

Verwandte Themen