So erhalten Sie ausgewertete Attribute in einer benutzerdefinierten Direktive


363

Ich versuche, ein ausgewertetes Attribut aus meiner benutzerdefinierten Direktive abzurufen, finde jedoch nicht den richtigen Weg.

Ich habe diese jsFiddle erstellt, um sie auszuarbeiten.

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

Was vermisse ich?


Sie können dem folgenden Link folgen, um die Richtlinien besser zu verstehen. undefinednull.com/2014/02/11/…
Prasanna Sasne

Antworten:


573

Hinweis: Ich aktualisiere diese Antwort, wenn ich bessere Lösungen finde. Ich behalte die alten Antworten auch zum späteren Nachschlagen, solange sie verwandt bleiben. Die neueste und beste Antwort steht an erster Stelle.

Bessere Antwort:

Direktiven in Angularjs sind sehr mächtig, aber es braucht Zeit, um zu verstehen, welche Prozesse dahinter stehen.

Während Sie Direktiven erstellen, können Sie mit anglejs einen isolierten Bereich mit einigen Bindungen an den übergeordneten Bereich erstellen . Diese Bindungen werden durch das Attribut angegeben, mit dem Sie das Element in DOM anhängen, und wie Sie die Eigenschaft scope im Direktivendefinitionsobjekt definieren .

Es gibt drei Arten von Bindungsoptionen, die Sie im Bereich definieren können und die Sie als Präfix-bezogene Attribute schreiben.

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 diesem Fall können wir im Rahmen der Direktive (unabhängig davon, ob es sich um eine Verknüpfungsfunktion oder eine Steuerung handelt) auf folgende Eigenschaften zugreifen:

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

"Immer noch OK" Antwort:

Da diese Antwort akzeptiert wurde, aber einige Probleme hat, werde ich sie auf eine bessere aktualisieren. Anscheinend $parsehandelt es sich um einen Dienst, der nicht in den Eigenschaften des aktuellen Bereichs liegt, was bedeutet, dass er nur Winkelausdrücke akzeptiert und den Bereich nicht erreichen kann. {{, }}Ausdrücke werden kompiliert, während anglejs initiiert. Wenn wir also versuchen, in unserer Direktivenmethode auf sie zuzugreifen postlink, sind sie bereits kompiliert. ( {{1+1}}ist bereits 2in der Richtlinie).

So möchten Sie Folgendes verwenden:

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

Eine Sache, die Sie hier beachten sollten, ist, dass Sie die Wertzeichenfolge in Anführungszeichen setzen sollten, wenn Sie sie festlegen möchten. (Siehe 3. Eingang)

Hier ist die Geige zum Spielen: http://jsfiddle.net/neuTA/6/

Alte Antwort:

Ich entferne dies nicht für Leute, die wie ich irregeführt werden können. Beachten Sie, dass die Verwendung $evalvollkommen in Ordnung ist, aber $parseein anderes Verhalten aufweist. In den meisten Fällen benötigen Sie dies wahrscheinlich nicht.

Der Weg, dies zu tun, ist wieder einmal mit scope.$eval. Es kompiliert nicht nur den Winkelausdruck, sondern hat auch Zugriff auf die Eigenschaften des aktuellen Bereichs.

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

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

function MyCtrl($scope) {

}​

Was Sie vermissen war $eval.

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

Führt den Ausdruck im aktuellen Bereich aus und gibt das Ergebnis zurück. Alle Ausnahmen im Ausdruck werden weitergegeben (nicht erfasst). Dies ist nützlich, wenn Winkelausdrücke ausgewertet werden.


Vielen Dank für die Antwort, dies ist jedoch nicht die Lösung. Ich habe die Geige mit Ihrem Code aktualisiert. jsfiddle.net/neuTA/3
Shlomi Schwartz

In Chrome wird dieser Fehler angezeigt, wenn versucht wird, den Bereich zu verwenden. $ Parse: Objekt # <Objekt> hat keine Methode '$ parse'. Wenn ich die $ parse service - Funktion ($ parse) {return function (scope ... - injiziere), dann versuche: "value =" + $ parse (attr.value) - das scheint für mich nicht zu funktionieren entweder.
Mark Rajcok

