web-dev-qa-db-fra.com

Comment obtenir des attributs évalués dans une directive personnalisée

J'essaie d'obtenir un attribut évalué à partir de ma directive personnalisée, mais je ne trouve pas la bonne façon de le faire.

J'ai créé ce jsFiddle pour élaborer.

<div ng-controller="MyCtrl">
    <input my-directive value="123">
    <input my-directive value="{{1+1}}">
</div>

myApp.directive('myDirective', function () {
    return function (scope, element, attr) {
        element.val("value = "+attr.value);
    }
});

Qu'est-ce que je rate?

361
Shlomi Schwartz

Avis: Je met à jour cette réponse car je trouve de meilleures solutions. Je garde également les anciennes réponses pour référence future tant qu'elles restent liées. La dernière et la meilleure réponse vient en premier.

Meilleure réponse:

Les directives dans angularjs sont très puissantes, mais il faut du temps pour comprendre quels processus sont derrière elles.

Lors de la création de directives, angularjs vous permet de créer une portée isolée avec certaines liaisons à la portée parente. Ces liaisons sont spécifiées par l'attribut que vous attachez l'élément dans DOM et comment vous définissez la portée propriété dans le objet de définition de directive.

Il existe 3 types d'options de liaison que vous pouvez définir dans la portée et que vous écrivez comme attribut lié aux préfixes.

angular.module("myApp", []).directive("myDirective", function () {
    return {
        restrict: "A",
        scope: {
            text: "@myText",
            twoWayBind: "=myTwoWayBind",
            oneWayBind: "&myOneWayBind"
        }
    };
}).controller("myController", function ($scope) {
    $scope.foo = {name: "Umur"};
    $scope.bar = "qwe";
});

HTML

<div ng-controller="myController">
    <div my-directive my-text="hello {{ bar }}" my-two-way-bind="foo" my-one-way-bind="bar">
    </div>
</div>

Dans ce cas, dans le champ d'application de la directive (qu'il s'agisse de lier une fonction ou un contrôleur), nous pouvons accéder à ces propriétés comme suit:

/* Directive scope */

in: $scope.text
out: "hello qwe"
// this would automatically update the changes of value in digest
// this is always string as dom attributes values are always strings

in: $scope.twoWayBind
out: {name:"Umur"}
// this would automatically update the changes of value in digest
// changes in this will be reflected in parent scope

// in directive's scope
in: $scope.twoWayBind.name = "John"

//in parent scope
in: $scope.foo.name
out: "John"


in: $scope.oneWayBind() // notice the function call, this binding is read only
out: "qwe"
// any changes here will not reflect in parent, as this only a getter .

"Toujours OK" Réponse:

Depuis que cette réponse a été acceptée, mais a quelques problèmes, je vais la mettre à jour pour une meilleure. Apparemment, $parse est un service qui ne se trouve pas dans les propriétés de l'étendue actuelle, ce qui signifie qu'il ne prend que des expressions angular et ne peut pas atteindre l'étendue. Les expressions {{, }} sont compilées lors de l'initiation d'angularjs, ce qui signifie que lorsque nous essayons d'y accéder dans notre méthode directives postlink, elles sont déjà compilées. ({{1+1}} est déjà 2 dans la directive).

Voici comment vous voudriez utiliser:

var myApp = angular.module('myApp',[]);

myApp.directive('myDirective', function ($parse) {
    return function (scope, element, attr) {
        element.val("value=" + $parse(attr.myDirective)(scope));
    };
});

function MyCtrl($scope) {
    $scope.aaa = 3432;
}​

.

<div ng-controller="MyCtrl">
    <input my-directive="123">
    <input my-directive="1+1">
    <input my-directive="'1+1'">
    <input my-directive="aaa">
</div>​​​​​​​​

Une chose que vous devriez remarquer ici est que, si vous voulez définir la chaîne de valeur, vous devez l’envelopper entre guillemets. (Voir 3ème entrée)

Voici le violon avec lequel jouer: http://jsfiddle.net/neuTA/6/

Ancienne réponse:

Je ne supprime pas ceci pour les gens qui peuvent être induits en erreur comme moi, notez que l'utilisation de $eval est parfaitement la bonne façon de le faire, mais $parse a un comportement différent, vous avez probablement n’aura pas besoin de cela pour l’utiliser dans la plupart des cas.

