funzione di chiamata alla direttiva angularjs specificata nell'attributo e passarle un argomento


102

Voglio creare una direttiva che colleghi a un attributo. L'attributo specifica la funzione che dovrebbe essere chiamata nell'ambito. Ma voglio anche passare un argomento alla funzione determinata all'interno della funzione di collegamento.

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

Nella funzione link mi associo a un evento jQuery, che passa un argomento che devo passare alla funzione:

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

Senza passare l'id posso farlo funzionare, ma non appena provo a passare un argomento, la funzione non viene più chiamata


come accedere alla proprietà dell'oggetto tramite l'ambito isolato senza associazione a due vie? penso che sia utile. utilizzare l'ambito isolato e chiamare l'ambito padre tramite $ scope. $ parent
JJbang

Risposte:


98

La soluzione di Marko funziona bene .

Contrastare con il modo angolare consigliato (come mostrato dal plunkr di treeface) è usare un'espressione di callback che non richiede la definizione di expressionHandler. Nell'esempio di marko cambia:

Nel modello

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

Nella funzione di collegamento direttiva

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

Questo ha uno svantaggio rispetto alla soluzione di marko: al primo caricamento la funzioneMethodToBeCalled verrà invocata con myParam === undefined.

Un esempio funzionante può essere trovato su @treeface Plunker


In effetti, questo modello di fornire il nome della funzione con il parametro nell'attributo personalizzato sembra essere il modo più semplice e robusto per definire le "funzioni di callback" nelle direttive ... grazie !!
rekna

3
È molto confuso chiamare "setProduct" 2 cose diverse - attributo e funzione di ambito, rende davvero difficile capire quale sia dove.
Dmitri Zaitsev

Ciò che è fonte di confusione è perché la risposta giusta sta usando l'ambito isolato ma la domanda no. Oppure mi sfugge qualcosa?
j_walker_dev

Dai miei test utilizzando questo codice in angolare 1.2.28, funziona bene. I miei test non chiamano theMethodToBeCalled con undefined al primo caricamento. Nemmeno l'esempio del plunker. Questo non sembra essere un problema. In altre parole, questo sembra essere l'approccio corretto quando si desidera passare i parametri al collegamento direttiva (in un ambito isolato). Consiglio di aggiornare il commento relativo allo svantaggio e myParam === undefined.
jazeee

Questo "Questo ha uno svantaggio rispetto alla soluzione di marko - al primo caricamento la funzioneMethodToBeCalled verrà invocata con myParam === undefined" non sta accadendo nel mio caso .... immagino che ci sia un contesto implicito qui
Victor

95

Solo per aggiungere alcune informazioni alle altre risposte: l'uso &è un buon modo se hai bisogno di un ambito isolato.

Lo svantaggio principale della soluzione di marko è che ti costringe a creare uno scope isolato su un elemento, ma puoi averne solo uno su un elemento (altrimenti incorrerai in un errore angolare: più direttive [direttiva1, direttiva2] chiedendo per ambito isolato )

Ciò significa che:

  • non posso usarlo su un elemento che il cappello ha un ambito isolato
  • non è possibile utilizzare due direttive con questa soluzione sullo stesso elemento

Poiché la domanda originale utilizza una direttiva con restrict:'A' entrambe le situazioni potrebbe sorgere abbastanza spesso in applicazioni più grandi, e l'utilizzo di un ambito isolato qui non è una buona pratica e anche non necessario. In effetti rekna ha avuto una buona intuizione in questo caso, e quasi funzionava, l'unica cosa che stava sbagliando era chiamare la funzione $ parsed sbagliata (guarda cosa restituisce qui: https://docs.angularjs.org/api/ ng / service / $ parse ).

TL; DR; Codice domanda corretto

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

e il codice

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 Effettivamente - non c'è bisogno di isolare l'ambito - molto più pulito!
Dmitri Zaitsev

cosa succede se l'attributo contiene solo il nome del metodo, come <div my-method = 'theMethodToBeCalled'> </div> o una funzione inline come <div my-method = 'alert ("hi");'> </div>
Jonathan.

1
Se hai problemi con uno di questi approcci, tieni presente che il metodo chiamato deve essere disponibile nell'ambito da cui passi alla funzione restituita $parse.
ragamufin

Questa risposta è esattamente quello che stavo cercando +1
grimmdude

cos'è "theEvent"? Come posso eseguire la funzione su un div figlio?
Shlomo

88

Non sapendo esattamente cosa vuoi fare ... ma c'è comunque una possibile soluzione.

Crea un ambito con una proprietà "&" nell'ambito locale. "Fornisce un modo per eseguire un'espressione nel contesto dell'ambito padre" (vedere la documentazione della direttiva per i dettagli).

Ho anche notato che hai usato una funzione di collegamento abbreviato e hai inserito gli attributi degli oggetti lì dentro. Non puoi farlo. È più chiaro (imho) restituire solo l'oggetto di definizione della direttiva. Vedi il mio codice qui sotto.

Ecco un esempio di codice e un violino .

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

La funzione viene chiamata, quando imposto $ scope.id = id all'interno diMethodToBeCalled la vista non viene aggiornata. Probabilmente ho bisogno di racchiudere expressionHandler (id) all'interno di scope.apply.
rekna

2
Fantastico, questo mi ha aiutato a trovare la soluzione al mio problema. Vale la pena ricordare che devi farlo solo al livello più alto se stai facendo un nido più profondo di direttive. Considera questo: plnkr.co/edit/s3y67iGL12F2hDER2RNl?p=preview dove passo il metodo attraverso due direttive.
treeface

3
Ed ecco un secondo (forse più spigoloso) modo per farlo: plnkr.co/edit/r9CV9Y1RWRuse4RwFPka?p=preview
treeface

1
Come posso farlo senza l'isolamento dell'ambito?
Evan Lévesque

3
+1 perché questa soluzione mi ha insegnato che devo chiamare scope.method (); prima per ottenere il riferimento effettivo al metodo.
Sal

6

È possibile creare una direttiva che esegua una chiamata di funzione con parametri utilizzando il attrName: "&" per fare riferimento all'espressione nell'ambito esterno.

Vogliamo sostituire la ng-clickdirettiva con ng-click-x:

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

Se avessimo questo scopo:

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

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

Potremmo scrivere la nostra direttiva in questo modo:

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

Ecco una demo dal vivo: http://plnkr.co/edit/4QOGLD?p=info


2

Ecco cosa ha funzionato per me.

Html utilizzando la direttiva

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

Html della direttiva: orderitem.directive.html

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

Campo di applicazione della direttiva:

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

0

La mia soluzione:

  1. sul polimero solleva un evento (es. complete)
  2. definire una direttiva che colleghi l'evento alla funzione di controllo

Direttiva

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

Componente polimero

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

Pagina

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

searchData è la funzione di controllo

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

}

-2

Questo dovrebbe funzionare.

<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); };
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.