Функція виклику директиви angularjs, визначена в атрибуті, передає йому аргумент


102

Я хочу створити директиву, яка посилається на атрибут. Атрибут вказує функцію, яку слід викликати в області застосування. Але я також хочу передати аргумент функції, яка визначається всередині функції зв'язку.

<div my-method='theMethodToBeCalled'></div>

У функції посилання я прив'язуюсь до події jQuery, яка передає аргумент, який мені потрібно передати функції:

app.directive("myMethod",function($parse) {
  restrict:'A',
  link:function(scope,element,attrs) {
     var expressionHandler = $parse(attrs.myMethod);
     $(element).on('theEvent',function( e, rowid ) {
        id = // some function called to determine id based on rowid
        scope.$apply(function() {expressionHandler(id);});
     }
  }
}

app.controller("myController",function($scope) {
   $scope.theMethodToBeCalled = function(id) { alert(id); };
}

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


як отримати доступ до власності об'єкта через ізоляційну область без двостороннього прив'язки? я думаю, що це корисно. скористайтеся ізольованою сферою дії та зателефонуйте до батьківського діапазону через $
range

Відповіді:


98

Рішення Марко працює добре .

На відміну від рекомендованого кутового способу (як показано на plunkr дерева), використовується вираз зворотного виклику, який не потребує визначення вираженняHandler. У прикладі Марко зміни:

У шаблоні

<div my-method="theMethodToBeCalled(myParam)"></div>

У директиві функція зв'язку

$(element).click(function( e, rowid ) {
  scope.method({myParam: id});
});

У цього є один недолік порівняно з рішенням Марко - при першому завантаженні функціяMethodToBeCalled буде викликана за допомогою myParam === undefined.

Робочий іспит можна знайти на сайті @treeface Plunker


Дійсно, ця модель надання імені функції параметром у користувальницькому атрибуті здається найпростішим та найнадійнішим способом визначення "функцій зворотного виклику" в директивах ... thx !!
rekna

3
Дуже заплутано називати "setProduct" двома різними речами - функцію атрибутів і областей, що робить насправді важко зрозуміти, яка з них де.
Дмитро Зайцев

Що заплутано, це те, чому правильна відповідь використовує ізоляційну сферу, але питання не відповідає. Або я щось пропускаю?
j_walker_dev

З моїх тестів, що використовують цей код у кутовій 1.2.28, він працює добре. Мої тести не викликають TheMethodToBeCalled з невизначеним при першому завантаженні. Так само не є прикладом планку. Це, здається, не є проблемою. Іншими словами, це, мабуть, є правильним підходом, коли потрібно передати параметри у посилання на директиву (в ізоляційній області). Я рекомендую оновити коментар щодо недоліків та myParam === undefined.
jazeee

Це "У цього є один недолік порівняно з рішенням Марко - при першому завантаженні функціяMethodToBeCalled буде викликана myParam === undefined", у моєму випадку не відбувається .... я думаю, тут є якийсь неявний контекст
Віктор

95

Просто додати трохи інформації до інших відповідей - використання &- це хороший спосіб, якщо вам потрібна ізольована область.

Основним недоліком рішення marko є те, що воно змушує вас створити ізольований діапазон для елемента, але ви можете мати лише один із елементів на елементі (інакше ви зіткнетеся з кутовою помилкою: Кілька директив [директива1, директива2] із запитом для ізольованого обсягу )

Це означає, що ви:

  • не може використовувати його на елементі капелюх, має сам ізольований обсяг
  • не може використовувати дві директиви з цим рішенням на одному елементі

Оскільки в оригінальному запитанні використовується директива з restrict:'A'обома ситуаціями, може виникати досить часто у великих програмах, а використання ізольованої області застосування тут не є хорошою практикою, а також зайвою. Насправді rekna мав добру інтуїцію в цьому випадку, і мало не працював, єдине, що він робив неправильно, - це неправильно називати функцію $ parsed (дивіться, що вона повертається тут: https://docs.angularjs.org/api/ нг / сервіс / $ розбір ).

TL; DR; Виправлений код питання

<div my-method='theMethodToBeCalled(id)'></div>

і код

app.directive("myMethod",function($parse) {
  restrict:'A',
  link:function(scope,element,attrs) {
     // here you can parse any attribute (so this could as well be,
     // myDirectiveCallback or multiple ones if you need them )
     var expressionHandler = $parse(attrs.myMethod);
     $(element).on('theEvent',function( e, rowid ) {
        calculatedId = // some function called to determine id based on rowid

        // HERE: call the parsed function correctly (with scope AND params object)
        expressionHandler(scope, {id:calculatedId});
     }
  }
}

app.controller("myController",function($scope) {
   $scope.theMethodToBeCalled = function(id) { alert(id); };
}

11
+1 Дійсно - не потрібно ізолювати область - набагато чистіше!
Дмитро Зайцев

що робити, якщо атрибут містить лише ім'я методу, як <div my-method = 'theMethodToBeCalled'> </div> або вбудовану функцію, як <div my-method = 'alert ("привіт");'> </div>
Джонатан.

1
Якщо у вас виникають проблеми з будь-яким із цих підходів, майте на увазі, що викликаний метод повинен бути доступний у межах, з якого ви переходите до функції, повернутої з нього $parse.
ragamufin

Ця відповідь саме те, що я шукав +1
похмурий день

що таке "подія"? Як я можу виконати функцію на дитячому діві?
Шломо

88

Не знаючи точно, що ви хочете зробити ... але все ж ось можливе рішення.

Створіть область із властивістю "&" у локальному масштабі. Він "надає спосіб виконати вираз у контексті батьківської області" ( детальну інформацію див. У документації директиви ).

Я також зауважив, що ви використовували функцію зв’язування скорочень і вписували в атрибути об'єкта там. Ви не можете цього зробити. Ясніше (imho) просто повернути об’єкт визначення директиви. Дивіться мій код нижче.

Ось зразок коду та загадка .

<div ng-app="myApp">
<div ng-controller="myController">
    <div my-method='theMethodToBeCalled'>Click me</div>
</div>
</div>

<script>

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

   app.directive("myMethod",function($parse) {
       var directiveDefinitionObject = {
         restrict: 'A',
         scope: { method:'&myMethod' },
         link: function(scope,element,attrs) {
            var expressionHandler = scope.method();
            var id = "123";

            $(element).click(function( e, rowid ) {
               expressionHandler(id);
            });
         }
       };
       return directiveDefinitionObject;
   });

   app.controller("myController",function($scope) {
      $scope.theMethodToBeCalled = function(id) { 
          alert(id); 
      };
   });

</script>

Функція викликається, коли я встановлюю $ range.id = id всерединіMethodToBeCalled, вигляд не оновлюється. Ймовірно, мені потрібно загорнути expressHandler (id) всередині сфери застосування.
rekna

2
Дивовижно це допомогло мені знайти рішення своєї проблеми. Варто зазначити, що робити це потрібно лише на найвищому рівні, якщо ви робите більш глибоке гніздо директив. Враховуйте це: plnkr.co/edit/s3y67iGL12F2hDER2RNl?p=preview, де я передаю метод через дві директиви.
поверхня дерева

3
І ось другий (можливо більш кутовий) спосіб зробити це: plnkr.co/edit/r9CV9Y1RWRuse4RwFPka?p=preview
дерево

1
Як я можу це зробити без ізоляції сфери?
Еван Левеск

3
+1, тому що це рішення навчило мене, що мені потрібно викликати область.method (); спочатку, щоб отримати фактичне посилання на метод.
Сал

6

Ви можете створити директиву, яка виконує виклик функції парами, використовуючи attrName: "&"посилання на вираз у зовнішній області.

Ми хочемо замінити ng-clickдирективу на ng-click-x:

<button ng-click-x="add(a,b)">Add</button>

Якби у нас була така сфера:

$scope.a = 2;
$scope.b = 2;

$scope.add = function (a, b) {
  $scope.result = parseFloat(a) + parseFloat(b);
}

Ми можемо написати нашу директиву так:

angular.module("ng-click-x", [])

.directive('ngClickX', [function () {

  return {

    scope: {

      // Reference the outer scope
      fn: "&ngClickX",

    },

    restrict: "A",

    link: function(scope, elem) {

      function callFn () {
        scope.$apply(scope.fn());
      }

      elem[0].addEventListener('click', callFn);
    }
  };
}]);

Ось демонстрація в прямому ефірі: http://plnkr.co/edit/4QOGLD?p=info


2

Ось що для мене спрацювало.

Html, використовуючи директиву

 <tr orderitemdirective remove="vm.removeOrderItem(orderItem)" order-item="orderitem"></tr>

Html директиви: orderitem.directive.html

<md-button type="submit" ng-click="remove({orderItem:orderItem})">
       (...)
</md-button>

Область застосування директиви:

scope: {
    orderItem: '=',
    remove: "&",

0

Моє рішення:

  1. на полімер викликають подію (напр. complete)
  2. визначити директиву, що пов'язує подію з функцією управління

Директива

/*global define */
define(['angular', './my-module'], function(angular, directives) {
    'use strict';
    directives.directive('polimerBinding', ['$compile', function($compile) {

            return {
                 restrict: 'A',
                scope: { 
                    method:'&polimerBinding'
                },
                link : function(scope, element, attrs) {
                    var el = element[0];
                    var expressionHandler = scope.method();
                    var siemEvent = attrs['polimerEvent'];
                    if (!siemEvent) {
                        siemEvent = 'complete';
                    }
                    el.addEventListener(siemEvent, function (e, options) {
                        expressionHandler(e.detail);
                    })
                }
            };
        }]);
});

Полімерний компонент

<dom-module id="search">

<template>
<h3>Search</h3>
<div class="input-group">

    <textarea placeholder="search by expression (eg. temperature>100)"
        rows="10" cols="100" value="{{text::input}}"></textarea>
    <p>
        <button id="button" class="btn input-group__addon">Search</button>
    </p>
</div>
</template>

 <script>
  Polymer({
    is: 'search',
            properties: {
      text: {
        type: String,
        notify: true
      },

    },
    regularSearch: function(e) {
      console.log(this.range);
      this.fire('complete', {'text': this.text});
    },
    listeners: {
        'button.click': 'regularSearch',
    }
  });
</script>

</dom-module>

Сторінка

 <search id="search" polimer-binding="searchData"
 siem-event="complete" range="{{range}}"></siem-search>

searchData є функцією управління

$scope.searchData = function(searchObject) {
                    alert('searchData '+ searchObject.text + ' ' + searchObject.range);

}

-2

Це має спрацювати.

<div my-method='theMethodToBeCalled'></div>

app.directive("myMethod",function($parse) {
  restrict:'A',
  scope: {theMethodToBeCalled: "="}
  link:function(scope,element,attrs) {
     $(element).on('theEvent',function( e, rowid ) {
        id = // some function called to determine id based on rowid
        scope.theMethodToBeCalled(id);
     }
  }
}

app.controller("myController",function($scope) {
   $scope.theMethodToBeCalled = function(id) { alert(id); };
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.