web-dev-qa-db-fra.com

Comment mettre à jour le modèle AngularJS à partir de la fonction de liaison de directive?

Ce que j'essaie de réaliser, c'est d'ajouter une interface supplémentaire pour que les champs de saisie puissent augmenter et diminuer la valeur numérique en cliquant sur les boutons + et -.

(Essentiellement, c'est ce que les champs d'entrée [type = nombre] ont sur Chrome, mais je veux que cela soit compatible entre les navigateurs et ait également un contrôle total de la présentation sur tous les navigateurs).

Code en vue:

<input data-ng-model="session.amountChosen" type="text" min="1" class="form-control input-small" data-number-input>

Code de directive:

app.directive('numberInput', function() {
return {
    require: 'ngModel',
    scope: true,
    link: function(scope, Elm, attrs, ctrl) {

        var currValue = parseInt(scope.$eval(attrs.ngModel)),
            minValue = attrs.min || 0,
            maxValue = attrs.max || Infinity,
            newValue;

        //puts a wrap around the input and adds + and - buttons
        Elm.wrap('<div class="number-input-wrap"></div>').parent().append('<div class="number-input-controls"><a href="#" class="btn btn-xs btn-pluimen">+</a><a href="#" class="btn btn-xs btn-pluimen">-</a></div>');

        //finds the buttons ands binds a click event to them where the model increase/decrease should happen
        Elm.parent().find('a').bind('click',function(e){

            if(this.text=='+' && currValue<maxValue) {
                newValue = currValue+1;    
            } else if (this.text=='-' && currValue>minValue) {
                newValue = currValue-1;    
            }

            scope.$apply(function(){
                scope.ngModel = newValue;
            });

            e.preventDefault();
        });


    }
  };

})

Ceci est capable de récupérer la valeur actuelle du modèle via la portée. $ Eval (attrs.ngModel), mais ne parvient pas à définir la nouvelle valeur.

Aftermath edit: c'est le code qui fonctionne maintenant (au cas où vous ne voudriez pas voir la solution à ce problème)

app.directive('numberInput', function() {
  return {
    require: 'ngModel',
    scope: true,
    link: function(scope, Elm, attrs, ctrl) {

        var minValue = attrs.min || 0,
            maxValue = attrs.max || Infinity;

        Elm.wrap('<div class="number-input-wrap"></div>').parent().append('<div class="number-input-controls"><a href="#" class="btn btn-xs btn-pluimen">+</a><a href="#" class="btn btn-xs btn-pluimen">-</a></div>');
        Elm.parent().find('a').bind('click',function(e){

            var currValue = parseInt(scope.$eval(attrs.ngModel)),
                newValue = currValue;

            if(this.text=='+' && currValue<maxValue) {
                newValue = currValue+1;    
            } else if (this.text=='-' && currValue>minValue) {
                newValue = currValue-1;    
            }

            scope.$eval(attrs.ngModel + "=" + newValue);
            scope.$apply();            

            e.preventDefault();
        });
    }
  };
})
14
Harijs Deksnis

ngModelController les méthodes doivent être utilisées à la place de $ eval () pour obtenir et définir la valeur de la propriété ng-model.

parseInt () n'est pas requis lors de l'évaluation d'un attribut avec une valeur numérique, car $ eval convertira la valeur en nombre. $ eval doit être utilisé pour définir les variables minValue et maxValue.

Il n'est pas nécessaire que la directive crée une portée enfant.

$ apply () n'est pas nécessaire car les méthodes ngModelController ($ render () en particulier) mettront automatiquement à jour la vue. Cependant, comme @Harijs le note dans un commentaire ci-dessous, $ apply () est nécessaire si d'autres parties de l'application doivent également être mises à jour.

app.directive('numberInput', function ($parse) {
    return {
        require: 'ngModel',
        link: function (scope, Elm, attrs, ctrl) {
            var minValue = scope.$eval(attrs.min) || 0,
                maxValue = scope.$eval(attrs.max) || Infinity;
            Elm.wrap('<div class="number-input-wrap"></div>').parent()
                .append('<div class="number-input-controls"><a href="#" class="btn btn-xs btn-pluimen">+</a><a href="#" class="btn btn-xs btn-pluimen">-</a></div>');
            Elm.parent().find('a').bind('click', function (e) {
                var currValue = ctrl.$modelValue,
                    newValue;
                if (this.text === '+' && currValue < maxValue) {
                    newValue = currValue + 1;
                } else if (this.text === '-' && currValue > minValue) {
                    newValue = currValue - 1;
                }
                ctrl.$setViewValue(newValue);
                ctrl.$render();
                e.preventDefault();
                scope.$apply(); // needed if other parts of the app need to be updated
            });
        }
    };
});

fiddle

29
Mark Rajcok

Vous ne voudriez pas remplacer le scope.ngModel variable, mais la valeur qui se trouve derrière cette variable. Vous l'avez déjà fait lorsque vous avez lu la valeur dans la première ligne de la fonction de lien:

 currValue = parseInt(scope.$eval(attrs.ngModel))
 //                         ^^^^^^^^^^^^^^^^^^^^

Si c'est une valeur simple, comme myProperty, vous pouvez l'utiliser sur la portée:

 scope[attr.ngModel] = newValue

Mais cela ne fonctionnera pas, si vous avez une valeur d'expression, comme container.myProperty. Dans ce cas (et c'est le type le plus générique, vous devriez viser), vous devez évaluer la valeur définie dans la portée, comme ceci:

scope.$eval(attrs.ngModel + "=" + newValue)

Je dois admettre que le $eval part est un peu moche, comme c'est en JavaScript avec le pendant eval, mais ça fait l'affaire. Gardez à l'esprit que cela pourrait ne pas fonctionner de cette façon, lorsque vous souhaitez définir des valeurs de chaîne. Il faudrait alors échapper à ces valeurs.

J'espère que cela pourra aider ;)

4
Tharabas
.directive('numberInput', function ($parse) {
   return {
      require: 'ngModel',
      link: function (scope, Elm, attrs){ 
        Elm.bind("click", function() {
          var model = $parse(attrs.ngModel);
          var modelSetter = model.assign;
          scope.$apply(function() {
               modelSetter(scope, model);
          });
     }
  }
})
3
Vaibhao