Jak uzyskać ocenione atrybuty w niestandardowej dyrektywie

363

Próbuję uzyskać oceniany atrybut z mojej niestandardowej dyrektywy, ale nie mogę znaleźć odpowiedniego sposobu na zrobienie tego.

Stworzyłem ten jsFiddle do opracowania.

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

czego mi brakuje?

Shlomi Schwartz
źródło
Możesz skorzystać z poniższego linku, aby lepiej zrozumieć dyrektywy. undefinednull.com/2014/02/11/…
Prasanna Sasne

Odpowiedzi:

573

Uwaga: aktualizuję tę odpowiedź, ponieważ znajduję lepsze rozwiązania. Zachowuję również stare odpowiedzi do wykorzystania w przyszłości, o ile będą one powiązane. Najnowsza i najlepsza odpowiedź jest najważniejsza.

Lepsza odpowiedź:

Dyrektywy w angularjs są bardzo potężne, ale potrzeba czasu, aby zrozumieć, które procesy za nimi stoją.

Podczas tworzenia dyrektyw angularjs umożliwia utworzenie izolowanego zakresu z pewnymi powiązaniami z zakresem nadrzędnym. Te powiązania są określone przez atrybut, który dołączasz do elementu w DOM oraz sposób definiowania właściwości scope w obiekcie definicji dyrektywy .

Istnieją 3 rodzaje opcji wiązania, które możesz zdefiniować w zakresie i zapisujesz je jako atrybuty związane z prefiksami.

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>

W takim przypadku w zakresie dyrektywy (niezależnie od tego, czy chodzi o połączenie funkcji czy kontrolera), możemy uzyskać dostęp do tych właściwości w następujący sposób:

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

„Nadal OK” Odpowiedź:

Ponieważ ta odpowiedź została zaakceptowana, ale ma pewne problemy, zamierzam ją zaktualizować do lepszej. Najwyraźniej $parsejest to usługa, która nie leży we właściwościach bieżącego zakresu, co oznacza, że ​​przyjmuje tylko wyrażenia kątowe i nie może osiągnąć zakresu. {{, }}wyrażenia są kompilowane podczas inicjacji angularjs, co oznacza, że ​​kiedy próbujemy uzyskać do nich dostęp w naszej postlinkmetodzie dyrektyw , są one już skompilowane. ( {{1+1}}jest 2już w dyrektywie).

Oto jak chcesz użyć:

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

Jedną z rzeczy, które powinieneś tutaj zauważyć, jest to, że jeśli chcesz ustawić ciąg wartości, powinieneś zawinąć go w cudzysłów. (Patrz 3. wejście)

Oto skrzypce do zabawy: http://jsfiddle.net/neuTA/6/

Stara odpowiedź:

Nie usuwam tego dla ludzi, którzy mogą być wprowadzani w błąd, tak jak ja, zauważ, że używanie $evaljest w porządku poprawny sposób, ale $parsema inne zachowanie, prawdopodobnie nie będziesz tego potrzebował w większości przypadków.

Sposobem na to jest ponowne użycie scope.$eval. Nie tylko kompiluje wyrażenie kątowe, ale ma również dostęp do właściwości bieżącego zakresu.

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

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

function MyCtrl($scope) {

}​

Brakowało ci tego $eval.

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

Wykonuje wyrażenie w bieżącym zakresie zwracając wynik. Wszelkie wyjątki w wyrażeniu są propagowane (nieprzechwycone). Jest to przydatne podczas oceny wyrażeń kątowych.

