ฟังก์ชันเรียกคำสั่ง 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); };
}

โดยไม่ต้องผ่าน id ฉันสามารถทำให้มันใช้งานได้ แต่ทันทีที่ฉันพยายามส่งอาร์กิวเมนต์ฟังก์ชันจะไม่ถูกเรียกอีกต่อไป


วิธีเข้าถึงคุณสมบัติของวัตถุผ่านขอบเขตการแยกโดยไม่มีการผูกแบบสองทาง ฉันคิดว่ามันมีประโยชน์ ใช้ขอบเขตแยกและเรียกขอบเขตหลักผ่าน$ scope $ parent
JJbang

คำตอบ:


98

มาร์โคโซลูชั่นที่ทำงานได้ดี

ในทางตรงกันข้ามกับ Angular way ที่แนะนำ (ดังที่แสดงโดย plunkr ของ treeface) คือการใช้นิพจน์เรียกกลับซึ่งไม่จำเป็นต้องกำหนดนิพจน์ Handler ในการเปลี่ยนแปลงตัวอย่างของ marko:

ในเทมเพลต

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

ในฟังก์ชันลิงค์คำสั่ง

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

สิ่งนี้มีข้อเสียอย่างหนึ่งเมื่อเทียบกับโซลูชันของ marko - ในการโหลดครั้งแรกฟังก์ชันMethodToBeCalledจะถูกเรียกใช้ด้วย myParam === ไม่ได้กำหนด

สามารถดูข้อสอบการทำงานได้ที่@treeface Plunker


อันที่จริงรูปแบบของการระบุชื่อฟังก์ชันพร้อมพารามิเตอร์ในแอตทริบิวต์ที่กำหนดเองนี้ดูเหมือนจะเป็นวิธีที่ง่ายและมีประสิทธิภาพที่สุดสำหรับการกำหนด "ฟังก์ชันเรียกกลับ" ในคำสั่ง ... thx !!
rekna

3
มันสับสนมากที่จะเรียก "setProduct" 2 สิ่งที่แตกต่างกัน - แอตทริบิวต์และฟังก์ชันขอบเขตทำให้ยากที่จะเข้าใจว่าอันไหนอยู่ที่ไหน
Dmitri Zaitsev

สิ่งที่น่าสับสนคือทำไมคำตอบที่ถูกต้องใช้ขอบเขตการแยก แต่คำถามไม่ได้ หรือฉันขาดอะไรไป?
j_walker_dev

จากการทดสอบของฉันโดยใช้รหัสนี้ในเชิงมุม 1.2.28 ทำงานได้ดี การทดสอบของฉันไม่เรียก theMethodToBeCalled โดยไม่ได้กำหนดในการโหลดครั้งแรก ตัวอย่างเช่นนักเลง สิ่งนี้ดูเหมือนจะไม่เป็นปัญหา กล่าวอีกนัยหนึ่งดูเหมือนว่านี่จะเป็นแนวทางที่ถูกต้องเมื่อคุณต้องการส่งผ่านพารามิเตอร์ไปยังลิงก์คำสั่ง (ในขอบเขตการแยก) ฉันขอแนะนำให้อัปเดตความคิดเห็นเกี่ยวกับข้อเสียและ myParam === ไม่ได้กำหนด
jazeee

"สิ่งนี้มีข้อเสียอย่างหนึ่งเมื่อเทียบกับโซลูชันของ marko - ในการโหลดครั้งแรกฟังก์ชันMethodToBeCalledจะถูกเรียกใช้ด้วย myParam === undefined" ไม่ได้เกิดขึ้นในกรณีของฉัน .... ฉันเดาว่ามีบริบทโดยนัยอยู่ที่นี่
Victor

95

เพียงเพื่อเพิ่มข้อมูลบางอย่างเพื่อให้คำตอบอื่น ๆ - ใช้&เป็นวิธีที่ดีถ้าคุณจำเป็นต้องมีขอบเขตบางแห่ง

ข้อเสียหลักของการแก้ปัญหาของ marko คือบังคับให้คุณสร้างขอบเขตแยกในองค์ประกอบ แต่คุณสามารถมีได้เพียงหนึ่งในองค์ประกอบเท่านั้น (มิฉะนั้นคุณจะพบข้อผิดพลาดเชิงมุม: คำสั่งหลายคำสั่ง [directive1, directive2] สำหรับขอบเขตที่แยก )

ซึ่งหมายความว่าคุณ:

  • ไม่สามารถใช้มันกับหมวกองค์ประกอบที่มีขอบเขตแยกตัวเอง
  • ไม่สามารถใช้สองคำสั่งกับโซลูชันนี้ในองค์ประกอบเดียวกัน

เนื่องจากคำถามเดิมใช้คำสั่งกับrestrict:'A'ทั้งสองสถานการณ์จึงอาจเกิดขึ้นได้บ่อยในแอปพลิเคชันที่ใหญ่กว่าและการใช้ขอบเขตแยกที่นี่ไม่ใช่แนวทางปฏิบัติที่ดีและไม่จำเป็นด้วย ในความเป็นจริง rekna มีสัญชาตญาณที่ดีในกรณีนี้และเกือบจะใช้งานได้สิ่งเดียวที่เขาทำผิดคือการเรียกฟังก์ชัน$ parsedผิด (ดูสิ่งที่ส่งคืนที่นี่: https://docs.angularjs.org/api/ ng / บริการ / $ แจง )

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 แน่นอน - ไม่จำเป็นต้องแยกขอบเขต - สะอาดกว่ามาก!
Dmitri Zaitsev

จะเกิดอะไรขึ้นถ้าแอตทริบิวต์มีเพียงชื่อเมธอดเช่น <div my-method = 'theMethodToBeCalled'> </div> หรือฟังก์ชันอินไลน์เช่น <div my-method = 'alert ("hi");'> </div>
โจนาธาน

1
หากคุณกำลังมีปัญหากับการใด ๆ $parseของวิธีการเหล่านี้เก็บไว้ในใจว่าวิธีการที่ถูกเรียกว่าจะต้องมีอยู่ในขอบเขตที่คุณส่งผ่านไปยังฟังก์ชั่นกลับมาจาก
ragamufin

คำตอบนี้คือสิ่งที่ฉันกำลังมองหา +1
grimmdude

"theEvent" คืออะไร? ฉันจะเรียกใช้ฟังก์ชันบน div ลูกได้อย่างไร
Shlomo

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 ภายใน theMethodToBeCalled มุมมองจะไม่อัปเดต เป็นไปได้ฉันต้องห่อ 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); };
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.