Question

tl:dr

Comment puis-je remplir une table ng comprenant des filtres « sélectionner » en utilisant ajax/json ?

Plunk montrant le problème :http://plnkr.co/Zn09LV


Détail

J'essaie de me familiariser avec AngualrJS et l'extension ng-table et bien que je puisse obtenir de belles tables avec des filtres fonctionnels et autres lorsque j'utilise des données statiques définies dans le javascript - une fois que j'essaie de charger des données réelles dans la table, j'ai rencontré un problème.

Le corps principal de la table ng est rempli correctement et tant que j'utilise uniquement le filtre de texte, tout semble fonctionner :

        <td data-title="'Name'" filter="{ 'Name': 'text' }" sortable="'Name'">
            {{user.Name}}
        </td>

Fonctionne très bien.

Cependant, si je mets à jour ceci pour utiliser le filtre de sélection :

        <td data-title="'Name'" filter="{ 'Name': 'select' }" sortable="'Name'"  filter-data="Names($column)">
            {{user.Name}}
        </td>

Je rencontre un problème de synchronisation dans la mesure où la variable Names est toujours évaluée avant que les données ne soient renvoyées du serveur.(Il est possible que la varibale des noms soit évaluée avant même que la requête au serveur ne soit envoyée.) Cela signifie que j'obtiens une liste vide pour le filtre.

Une fois les données renvoyées du serveur, je n'arrive pas à trouver un moyen de mettre à jour le filtre de sélection.La réexécution du code qui crée la liste de filtres semble initialement n'avoir aucun effet - je ne sais pas comment déclencher la table ng pour revérifier ses filtres afin que la variable mise à jour ne soit pas lue.Je n'arrive pas non plus à trouver un moyen de reporter l'évaluation de la variable jusqu'à la fin de l'appel asynchrone.

Pour mon javascript, j'ai à peu près utilisé l'exemple de code ajax de la page ng-table GitHub et y ai ajouté l'exemple de code pour le filtre de sélection.

    $scope.tableParams = new ngTableParams({
        page: 1,            // show first page
        count: 10,          // count per page
        sorting: {
            name: 'asc'     // initial sorting
        }
    }, {
        total: 0,           // length of data
        getData: function($defer, params) {
            // ajax request to api
            Api.get(params.url(), function(data) {
                $timeout(function() {
                    // update table params
                    var orderedData = params.sorting ?
                    $filter('orderBy')(data.result, params.orderBy()) :
                    data.result;
                    orderedData = params.filter ?
                    $filter('filter')(orderedData, params.filter()) :
                    orderedData;

                    $scope.users = orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count());

                    params.total(orderedData.length); // set total for recalc pagination
                    $defer.resolve($scope.users);
                }, 500);
            });
        }
    });

    var inArray = Array.prototype.indexOf ?
    function (val, arr) {
        return arr.indexOf(val)
    } :
    function (val, arr) {
        var i = arr.length;
        while (i--) {
            if (arr[i] === val) return i;
        }
        return -1
    };
$scope.names = function(column) {
    var def = $q.defer(),
        arr = [],
        names = [];
    angular.forEach(data, function(item){
        if (inArray(item.name, arr) === -1) {
            arr.push(item.name);
            names.push({
                'id': item.name,
                'title': item.name
            });
        }
    });
    def.resolve(names);
    return def;
};

J'ai essayé quelques tentatives pour ajouter un $q.defer() supplémentaire et encapsuler les données initiales suivies de la fonction $scope.names - mais ma compréhension de la promesse et du report n'est pas assez forte pour que quoi que ce soit fonctionne.

Il y a quelques notes sur GitHub suggérant qu'il s'agit d'un bug dans ng-table, mais je ne sais pas si c'est le cas ou si je fais juste quelque chose de stupide.

https://github.com/esvit/ng-table/issues/186

Des conseils sur la façon de procéder sont grandement appréciés

-Kaine-

Était-ce utile?