Umur Kontacı
źródło
Dzięki za odpowiedź, ale to nie jest rozwiązanie. Zaktualizowałem skrzypce twoim kodem. jsfiddle.net/neuTA/3
Shlomi Schwartz
W Chrome pojawia się ten błąd, gdy próbuję użyć zakresu. $ Parse: Obiekt # <Object> nie ma metody „$ parse”. Jeśli wstrzyknę usługę $ parse - funkcja ($ parse) {funkcja return (zakres ... - to spróbuj: "wartość =" + $ parse (attr.value) - wydaje mi się, że to nie działa albo
Mark Rajcok
@ Zaznacz, że masz rację, dziwne, że to działa na przykładzie skrzypiec ( jsfiddle.net/neuTA/4 ), ale nie w kodzie mam ... wersje kątowe?
Shlomi Schwartz,
2
W sekcji „Lepsza odpowiedź” $scope.textbędzie niezdefiniowana w funkcji łączenia. Sposób, w jaki obecnie brzmi odpowiedź, brzmi, jakby nie byłby niezdefiniowany. Musisz użyć $ observ () (lub $ watch () też tutaj będzie działał), aby asynchronicznie zobaczyć interpolowaną wartość. Zobacz moją odpowiedź, a także stackoverflow.com/questions/14876112/...
Mark Rajcok
1
W odpowiedzi „Nadal OK” wydaje się, że $parseusługa jest wstrzykiwana, a następnie nigdy nie używana. Czy coś brakuje?
superjos,
83

Dla wartości atrybutu, którą należy interpolować w dyrektywie, która nie korzysta z izolowanego zakresu, np.

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

użyj metody Atrybuty $observe:

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

Z dyrektywy stronie

obserwowanie interpolowanych atrybutów: służy $observedo obserwowania zmian wartości atrybutów zawierających interpolację (np src="{{bar}}".). Jest to nie tylko bardzo wydajne, ale także jedyny sposób na łatwe uzyskanie rzeczywistej wartości, ponieważ podczas fazy łączenia interpolacja nie została jeszcze oszacowana, a więc wartość jest w tej chwili ustawiona na undefined.

Jeśli wartość atrybutu jest tylko stałą, np.

<input my-directive value="123">

możesz użyć $ eval, jeśli wartością jest liczba lub wartość logiczna, i chcesz mieć poprawny typ:

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

Jeśli wartością atrybutu jest stała ciągu lub jeśli chcesz, aby wartość była typem ciągu w dyrektywie, możesz uzyskać do niej bezpośredni dostęp:

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

Jednak w twoim przypadku, ponieważ chcesz obsługiwać interpolowane wartości i stałe, użyj $observe.

Mark Rajcok
źródło
czy to było jedyne znalezione rozwiązanie?
Shlomi Schwartz,
4
Tak, a ponieważ strona z zaleceniami zaleca takie podejście, właśnie tak bym to zrobił.
Mark Rajcok,
7
+1, to najlepsza odpowiedź IMO, ponieważ nie
narzuca
4

Pozostałe odpowiedzi tutaj są bardzo poprawne i cenne. Ale czasami chcesz po prostu proste: uzyskać zwykłą starą przeanalizowaną wartość przy tworzeniu instancji dyrektywy, bez potrzeby aktualizacji i bez bałaganu z izolowanym zakresem. Na przykład przydatne może być podanie deklaratywnego ładunku w dyrektywie jako tablicy lub obiektu mieszającego w postaci:

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

W takim przypadku możesz przejść do sedna i po prostu użyć ładnego podstawowego angular.$eval(attr.attrName).

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

Pracujący Fiddle .

XML
źródło
Nie wiem, czy użyłeś starej wersji kątowej, czy co nie, ale wszystkie twoje próbki kodu są albo niepoprawne javascript (moja-nazwa-dyrektywy =), albo nieprawidłowe (kątowe. $ Eval nie istnieje), więc -1
BiAiB
Ummm ... biorąc pod uwagę, że ten post ma ponad rok, nie byłoby w ogóle nic dziwnego, gdyby coś było przestarzałe. Jednak w 10-sekundowym wyszukiwaniu Google znajdziesz mnóstwo materiałów na temat $ eval, w tym tutaj w SO . Innym przytoczonym przez ciebie przykładem jest wywołanie w HTML, a nie JavaScript.
XML
$ scope. $ eval (attr.val) działa w układzie kątowym 1.4. Wymaga wprowadzenia zakresu $ do funkcji dowiązania dyrektywy.
Martin Connell,
4

Dla tego samego rozwiązania, którego szukałem Angularjs directive with ng-Model.
Oto kod, który rozwiązuje problem.

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


Mój wynik to:

92108-2223
Satish Singh
źródło
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) {

}

Użyj limitu czasu $, ponieważ dyrektywa ładuje po załadowaniu dom, więc zmiany nie mają zastosowania

użytkownik1693371
źródło