Як оцінити атрибути всередині спеціальної директиви


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

Одне, що ви повинні помітити тут, це те, що якщо ви хочете встановити рядок значення, вам слід загорнути його в лапки. (Див. Третій вхід)

Ось ця загадка, з якою можна грати: 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> немає методу "$ parse". Якщо я ввожу службу $ parse - функція ($ parse) {функція повернення (область ... - тоді спробуйте: "value =" + $ parse (attr.value) - це, здається, не працює для мене або
Марк Райкок

@Марк ви маєте рацію, дивно це працює на прикладі скрипки ( jsfiddle.net/neuTA/4 ), але не в коді, який у мене є ... кутові версії?
Шломі Шварц

2
У розділі "Краща відповідь" $scope.textбуде визначено функцію зв'язування. Те, як формулюється відповідь на даний момент, звучить як би не було визначено. Для асинхронного перегляду інтерпольованого значення потрібно використовувати $ obser () (або насправді $ watch () також буде працювати тут). Дивіться мою відповідь , а також stackoverflow.com/questions/14876112 / ...
Марк Rajcok

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, оскільки вона не змушує застосовувати директиву, а також охоплює зміни атрибутів з $
obser

4

Інші відповіді тут дуже правильні та цінні. Але іноді просто хочеться просто: отримати звичайне старе проаналізоване значення при створенні директиви, без необхідності оновлень і без водіння з ізоляційною областю. Наприклад, може бути зручно надати декларативний корисний вантаж у вашу директиву у вигляді масиву або хеш-об’єкта у формі:

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

У такому випадку ви можете вирізати погоню і просто використовувати хороший базовий angular.$eval(attr.attrName).

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

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


Я не знаю, використовували ви стару кутову версію чи ні, але всі ваші зразки коду є або недійсним JavaScript (моя-директива-назва =), або недійсним кутовим (кутовий. $ Eval не існує), так -1
BiAiB

Уммм ... враховуючи, що цій посаді більше року, це не було б дивно, якби щось з тих пір застаріло. Однак 10-секундний пошук Google знайде вам багато матеріалів на $ eval, в тому числі тут, на SO . А другий приклад, який ви наводите, - це виклик у HTML, а не в JavaScript.
XML

$ range. $ eval (attr.val) працює в кутовому 1.4. Потрібен $ range для введення у функцію посилання директиви.
Мартін Коннелл

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, тому ваші зміни не застосовуються

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.