La solution

J'ai eu un problème similaire mais légèrement plus complexe.Je voulais pouvoir mettre à jour la liste des filtres de manière dynamique, ce qui semblait tout à fait réalisable puisqu'ils devraient de toute façon être simplement dans une variable $scope.Au fond, je m'attendais à ce que, si j'ai $scope.filterOptions = []; alors je pourrais définir filter-data="filterOptions" et toute mise à jour de cette liste serait automatiquement reflétée.J'ai eu tort.

Mais j'ai trouvé une solution que je trouve plutôt bonne.Tout d'abord, vous devez remplacer le modèle de filtre de sélection ngTable (au cas où vous ne sauriez pas comment procéder, cela implique d'utiliser $templateCache et la clé que vous devez remplacer est 'ng-table/filters/select.html').

Dans le modèle normal, vous trouverez quelque chose comme ceci ng-options="data.id as data.title for data in $column.data" et le problème c'est que $column.data est une valeur fixe qui ne changera pas lors de la mise à jour $scope.filterOptions.

Ma solution est de transmettre uniquement le $scope clé en tant que données de filtre au lieu de transmettre toute la liste des options.Alors, au lieu de filter-data="filterOptions", Je vais passer filter-data="'filterOptions'" puis, mettez un petit changement dans le modèle comme : ng-options="data.id as data.title for data in {{$column.data}}".

Évidemment, il s’agit d’un changement important dans le fonctionnement du filtre de sélection.Dans mon cas, il s'agissait d'une très petite application qui n'avait qu'une seule table, mais vous craignez peut-être qu'un changement comme celui-ci ne brise vos autres sélections.Si tel est le cas, vous souhaiterez peut-être intégrer cette solution dans un filtre personnalisé au lieu de simplement remplacer « sélectionner ».

Autres conseils

Vous pouvez y parvenir avec un Filtre personnalisé:

Le code pour le filtre de sélection standard sur ngtable dit :

<select ng-options="data.id as data.title for data in column.data"
    ng-model="params.filter()[name]"
    ng-show="filter == 'select'"
    class="filter filter-select form-control" name="{{column.filterName}}">
</select>

Lorsque vous appelez ces données, vous transmettez : filter-data="names($column)" et ngtable se charge d'obtenir les données pour vous.Je ne sais pas pourquoi cela ne fonctionne pas avec une ressource externe.Je parie que cela a quelque chose à voir avec la colonne $ et la promesse, comme vous l'avez souligné.

J'ai fait une solution de contournement rapide dans mon code pour éviter cela.Écrire mon propre modèle de filtre de sélection comme :

<select id="filterTest" class="form-control" 
    ng-model="tableParams.filter()['test']" 
    ng-options="e.id as e.title for e in externaldata">
</select>

Vous récupérez ces données externes dans votre contrôleur :

$scope.externaldata = Api.query(); // Your custom api call

Cela fonctionne parfaitement, mais j'ai un id sur mes données, donc pas besoin du name fonction.

Je comprends que cette solution n'est pas optimale.Voyons si quelqu'un écrit ici plus que cette « solution de contournement » et nous éclaire.Même esvit est là parfois ;)

Cela fonctionne pour moi:

html:

<td data-title="'Doc type'" filter="{ 'doc_types': 'select' }" filter-data="docTypes()" sortable="'doc_types'">
    {{task.doc_type}}
</td>

angularjs:

$scope.docTypes = function ($scope) 
{
    var def = $q.defer();
    //var docType = [
    //    {'id':'4', 'title':'Whatever 1'},
    //    {'id':'9', 'title':'Whatever 2'},
    //    {'id':'11', 'title':'Whatever 3'}
    //];

    // Or get data from API.
    // Format should be same as above.
    var docType = $http.get('http://whatever.dev', {
        params: { param1: data1 }
    });

    //Or with Restangular 
    var docType = Restangular.all('/api/doctype').getList();

    def.resolve(docType);
    return def;
};

