Директива angularjs вызывает функцию, указанную в атрибуте, и передает ей аргумент


103

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

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

Без передачи идентификатора я могу заставить его работать, но как только я пытаюсь передать аргумент, функция больше не вызывается


как получить доступ к свойству объекта через изолированную область без двусторонней привязки? я думаю, это полезно. используйте изолированную область и вызовите родительскую область через $ scope. $ parent
JJbang

Ответы:


98

Решение Марко работает хорошо .

В отличие от рекомендованного способа Angular (как показано plunkr treeface) следует использовать выражение обратного вызова, которое не требует определения expressionHandler. В примере Марко изменение:

В шаблоне

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

В функции директивы ссылки

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

У этого есть один недостаток по сравнению с решением Марко - при первой загрузке функцияMethodToBeCalled будет вызываться с myParam === undefined.

Рабочий экзамен можно найти на @treeface Plunker


В самом деле, этот шаблон предоставления имени функции параметру в настраиваемом атрибуте кажется самым простым и надежным способом определения «функций обратного вызова» в директивах ... спасибо !!
rekna

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

Что сбивает с толку, так это то, почему правильный ответ - использовать изолированную область, а вопрос - нет. Или мне что-то не хватает?
j_walker_dev

Судя по моим тестам с использованием этого кода в angular 1.2.28, он работает хорошо. Мои тесты не вызывают theMethodToBeCalled с неопределенным значением при первой загрузке. Пример с плункером тоже. Кажется, это не проблема. Другими словами, это кажется правильным подходом, когда вы хотите передать параметры в ссылку директивы (в изолированной области). Рекомендую обновить комментарий о недостатке и myParam === undefined.
jazeee

Это «У этого есть один недостаток по сравнению с решением Марко - при первой загрузке функцияMethodToBeCalled будет вызываться с myParam === undefined» не происходит в моем случае .... Я думаю, здесь есть неявный контекст
Виктор

95

Просто чтобы добавить некоторую информацию к другим ответам - использование &- хороший способ, если вам нужна изолированная область.

Основным недостатком решения marko является то, что оно заставляет вас создавать изолированную область видимости для элемента, но вы можете иметь только одну из них на элементе (в противном случае вы столкнетесь с угловой ошибкой: несколько директив [директива1, директива2] с запросом для изолированного прицела )

Это значит, что ты :

  • не может использовать его на элементе, шляпа сама имеет изолированную область видимости
  • нельзя использовать две директивы с этим решением для одного и того же элемента

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

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 ("hi");'> </div>
Джонатан.

1
Если у вас возникли проблемы с любым из этих подходов, имейте в виду, что вызываемый метод должен быть доступен в области, которую вы передаете функции, возвращаемой из $parse.
ragamufin

Это именно то, что я искал +1
grimmdude

что такое «событие»? Как я могу выполнить функцию для дочернего div?
Шломо

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>

Функция вызывается, когда я устанавливаю $ scope.id = id внутриMethodToBeCalled, представление не обновляется. Возможно, мне нужно обернуть expressionHandler (id) внутри scope.apply.
rekna

2
Замечательно, это помогло мне найти решение моей проблемы. Стоит отметить, что вам нужно делать это на верхнем уровне только в том случае, если вы делаете более глубокий набор директив. Рассмотрим это: plnkr.co/edit/s3y67iGL12F2hDER2RNl?p=preview, где я передаю метод через две директивы.
treeface

3
А вот второй (возможно, более угловатый) способ сделать это: plnkr.co/edit/r9CV9Y1RWRuse4RwFPka?p=preview
treeface

1
Как я могу это сделать без изоляции области видимости?
Evan Lévesque

3
+1, потому что это решение научило меня, что мне нужно вызвать scope.method (); сначала получить фактическую ссылку на метод.
Sal

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.