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

idを渡さなくても機能しますが、引数を渡そうとするとすぐに関数が呼び出されなくなります


双方向バインディングなしで分離スコープを介してオブジェクトプロパティにアクセスする方法 参考になりました。分離されたスコープを使用し、$ scope。$ parentを
JJbang

回答:


98

マルコのソリューションはうまく機能します

(treefaceのplunkrで示されている)推奨されるAngularの方法とは対照的に、expressionHandlerを定義する必要のないコールバック式を使用します。markoの例の変更:

テンプレート内

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

ディレクティブリンク機能

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

これには、マルコのソリューションと比較して1つの欠点があります。最初のロード時に、theMethodToBeCalled関数がmyParam === undefinedで呼び出されます。

実用的な例は@treeface Plunkerにあります。


確かに、カスタム属性のパラメーターに関数名を指定するこのパターンは、ディレクティブで「コールバック関数」を定義するための最も簡単で最も堅牢な方法のようです... thx !!
rekna 2013年

3
「setProduct」を2つの異なるもの(属性とスコープ関数)で呼び出すのは非常に混乱し、どちらがどこにあるのかを理解するのが非常に難しくなります。
Dmitri Zaitsev 2014

混乱しているのは、正しい答えが分離スコープを使用しているのに、質問では使用しない理由です。それとも何か不足していますか?
j_walker_dev 2014年

このコードをangular 1.2.28で使用した私のテストから、それはうまく機能します。私のテストでは、最初のロード時にundefinedでtheMethodToBeCalledを呼び出しません。プランカーの例も同様です。これは問題ではないようです。つまり、ディレクティブリンク(分離スコープ内)にパラメーターを渡したい場合、これは正しいアプローチのように見えます。欠点とmyParam === undefinedに関するコメントを更新することをお勧めします。
jazeee

この「マルコのソリューションと比較して、これには1つの欠点があります。最初のロード時に、theMethodToBeCalled関数はmyParam === undefinedで呼び出されます」は私の場合は発生しません。...ここに暗黙のコンテキストがあると思います
Victor

95

他の回答にいくつかの情報を追加するだけ&です。分離されたスコープが必要な場合は、これを使用することをお勧めします。

markoのソリューションの主な欠点は、要素に分離されたスコープを作成することを強制することですが、要素にはそれらの1つしか持てません(そうしないと、角度エラーが発生します:複数のディレクティブ[directive1、directive2]が要求する分離スコープ用

これはあなたを意味します:

  • それ自体が孤立したスコープを持つ要素の帽子には使用できません
  • 同じ要素でこのソリューションで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
ラガムフィン

この答えは、まさに私が探していたものです+1
grimmdude

「theEvent」とは何ですか?子divで関数を実行するにはどうすればよいですか?
Shlomo、2015

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>

メソッドが呼び出され、theMethodToBeCalled内で$ scope.id = idを設定すると、ビューが更新されません。おそらく、scope.apply内でexpressionHandler(id)をラップする必要があります。
rekna 2013

2
これは、問題の解決策を見つけるのに役立ちました。より深いネストのディレクティブを実行している場合にのみ、これをトップレベルで実行する必要があることに言及する価値があります。これを考慮してください:plnkr.co/edit/s3y67iGL12F2hDER2RNl?p=previewここで、2つのディレクティブを介してメソッドを渡します。
treeface 2013年

3
そして、これがそれを実行する2番目の(おそらくより角度のある)方法です:plnkr.co/edit/r9CV9Y1RWRuse4RwFPka
p=

1
スコープを分離せずにどうすればよいですか?
エヴァンレベスク2014年

3
+1は、このソリューションから、scope.method()を呼び出す必要があることがわかったためです。まず、メソッドへの実際の参照を取得します。
Sal

6

を使用attrName: "&"して外部スコープの式を参照することにより、paramsで関数呼び出しを実行するディレクティブを作成できます。

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); };
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.