Die im Attribut angegebene Funktion zum Aufrufen der Direktive anglejs übergibt ihr ein Argument


102

Ich möchte eine Direktive erstellen, die auf ein Attribut verweist. Das Attribut gibt die Funktion an, die für den Bereich aufgerufen werden soll. Ich möchte aber auch ein Argument an die Funktion übergeben, die innerhalb der Link-Funktion festgelegt ist.

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

In der Link-Funktion binde ich an ein jQuery-Ereignis, das ein Argument übergibt, das ich an die Funktion übergeben muss:

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

Ohne die ID zu übergeben, kann ich es zum Laufen bringen, aber sobald ich versuche, ein Argument zu übergeben, wird die Funktion nicht mehr aufgerufen


Wie kann ich über einen isolierten Bereich ohne bidirektionale Bindung auf Objekteigenschaften zugreifen? Ich finde es hilfreich. Verwenden Sie den isolierten Bereich und rufen Sie den übergeordneten Bereich über $ scope auf. $ parent
JJbang

Antworten:


98

Markos Lösung funktioniert gut .

Im Gegensatz zur empfohlenen Angular-Methode (wie in Treeface's Plunkr gezeigt) wird ein Callback-Ausdruck verwendet, für den der expressionHandler nicht definiert werden muss. In Markos Beispieländerung:

In Vorlage

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

In Direktiven-Link-Funktion

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

Dies hat einen Nachteil gegenüber der Lösung von marko: Beim ersten Laden wird die Funktion MethodToBeCalled mit myParam === undefined aufgerufen.

Eine funktionierende Exampe finden Sie unter @treeface Plunker


In der Tat scheint dieses Muster der Bereitstellung des Funktionsnamens mit Parametern im benutzerdefinierten Attribut die einfachste und robusteste Methode zum Definieren von "Rückruffunktionen" in Direktiven zu sein ... thx !!
rekna

3
Es ist sehr verwirrend, "setProduct" 2 verschiedene Dinge aufzurufen - Attribut- und Bereichsfunktion, was es wirklich schwierig macht zu verstehen, welches wo ist.
Dmitri Zaitsev

Was verwirrend ist, ist, warum die richtige Antwort den isolierten Bereich verwendet, die Frage jedoch nicht. Oder fehlt mir etwas?
j_walker_dev

Nach meinen Tests mit diesem Code in Winkel 1.2.28 funktioniert es gut. Meine Tests rufen theMethodToBeCalled beim ersten Laden nicht mit undefined auf. Das Plunker-Beispiel auch nicht. Dies scheint kein Problem zu sein. Mit anderen Worten, dies scheint der richtige Ansatz zu sein, wenn Sie Parameter an den Direktivenlink übergeben möchten (in einem isolierten Bereich). Ich empfehle, den Kommentar bezüglich des Nachteils und myParam === undefined zu aktualisieren.
Jazeee

Dieses "Dies hat einen Nachteil gegenüber der Marko-Lösung - beim ersten Laden wird die MethodToBeCalled-Funktion mit myParam === undefined aufgerufen" tritt in meinem Fall nicht auf ... Ich denke, hier gibt es einen impliziten Kontext
Victor

95

Nur um den anderen Antworten einige Informationen hinzuzufügen - die Verwendung &ist ein guter Weg, wenn Sie einen isolierten Bereich benötigen.

Der Hauptnachteil von Markos Lösung besteht darin, dass Sie gezwungen sind, einen isolierten Bereich für ein Element zu erstellen. Sie können jedoch nur einen Bereich für ein Element festlegen (andernfalls tritt ein Winkelfehler auf: Mehrere Direktiven [Direktive1, Direktive2] fragen für isolierten Bereich )

Das bedeutet du :

  • Ich kann es nicht für ein Element verwenden. Hat selbst einen isolierten Bereich
  • Mit dieser Lösung können nicht zwei Direktiven für dasselbe Element verwendet werden

Da in der ursprünglichen Frage eine Richtlinie verwendet wird, restrict:'A'kann es in größeren Situationen häufig vorkommen, dass in beiden Situationen ein isolierter Geltungsbereich verwendet wird. Dies ist keine gute Praxis und auch nicht erforderlich. Tatsächlich hatte rekna in diesem Fall eine gute Intuition und hätte fast funktioniert. Das einzige, was er falsch machte, war, die $ parsed- Funktion falsch aufzurufen (siehe, was sie hier zurückgibt: https://docs.angularjs.org/api/). ng / service / $ parse ).

TL; DR; Fragencode behoben

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

und der Code

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 In der Tat - keine Notwendigkeit, das Zielfernrohr zu isolieren - viel sauberer!
Dmitri Zaitsev

Was ist, wenn das Attribut nur den Methodennamen enthält, z. B. <div my-method = 'theMethodToBeCalled'> </ div> oder eine Inline-Funktion wie <div my-method = 'alert ("hi");'> </ div>
Jonathan.

1
Wenn Sie Probleme mit einem dieser Ansätze haben, beachten Sie, dass die aufgerufene Methode in dem Bereich verfügbar sein muss, den Sie an die zurückgegebene Funktion übergeben $parse.
Ragamufin

Diese Antwort ist genau das, wonach ich gesucht habe +1
grimmdude

Was ist die Veranstaltung"? Wie kann ich die Funktion auf einem untergeordneten Div ausführen?
Shlomo

88

Sie wissen nicht genau, was Sie tun möchten ... aber hier ist eine mögliche Lösung.

Erstellen Sie einen Bereich mit einer '&' - Eigenschaft im lokalen Bereich. Es "bietet eine Möglichkeit, einen Ausdruck im Kontext des übergeordneten Bereichs auszuführen" ( Einzelheiten finden Sie in der Direktivendokumentation ).

Mir ist auch aufgefallen, dass Sie eine Kurzverknüpfungsfunktion verwendet und dort Objektattribute eingefügt haben. Das kannst du nicht machen. Es ist klarer (imho), nur das Direktivendefinitionsobjekt zurückzugeben. Siehe meinen Code unten.

Hier ist ein Codebeispiel und eine Geige .

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

Die Funktion wird aufgerufen, wenn ich $ scope.id = id in theMethodToBeCalled setze, wird die Ansicht nicht aktualisiert. Wahrscheinlich muss ich expressionHandler (id) in scope.apply einschließen.
rekna

2
Das hat mir geholfen, die Lösung für mein Problem zu finden. Es ist erwähnenswert, dass Sie dies nur auf oberster Ebene tun müssen, wenn Sie ein tieferes Nest von Anweisungen ausführen. Beachten Sie Folgendes : plnkr.co/edit/s3y67iGL12F2hDER2RNl?p=preview, wobei ich die Methode über zwei Anweisungen übergebe.
Baumgesicht

3
Und hier ist eine zweite (vielleicht eckigere) Möglichkeit: plnkr.co/edit/r9CV9Y1RWRuse4RwFPka?p=preview
treeface

1
Wie kann ich das ohne Bereichsisolierung tun?
Evan Lévesque

3
+1, weil diese Lösung mich gelehrt hat, dass ich scope.method () aufrufen muss; zuerst, um den tatsächlichen Verweis auf die Methode zu erhalten.
Sal

6

Sie können eine Direktive erstellen, die einen Funktionsaufruf mit Parametern ausführt, indem Sie mit attrName: "&"auf den Ausdruck im äußeren Bereich verweisen.

Wir wollen die ng-clickRichtlinie ersetzen durch ng-click-x:

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

Wenn wir diesen Umfang hätten:

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

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

Wir könnten unsere Richtlinie so schreiben:

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

Hier ist eine Live-Demo: http://plnkr.co/edit/4QOGLD?p=info


2

Folgendes hat bei mir funktioniert.

HTML mit der Direktive

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

HTML der Direktive: orderitem.directive.html

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

Geltungsbereich der Richtlinie:

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

0

Meine Lösung:

  1. auf Polymer erhöht ein Ereignis (z. B. complete)
  2. Definieren Sie eine Direktive, die das Ereignis mit der Steuerfunktion verknüpft

Richtlinie

/*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);
                    })
                }
            };
        }]);
});

Polymerkomponente

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

Seite

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

searchData ist die Steuerfunktion

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

}

-2

Das sollte funktionieren.

<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); };
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.