Как получить оцененные атрибуты внутри пользовательской директивы

363

Я пытаюсь получить оцененный атрибут из моей пользовательской директивы, но не могу найти правильный способ сделать это.

Я создал этот jsFiddle для разработки.

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

Что мне не хватает?

Шломи Шварц
источник
Вы можете перейти по ссылке ниже для лучшего понимания директив. undefinednull.com/2014/02/11/…
Прасанна Сасне

Ответы:

573

Обратите внимание: я обновляю этот ответ, так как нахожу лучшие решения. Я также сохраняю старые ответы для дальнейшего использования, пока они остаются связанными. Последний и лучший ответ приходит первым.

Лучший ответ:

Директивы в angularjs очень мощные, но требуется время, чтобы понять, какие процессы стоят за ними.

При создании директив angularjs позволяет вам создавать изолированную область с некоторыми привязками к родительской области. Эти привязки задаются с помощью атрибута прикрепить элемент в DOM и как определить области видимости свойства в объекте директивы определения .

Существует 3 типа параметров привязки, которые вы можете определить в области видимости, и вы записываете их в качестве префиксов связанных атрибутов.

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>

В этом случае в области действия директивы (будь то связывание функции или контроллера) мы можем получить доступ к этим свойствам следующим образом:

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

"Все еще в порядке" Ответ:

Поскольку этот ответ принят, но у него есть некоторые проблемы, я собираюсь обновить его до лучшего. По-видимому, $parseэто сервис, который не лежит в свойствах текущей области, что означает, что он принимает только угловые выражения и не может достичь области. {{, }}выражения компилируются при инициализации angularjs, что означает, что когда мы пытаемся получить к ним доступ в нашем postlinkметоде директив , они уже скомпилированы. ( {{1+1}}уже 2в директиве).

Вот как вы хотели бы использовать:

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

Здесь вы должны заметить одну вещь: если вы хотите установить строку значения, вы должны заключить ее в кавычки. (См 3-й вход)

Вот скрипка для игры: http://jsfiddle.net/neuTA/6/

Старый ответ:

Я не убираю это для людей, которые могут быть введены в заблуждение, как я, обратите внимание, что использование $evalсовершенно нормально, правильный способ сделать это, но $parseимеет другое поведение, вам, вероятно, не понадобится это для использования в большинстве случаев.

Способ сделать это, еще раз, используя scope.$eval. Он не только компилирует угловое выражение, но также имеет доступ к свойствам текущей области.

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

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

function MyCtrl($scope) {

}​

То, что вам не хватает было $eval.

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

Выполняет выражение в текущей области, возвращая результат. Любые исключения в выражении распространяются (uncaught). Это полезно при оценке угловых выражений.

Умур Контачи
источник
Спасибо за ответ, однако это не решение. Я обновил скрипку с вашим кодом. jsfiddle.net/neuTA/3
Шломи Шварц
В Chrome я получаю эту ошибку при попытке использовать область действия. $ Parse: Object # <Object> не имеет метода $ parse. Если я добавлю $ parse service - function ($ parse) {return function (scope ... - затем попробую: "value =" + $ parse (attr.value) - это, похоже, не работает для меня либо
Марк Райкок
@ Вы правы, странно, это работает в примере скрипки ( jsfiddle.net/neuTA/4 ), но не в коде, который у меня есть ... угловые версии?
Шломи Шварц
2
В разделе «Лучший ответ» $scope.textбудет не определена функция связывания. То, как ответ в настоящее время сформулирован, звучит так, будто оно не будет неопределенным. Вы должны использовать $ наблюдаем () (или $ watch () также будет работать здесь), чтобы асинхронно видеть интерполированное значение. Смотрите мой ответ, а также stackoverflow.com/questions/14876112/…
Марк Райкок
1
В ответе «Все еще в порядке» кажется, что $parseсервис вводится, а затем никогда не используется. Я что-то пропустил?
Superjos
83

Для значения атрибута, которое необходимо интерполировать в директиве, которая не использует изолированную область видимости, например,

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

использовать метод атрибутов $observe:

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

Со страницы директивы ,

Наблюдение интерполированных атрибутов: Используйте $observeдля наблюдения за изменениями значений атрибутов, которые содержат интерполяцию (например src="{{bar}}"). Мало того, что это очень эффективно, но это также единственный способ легко получить фактическое значение, потому что на этапе связывания интерполяция еще не была оценена, и поэтому значение в это время установлено в undefined.

Если значение атрибута является просто константой, например,

<input my-directive value="123">

Вы можете использовать $ eval, если значение является числом или логическим значением, и вам нужен правильный тип:

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

Если значение атрибута является строковой константой или вы хотите, чтобы значение было строковым типом в вашей директиве, вы можете получить к нему прямой доступ:

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

В вашем случае, однако, поскольку вы хотите поддерживать интерполированные значения и константы, используйте $observe.

Марк Райкок
источник
это было единственное решение, которое вы нашли?
Шломи Шварц
4
Да, и поскольку страница директивы рекомендует такой подход, я бы так и сделал.
Марк Райкок
7
+1, это лучший ответ IMO, так как он не
накладывает
4

Другие ответы здесь очень правильные и ценные. Но иногда вам просто нужно просто: получить простое старое проанализированное значение при создании экземпляра директивы, не требуя обновлений и не вмешиваясь в изолированную область видимости. Например, может быть удобно предоставить декларативную полезную нагрузку в вашу директиву в виде массива или хеш-объекта в виде:

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

В этом случае, вы можете сократить погоню и просто использовать хороший базовый angular.$eval(attr.attrName).

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

Рабочая скрипка .

XML
источник
Я не знаю, использовали ли вы старую угловую версию или нет, но все ваши примеры кода являются либо недействительным javascript (my-directive-name =), либо недопустимым angular (angular. $ Eval не существует), поэтому -1
BiAiB
Хммм ... учитывая, что этому посту больше года, совсем не удивительно, если что-то с тех пор устарело. Тем не менее, 10-секундный поиск в Google найдет вам много материала о $ eval, в том числе прямо здесь, в SO . И другой пример, который вы приводите, - это вызов в HTML, а не Javascript.
XML
$ scope. $ eval (attr.val) работает в угловой версии 1.4. Требует $ scope для внедрения в функцию директивы link.
Мартин Коннелл
4

Для того же решения, которое я искал Angularjs directive with ng-Model.
Вот код, который решает проблему.

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


Мой результат:

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

}

Используйте $ timeout, потому что директивный вызов после загрузки dom, так что ваши изменения не применяются

user1693371
источник