@Mark Sie haben Recht, seltsam, es funktioniert im Geigenbeispiel ( jsfiddle.net/neuTA/4 ), aber nicht im Code, den ich habe ... eckige Versionen?
Shlomi Schwartz

2
Im Abschnitt "Bessere Antwort" $scope.textwird in der Verknüpfungsfunktion undefiniert. Die Art und Weise, wie die Antwort derzeit formuliert ist, klingt so, als wäre sie nicht undefiniert. Sie müssen $ watch () verwenden (oder $ watch () funktioniert auch hier), um den interpolierten Wert asynchron anzuzeigen. Siehe meine Antwort und auch stackoverflow.com/questions/14876112/…
Mark Rajcok

1
In der Antwort "Immer noch OK" scheint der $parseDienst injiziert und dann nie verwendet zu werden. Vermisse ich etwas
Superjos

83

Für einen Attributwert, der in einer Direktive interpoliert werden muss, die keinen isolierten Bereich verwendet, z.

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

Verwenden Sie die Methode von Attributes $observe:

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

Von der Direktivenseite ,

Beobachten interpolierter Attribute: Verwenden Sie $observediese Option , um die Wertänderungen von Attributen zu beobachten, die Interpolation enthalten (z src="{{bar}}". B. ). Dies ist nicht nur sehr effizient, sondern auch die einzige Möglichkeit, den tatsächlichen Wert leicht zu ermitteln, da während der Verknüpfungsphase die Interpolation noch nicht ausgewertet wurde und der Wert zu diesem Zeitpunkt auf eingestellt ist undefined.

Wenn der Attributwert nur eine Konstante ist, z.

<input my-directive value="123">

Sie können verwenden $ eval verwenden, wenn der Wert eine Zahl oder ein Boolescher Wert ist und Sie den richtigen Typ möchten:

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

Wenn der Attributwert eine Zeichenfolgenkonstante ist oder Sie möchten, dass der Wert in Ihrer Direktive vom Typ Zeichenfolge ist, können Sie direkt darauf zugreifen:

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

In Ihrem Fall verwenden Sie jedoch, da Sie interpolierte Werte und Konstanten unterstützen möchten $observe.


War dies die einzige Lösung, die Sie gefunden haben?
Shlomi Schwartz

4
Ja, und da die Direktivenseite diesen Ansatz empfiehlt, würde ich dies so tun.
Mark Rajcok

7
+1, dies ist die beste Antwort IMO, da es keinen Gültigkeitsbereich für die Direktive
erzwingt

4

Die anderen Antworten hier sind sehr richtig und wertvoll. Aber manchmal möchten Sie einfach nur: einen einfachen alten analysierten Wert bei der Instanziierung der Direktive erhalten, ohne Aktualisierungen zu benötigen und ohne den isolierten Bereich zu beeinträchtigen. Zum Beispiel kann es nützlich sein, eine deklarative Nutzlast in Ihre Direktive als Array oder Hash-Objekt in der folgenden Form bereitzustellen:

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

In diesem Fall können Sie auf den Punkt kommen und einfach ein schönes Basic verwenden angular.$eval(attr.attrName).

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

Arbeits Fiddle .


Ich weiß nicht, ob Sie eine alte Winkelversion verwendet haben oder nicht, aber alle Ihre Codebeispiele sind entweder ungültiges Javascript (my-directive-name =) oder ungültiger Winkel (Winkel. $ Eval existiert nicht), also -1
BiAiB

Ähm ... da dieser Beitrag mehr als ein Jahr alt ist, wäre es überhaupt nicht überraschend, wenn etwas seitdem veraltet wäre. Bei einer 10-sekündigen Google-Suche finden Sie jedoch reichlich Material zu $ ​​eval, auch hier bei SO . Das andere Beispiel, das Sie zitieren, ist ein Aufruf in HTML, nicht in Javascript.
XML

$ scope. $ eval (attr.val) arbeitet im Winkel 1.4. Erfordert, dass $ scope in die Direktivenverknüpfungsfunktion eingefügt wird.
Martin Connell

4

Für die gleiche Lösung, die ich gesucht habe Angularjs directive with ng-Model.
Hier ist der Code, der das Problem löst.

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


Mein Ergebnis ist:

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

}

Verwenden Sie $ timeout, da die Direktive nach dem Laden von dom aufgerufen wird, sodass Ihre Änderungen nicht übernommen werden

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.