Comme mentionné par @ andión, vous pouvez obtenir avec filtre personnalisé .

Il est facile d'obtenir une population de données asynchrone avec des promesses ( Le service $ q en angulaire), intéressant Andy Article sur les promesses

Vous pouvez modifier la méthode $ de la portée.Names et ajouter un service HTTP $ qui renvoie les données asynchrones et résolvez l'objet différé comme suit:

$scope.names = function(column) {
  var def = $q.defer();

  /* http service is based on $q service */
  $http({
    url: siteurl + "app/application/fetchSomeList",
    method: "POST",

  }).success(function(data) {

    var arr = [],
      names = [];

    angular.forEach(data, function(item) {
      if (inArray(item.name, arr) === -1) {
        arr.push(item.name);
        names.push({
          'id': item.name,
          'title': item.name
        });
      }
    });
    
    /* whenever the data is available it resolves the object*/
    def.resolve(names);

  });

  return def;
};

J'ai rencontré un problème similaire, mais je ne voulais pas apporter l'appel supplémentaire Ajax pour obtenir les valeurs de filtrage.

Le problème avec le code de l'OP est que la fonction de filtre-filtre exécute avant $ péron.data est peuplée.Pour contourner cela, j'ai utilisé la montre angulaire $ pour écouter des changements sur $ Scope.Data.Une fois $ Scope.Data est valide que les données du filtre sont renseignées correctement.

        $scope.names2 = function () {
        var def = $q.defer(),
             arr = [],
                names = [];
        $scope.data = "";
        $scope.$watch('data', function () {


            angular.forEach($scope.data, function (item) {
                if (inArray(item.name, arr) === -1) {
                    arr.push(item.name);
                    names.push({
                        'id': item.name,
                        'title': item.name
                    });
                }
            });

        });
        def.resolve(names);
        return def;
    };

La Plunk originale forcée avec le changement: http://plnkr.co/edit/sjxvppqr2ziyasyavbqa

Voir aussi cette question sur $ watch: Comment puis-je utiliser $ de portée. $Regarder et $ portée. $ Appliquer à Angularjs?

J'ai résolu le problème avec $ q.defer () comme mentionné par diablo

Cependant, le code est en fait assez simple et simple:

dans html:

<td ... filter-data="countries">

dans le contrôleur:

$scope.countries = $q.defer();
$http.get("/getCountries").then(function(resp){
  $scope.countries.resolve(resp.data.data);
})

"Tout d'abord, vous devez remplacer le modèle de filtrage SELECT NGTABLE (au cas où vous ne savez pas comment faire cela, il s'agit d'utiliser $ templateCache et la touche que vous devez remplacer est 'ng-table / filtres / sélectionner.HTML '). "

J'ai ajouté le script annulé sous le script de NG-Table et tout a bien fonctionné ...

<script id="ng-table/filters/select.html" type="text/ng-template">
 <select ng-options="data.id as data.title for data in {{$column.data}}" ng-table-select-filter-ds="$column" ng-disabled="$filterRow.disabled" ng-model="params.filter()[name]" class="filter filter-select form-control" name="{{name}}"> <option style="display:none" value=""></option> </select>
</script>

Ce que j'ai fait est simplement de mettre la balise Select avec des valeurs et que le modèle NG renvoie les valeurs du filtre.

Ceci a été utile car je devais traduire le texte brut.

<td data-title="'Status'| translate" ng-bind = "("cc_assignment_active"== '0') ? ('Inactive' | translate) : ('Active'| translate)" 
                    filter="{ cc_assignment_active: 'select3'}" >

</td>

<script id="ng-table/filters/select3.html" type="text/ng-template">
<select  class="filter filter-select form-control"  ng-model="params.filter()[name]" name="{{name}}">
    <option active value="" translate>---All---</option>
    <option value="1" translate>Active</option>
    <option value="0" translate>Inactive</option>
</select>

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top