La manière de le faire est, encore une fois, d’utiliser scope.$eval. Non seulement il compile l'expression angular, mais il a également accès aux propriétés de l'étendue actuelle.

var myApp = angular.module('myApp',[]);

myApp.directive('myDirective', function () {
    return function (scope, element, attr) {
        element.val("value = "+ scope.$eval(attr.value));
    }
});

function MyCtrl($scope) {

}​

Ce qui vous manque, c'est $eval.

http://docs.angularjs.org/api/ng.$rootScope.Scope#$eval

Exécute l'expression sur l'étendue actuelle en renvoyant le résultat. Toutes les exceptions dans l'expression sont propagées (non capturées). Ceci est utile lors de l'évaluation d'expressions angular.

570
Umur Kontacı

Pour une valeur d'attribut qui doit être interpolée dans une directive qui n'utilise pas de portée isolée, par exemple,

<input my-directive value="{{1+1}}">

utiliser la méthode des attributs $observe:

myApp.directive('myDirective', function () {
  return function (scope, element, attr) {
    attr.$observe('value', function(actual_value) {
      element.val("value = "+ actual_value);
    })
 }
});

De la page directive ,

observation des attributs interpolés: utilisez $observe pour observer les changements de valeur des attributs contenant une interpolation (par exemple src="{{bar}}"). Non seulement cela est très efficace, mais c'est aussi le seul moyen d'obtenir facilement la valeur réelle car pendant la phase de liaison, l'interpolation n'a pas encore été évaluée et la valeur est donc définie sur undefined.

Si la valeur de l'attribut est juste une constante, par exemple,

<input my-directive value="123">

vous pouvez utiliser $ eval si la valeur est un nombre ou un booléen et que vous voulez le type correct:

return function (scope, element, attr) {
   var number = scope.$eval(attr.value);
   console.log(number, number + 1);
});

Si la valeur de l'attribut est une constante de chaîne ou si vous voulez que la valeur soit du type chaîne dans votre directive, vous pouvez y accéder directement:

return function (scope, element, attr) {
   var str = attr.value;
   console.log(str, str + " more");
});

Cependant, dans votre cas, puisque vous souhaitez prendre en charge les valeurs et les constantes interpolées, utilisez $observe.

82
Mark Rajcok

Pour la même solution, je cherchais Angularjs directive with ng-Model.
Voici le code qui résout le problème.

    myApp.directive('zipcodeformatter', function () {
    return {
        restrict: 'A', // only activate on element attribute
        require: '?ngModel', // get a hold of NgModelController
        link: function (scope, element, attrs, ngModel) {

            scope.$watch(attrs.ngModel, function (v) {
                if (v) {
                    console.log('value changed, new value is: ' + v + ' ' + v.length);
                    if (v.length > 5) {
                        var newzip = v.replace("-", '');
                        var str = newzip.substring(0, 5) + '-' + newzip.substring(5, newzip.length);
                        element.val(str);

                    } else {
                        element.val(v);
                    }

                }

            });

        }
    };
});


HTML DOM

<input maxlength="10" zipcodeformatter onkeypress="return isNumberKey(event)" placeholder="Zipcode" type="text" ng-readonly="!checked" name="zipcode" id="postal_code" class="form-control input-sm" ng-model="patient.shippingZipcode" required ng-required="true">


Mon résultat est:

92108-2223
4
Satish Singh

Les autres réponses ici sont très correctes et précieuses. Mais parfois, vous voulez simplement: obtenir une ancienne valeur analysée lors d’une instanciation de directive, sans avoir besoin de mises à jour, et sans jouer avec la portée d’isolement. Par exemple, il peut être utile de fournir une charge déclarative dans votre directive sous forme de tableau ou d'objet de hachage sous la forme:

my-directive-name="['string1', 'string2']"

Dans ce cas, vous pouvez aller droit au but et utiliser simplement un Nice basic angular.$eval(attr.attrName).

element.val("value = "+angular.$eval(attr.value));

Travailler violon .

4
XML
var myApp = angular.module('myApp',[]);

myApp .directive('myDirective', function ($timeout) {
    return function (scope, element, attr) {
        $timeout(function(){
            element.val("value = "+attr.value);
        });

    }
});

function MyCtrl($scope) {

}

Utilisez $ timeout parce que la directive appelle après le chargement de dom pour que vos modifications ne soient pas appliquées

2
user1693371