Come ottenere gli attributi valutati all'interno di una direttiva personalizzata


363

Sto cercando di ottenere un attributo valutato dalla mia direttiva personalizzata, ma non riesco a trovare il modo giusto di farlo.

Ho creato questo jsFiddle per elaborarlo.

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

Cosa mi sto perdendo?


Puoi seguire il link seguente per una migliore comprensione delle direttive. undefinednull.com/2014/02/11/…
Prasanna Sasne il

Risposte:


573

Avviso: aggiorno questa risposta quando trovo soluzioni migliori. Conservo anche le vecchie risposte per riferimento futuro, purché rimangano correlate. La risposta più recente e migliore viene prima di tutto.

Risposta migliore:

Le direttive in angularjs sono molto potenti, ma ci vuole tempo per capire quali processi si nascondono dietro di esse.

Durante la creazione delle direttive, angularjs consente di creare un ambito isolato con alcuni vincoli all'ambito padre. Questi attacchi sono specificati dal dell'attributo si collega l'elemento di DOM e come si definisce ambito proprietà nel oggetto di definizione direttiva .

Esistono 3 tipi di opzioni di rilegatura che è possibile definire nell'ambito e si scrivono come attributo relativo ai prefissi.

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>

In tal caso, nell'ambito della direttiva (sia che si tratti della funzione di collegamento o del controller), possiamo accedere a queste proprietà in questo modo:

/* 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 .

"Ancora OK" Risposta:

Poiché questa risposta è stata accettata, ma presenta alcuni problemi, la aggiornerò con una migliore. Apparentemente, $parseè un servizio che non risiede nelle proprietà dell'ambito attuale, il che significa che accetta solo espressioni angolari e non può raggiungere l'ambito. {{, le }}espressioni vengono compilate durante l'avvio di angularjs, il che significa che quando proviamo ad accedervi nel nostro postlinkmetodo delle direttive , sono già compilate. ( {{1+1}}è 2già in direttiva).

Ecco come vorresti usare:

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

Una cosa che dovresti notare qui è che, se vuoi impostare la stringa di valore, dovresti racchiuderla tra virgolette. (Vedi 3 ° input)

Ecco il violino con cui giocare: http://jsfiddle.net/neuTA/6/

Vecchia risposta:

Non lo sto rimuovendo per le persone che possono essere fuorviate come me, nota che usare $evalva benissimo il modo corretto di farlo, ma $parseha un comportamento diverso, probabilmente non avrai bisogno di questo da usare nella maggior parte dei casi.

Il modo per farlo è, ancora una volta, usare scope.$eval. Non solo compila l'espressione angolare, ma ha anche accesso alle proprietà dell'ambito corrente.

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

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

function MyCtrl($scope) {

}​

Quello che ti mancava era $eval.

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

Esegue l'espressione sull'ambito corrente restituendo il risultato. Eventuali eccezioni nell'espressione vengono propagate (non rilevate). Ciò è utile per valutare le espressioni angolari.


Grazie per la risposta, tuttavia questa non è la soluzione. Ho aggiornato il violino con il tuo codice. jsfiddle.net/neuTA/3
Shlomi Schwartz,

In Chrome ottengo questo errore quando provo ad usare scope. $ Parse: l'oggetto # <oggetto> non ha il metodo '$ parse'. Se inietto il servizio $ parse - function ($ parse) {return function (scope ... - quindi prova: "value =" + $ parse (attr.value) - questo non sembra funzionare per me neanche.
Mark Rajcok

@Mark hai ragione, strano che funzioni nell'esempio del violino ( jsfiddle.net/neuTA/4 ) ma non nel codice che ho ... versioni angolari?
Shlomi Schwartz,

2
Nella sezione "Risposta migliore", $scope.textnon sarà definito nella funzione di collegamento. Il modo in cui la risposta è attualmente formulata, sembra che non sarebbe indefinito. Devi usare $ observ () (o $ watch () funzionerà anche qui) per vedere in modo asincrono il valore interpolato. Vedere la mia risposta e anche stackoverflow.com/questions/14876112/...
Mark Rajcok

1
Nella risposta "Ancora OK" sembra che il $parseservizio sia stato iniettato e non sia mai stato utilizzato. Mi sto perdendo qualcosa?
superjos

83

Per un valore di attributo che deve essere interpolato in una direttiva che non utilizza un ambito isolato, ad es.

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

usa il metodo degli attributi $observe:

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

Dalla pagina della direttiva ,

osservando attributi interpolati: utilizzare $observeper osservare le variazioni di valore di attributi che contengono interpolazione (es src="{{bar}}"). Non solo è molto efficiente, ma è anche l'unico modo per ottenere facilmente il valore reale perché durante la fase di collegamento l'interpolazione non è stata ancora valutata e quindi il valore è impostato su undefined.

Se il valore dell'attributo è solo una costante, ad es.

<input my-directive value="123">

puoi usare $ eval se il valore è un numero o un valore booleano e vuoi il tipo corretto:

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

Se il valore dell'attributo è una costante di stringa o si desidera che il valore sia di tipo stringa nella direttiva, è possibile accedervi direttamente:

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

Nel tuo caso, tuttavia, poiché desideri supportare valori e costanti interpolati, usa $observe.


è stata questa l'unica soluzione che hai trovato?
Shlomi Schwartz,

4
Sì, e poiché la pagina della direttiva raccomanda questo approccio, ecco come lo farei.
Mark Rajcok,

7
+1, questa è la migliore risposta IMO in quanto non impone un campo di applicazione alla direttiva e copre anche le modifiche degli attributi con $
observ

4

Le altre risposte qui sono molto corrette e preziose. Ma a volte vuoi solo semplice: ottenere un semplice vecchio valore analizzato all'istanza della direttiva, senza bisogno di aggiornamenti e senza fare scherzi con l'ambito di isolamento. Ad esempio, può essere utile fornire un payload dichiarativo nella direttiva come array o hash-object nel formato:

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

In tal caso, puoi andare al sodo e usare solo una buona base angular.$eval(attr.attrName).

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

Fiddle di lavoro .


Non so se hai usato una vecchia versione angolare o cosa no, ma tutti i tuoi esempi di codice sono javascript non valido (my-direttiva-nome =) o angolare non valido (angolare. $ Eval non esiste), quindi -1
BiAiB,

Ummm ... dato che questo post ha più di un anno, non sarebbe affatto sorprendente se qualcosa fosse stato deprecato. Tuttavia, una ricerca di Google di 10 secondi ti troverebbe un sacco di materiale su $ eval, incluso proprio qui su SO . E l'altro esempio che citi è un'invocazione in HTML, non Javascript.
XML,

$ scope. $ eval (attr.val) funziona in angolare 1.4. Richiede $ scope per essere iniettato nella funzione di collegamento direttiva.
Martin Connell,

4

Per la stessa soluzione che stavo cercando Angularjs directive with ng-Model.
Ecco il codice che risolve il problema.

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


Il mio risultato è:

92108-2223

2
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) {

}

Usa $ timeout perché la direttiva dirige dopo il caricamento dom quindi le tue modifiche non si applicano

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.