วิธีรับคุณลักษณะที่ประเมินภายในคำสั่งที่กำหนดเอง


363

ฉันพยายามรับคุณลักษณะที่ประเมินจากคำสั่งที่กำหนดเองของฉัน แต่ฉันไม่สามารถหาวิธีที่ถูกต้องในการทำมัน

ฉันสร้างjsFiddle นี้ขึ้นเพื่ออธิบายอย่างละเอียด

<div ng-controller="MyCtrl">
    <input my-directive value="123">
    <input my-directive value="{{1+1}}">
</div>

myApp.directive('myDirective', function () {
    return function (scope, element, attr) {
        element.val("value = "+attr.value);
    }
});

ฉันกำลังคิดถึงอะไร


คุณสามารถทำตามลิงค์ด้านล่างเพื่อทำความเข้าใจเกี่ยวกับคำสั่ง undefinednull.com/2014/02/11/…
Prasanna Sasne

คำตอบ:


573

แจ้งให้ทราบล่วงหน้า: ฉันจะปรับปรุงคำตอบนี้ในขณะที่ฉันหาทางออกที่ดี ฉันยังเก็บคำตอบเก่าไว้สำหรับใช้อ้างอิงในอนาคตตราบใดที่ยังมีความเกี่ยวข้องอยู่ คำตอบล่าสุดและดีที่สุดมาก่อน

คำตอบที่ดีกว่า:

คำสั่งในเชิงมุมมีพลังมาก แต่ต้องใช้เวลาในการทำความเข้าใจว่ากระบวนการใดอยู่เบื้องหลังพวกเขา

ขณะสร้างคำสั่ง angularjs ให้คุณสร้างขอบเขตที่แยกได้ซึ่งมีการเชื่อมโยงกับขอบเขตพาเรนต์ ผูกเหล่านี้จะถูกระบุโดยแอตทริบิวต์คุณแนบองค์ประกอบใน DOM และวิธีการที่คุณกำหนดขอบเขตทรัพย์สินในวัตถุนิยามคำสั่ง

มีตัวเลือกการรวม 3 ชนิดซึ่งคุณสามารถกำหนดในขอบเขตและคุณเขียนสิ่งเหล่านั้นเป็นคุณลักษณะคำนำหน้าที่เกี่ยวข้อง

angular.module("myApp", []).directive("myDirective", function () {
    return {
        restrict: "A",
        scope: {
            text: "@myText",
            twoWayBind: "=myTwoWayBind",
            oneWayBind: "&myOneWayBind"
        }
    };
}).controller("myController", function ($scope) {
    $scope.foo = {name: "Umur"};
    $scope.bar = "qwe";
});

HTML

<div ng-controller="myController">
    <div my-directive my-text="hello {{ bar }}" my-two-way-bind="foo" my-one-way-bind="bar">
    </div>
</div>

ในกรณีนั้นในขอบเขตของคำสั่ง (ไม่ว่าจะเป็นการเชื่อมโยงฟังก์ชั่นหรือตัวควบคุม) เราสามารถเข้าถึงคุณสมบัติเหล่านี้ดังนี้:

/* Directive scope */

in: $scope.text
out: "hello qwe"
// this would automatically update the changes of value in digest
// this is always string as dom attributes values are always strings

in: $scope.twoWayBind
out: {name:"Umur"}
// this would automatically update the changes of value in digest
// changes in this will be reflected in parent scope

// in directive's scope
in: $scope.twoWayBind.name = "John"

//in parent scope
in: $scope.foo.name
out: "John"


in: $scope.oneWayBind() // notice the function call, this binding is read only
out: "qwe"
// any changes here will not reflect in parent, as this only a getter .

คำตอบ "ยังโอเค"

เนื่องจากคำตอบนี้ได้รับการยอมรับ แต่มีปัญหาบางอย่างฉันจะอัปเดตให้ดีขึ้น เห็นได้ชัดว่า$parseเป็นบริการที่ไม่ได้อยู่ในคุณสมบัติของขอบเขตปัจจุบันซึ่งหมายความว่าจะใช้เวลาเพียงการแสดงออกเชิงมุมและไม่สามารถเข้าถึงขอบเขต {{, }}การแสดงออกที่ถูกรวบรวมไว้ในขณะที่ angularjs เริ่มต้นซึ่งหมายความว่าเมื่อเราพยายามเข้าถึงพวกเขาในแนวทางของเราpostlinkวิธีการที่พวกเขาจะรวบรวมแล้ว ( {{1+1}}อยู่2ในคำสั่งแล้ว)

นี่คือวิธีที่คุณต้องการใช้:

var myApp = angular.module('myApp',[]);

myApp.directive('myDirective', function ($parse) {
    return function (scope, element, attr) {
        element.val("value=" + $parse(attr.myDirective)(scope));
    };
});

function MyCtrl($scope) {
    $scope.aaa = 3432;
}​

.

<div ng-controller="MyCtrl">
    <input my-directive="123">
    <input my-directive="1+1">
    <input my-directive="'1+1'">
    <input my-directive="aaa">
</div>​​​​​​​​

สิ่งหนึ่งที่คุณควรสังเกตที่นี่คือถ้าคุณต้องการตั้งค่าสตริงคุณควรตัดมันด้วยเครื่องหมายคำพูด (ดูอินพุต 3)

นี่คือซอที่จะเล่นกับ: http://jsfiddle.net/neuTA/6/

คำตอบเก่า:

ฉันไม่ได้ลบสิ่งนี้ออกไปสำหรับคนที่เข้าใจผิดเหมือนฉันโปรดทราบว่าการใช้งาน$evalนั้นเป็นวิธีที่ถูกต้องดีที่สุด แต่$parseมีพฤติกรรมที่แตกต่างกันคุณอาจไม่จำเป็นต้องใช้มันในกรณีส่วนใหญ่

scope.$evalวิธีที่จะทำมันเป็นอีกครั้งหนึ่งโดยใช้ ไม่เพียง แต่จะรวบรวมนิพจน์เชิงมุมเท่านั้น แต่ยังสามารถเข้าถึงคุณสมบัติของขอบเขตปัจจุบันได้อีกด้วย

var myApp = angular.module('myApp',[]);

myApp.directive('myDirective', function () {
    return function (scope, element, attr) {
        element.val("value = "+ scope.$eval(attr.value));
    }
});

function MyCtrl($scope) {

}​

$evalสิ่งที่คุณจะหายไปถูก

http://docs.angularjs.org/api/ng.$rootScope.Scope#$eval

เรียกใช้งานนิพจน์บนขอบเขตปัจจุบันที่ส่งคืนผลลัพธ์ ข้อยกเว้นใด ๆ ในนิพจน์นั้นเผยแพร่ (ไม่ถูกตรวจจับ) สิ่งนี้มีประโยชน์เมื่อประเมินนิพจน์เชิงมุม


ขอบคุณสำหรับการตอบกลับอย่างไรก็ตามนี่ไม่ใช่วิธีแก้ปัญหา ฉันได้อัพเดทซอด้วยรหัสของคุณแล้ว jsfiddle.net/neuTA/3
Shlomi Schwartz

ใน Chrome ฉันได้รับข้อผิดพลาดนี้เมื่อพยายามใช้ขอบเขต $ parse: Object # <Object> ไม่มีเมธอด '$ parse' ถ้าฉันฉีดบริการ $ parse - ฟังก์ชั่น ($ parse) {ฟังก์ชันคืน (ขอบเขต ... - จากนั้นลอง: "value =" + $ parse (attr.value) - ดูเหมือนจะไม่เหมาะกับฉัน ทั้ง.
ทำเครื่องหมาย Rajcok

@ ทำเครื่องหมายถูกถูกแปลกมันทำงานในตัวอย่างซอ ( jsfiddle.net/neuTA/4 ) แต่ไม่ได้อยู่ในรหัสฉันมี ... รุ่นเชิงมุม?
Shlomi Schwartz

2
ในส่วน "คำตอบที่ดีกว่า" $scope.textจะไม่ถูกกำหนดในฟังก์ชั่นการเชื่อมโยง วิธีที่คำตอบถูกใช้งานในปัจจุบันดูเหมือนว่าจะไม่ได้รับการกำหนด คุณต้องใช้ $ observ () (หรือ $ watch () จะใช้งานได้จริงที่นี่ด้วย) เพื่อดูค่าแบบสอดประสานแบบอะซิงโครนัส ดูคำตอบของฉันและยังstackoverflow.com/questions/14876112/…
ทำเครื่องหมาย Rajcok

1
ในคำตอบ "ยังคงตกลง"ดูเหมือนว่า$parseบริการจะถูกฉีดและไม่ใช้ ฉันพลาดอะไรไปรึเปล่า?
superjos

83

สำหรับค่าคุณลักษณะที่ต้องถูกแก้ไขในคำสั่งที่ไม่ได้ใช้ขอบเขตที่แยกได้เช่น

<input my-directive value="{{1+1}}">

ใช้วิธีการของแอตทริบิวต์$observe:

myApp.directive('myDirective', function () {
  return function (scope, element, attr) {
    attr.$observe('value', function(actual_value) {
      element.val("value = "+ actual_value);
    })
 }
});

จากหน้าคำสั่ง

การสังเกตแอ็ตทริบิวต์แบบสอดแทรก: ใช้$observeเพื่อสังเกตการเปลี่ยนแปลงค่าของแอ็ตทริบิวต์ที่มีการแก้ไข (เช่นsrc="{{bar}}") ไม่เพียงมีประสิทธิภาพมาก แต่ยังเป็นวิธีเดียวที่จะได้รับมูลค่าที่แท้จริงได้อย่างง่ายดายเพราะในระหว่างขั้นตอนการเชื่อมโยงการแก้ไขยังไม่ได้รับการประเมินดังนั้นค่าจึงเป็นค่าในเวลาundefinedนี้

หากค่าคุณลักษณะเป็นเพียงค่าคงที่เช่น

<input my-directive value="123">

คุณสามารถใช้ได้ $ evalถ้าค่าเป็นตัวเลขหรือบูลีนและคุณต้องการประเภทที่ถูกต้อง:

return function (scope, element, attr) {
   var number = scope.$eval(attr.value);
   console.log(number, number + 1);
});

หากค่าแอตทริบิวต์เป็นค่าคงที่สตริงหรือคุณต้องการให้ค่าเป็นชนิดสตริงในคำสั่งของคุณคุณสามารถเข้าถึงได้โดยตรง:

return function (scope, element, attr) {
   var str = attr.value;
   console.log(str, str + " more");
});

ในกรณีของคุณ $observeแต่เนื่องจากคุณต้องการที่จะสนับสนุนค่าและค่าคงที่สอดแทรกการใช้งาน


นี่เป็นทางออกเดียวที่คุณค้นพบ?
Shlomi Schwartz

4
ใช่และเนื่องจากหน้าคำสั่งแนะนำวิธีการนี้นี่คือวิธีที่ฉันจะทำ
Mark Rajcok

7
1 นี้เป็นคำตอบที่ดีที่สุด IMO เพราะมันไม่ได้บังคับให้ขอบเขตในการสั่งและยังครอบคลุมถึงการเปลี่ยนแปลงแอตทริบิวต์ด้วย $ สังเกต
BiAiB

4

คำตอบอื่น ๆ ที่นี่มีความถูกต้องและมีคุณค่ามาก แต่บางครั้งคุณแค่ต้องการง่าย ๆ : เพื่อรับค่าการแจงแบบเก่าแบบธรรมดาในคำสั่งอินสแตนซ์โดยไม่จำเป็นต้องอัพเดตและไม่ยุ่งกับขอบเขตแบบแยก ตัวอย่างเช่นมันจะมีประโยชน์ในการจัดทำ payload ที่ประกาศไว้ในคำสั่งของคุณเป็น array หรือ hash-object ในรูปแบบ:

my-directive-name="['string1', 'string2']"

angular.$eval(attr.attrName)ในกรณีที่คุณสามารถลดการล่าสัตว์และใช้เพียงพื้นฐานที่ดี

element.val("value = "+angular.$eval(attr.value));

การทำงานซอ


ฉันไม่ทราบว่าคุณใช้แองกูลาร์เวอร์ชันเก่าหรือไม่ แต่ตัวอย่างโค้ดทั้งหมดของคุณเป็นจาวาสคริปต์ที่ไม่ถูกต้อง (my-directive-name =) หรือแองกูลาร์ที่ไม่ถูกต้อง (แองกูลาร์ $ eval ไม่มีอยู่) ดังนั้น -1
BiAiB

อืมมม ... เนื่องจากโพสต์นี้มีอายุมากกว่าหนึ่งปีจึงไม่น่าแปลกใจหากมีบางสิ่งที่เลิกใช้ อย่างไรก็ตามการค้นหาของ Google 10 วินาทีจะพบคุณมากมายของวัสดุที่เกี่ยวกับ $ EVAL รวมทั้งขวาที่นี่ที่ SO และตัวอย่างอื่น ๆ ที่คุณอ้างถึงคือการเรียกใช้ใน HTML ไม่ใช่ Javascript
XML

$ scope $ eval (attr.val) ทำงานในเชิงมุม 1.4 ต้องการ $ ขอบเขตที่จะฉีดเข้าไปในฟังก์ชั่นการเชื่อมโยงคำสั่ง
Martin Connell

4

Angularjs directive with ng-Modelสำหรับการแก้ปัญหาเดียวกันผมกำลังมองหา
นี่คือรหัสที่แก้ไขปัญหา

    myApp.directive('zipcodeformatter', function () {
    return {
        restrict: 'A', // only activate on element attribute
        require: '?ngModel', // get a hold of NgModelController
        link: function (scope, element, attrs, ngModel) {

            scope.$watch(attrs.ngModel, function (v) {
                if (v) {
                    console.log('value changed, new value is: ' + v + ' ' + v.length);
                    if (v.length > 5) {
                        var newzip = v.replace("-", '');
                        var str = newzip.substring(0, 5) + '-' + newzip.substring(5, newzip.length);
                        element.val(str);

                    } else {
                        element.val(v);
                    }

                }

            });

        }
    };
});


HTML DOM

<input maxlength="10" zipcodeformatter onkeypress="return isNumberKey(event)" placeholder="Zipcode" type="text" ng-readonly="!checked" name="zipcode" id="postal_code" class="form-control input-sm" ng-model="patient.shippingZipcode" required ng-required="true">


ผลลัพธ์ของฉันคือ:

92108-2223

2
var myApp = angular.module('myApp',[]);

myApp .directive('myDirective', function ($timeout) {
    return function (scope, element, attr) {
        $timeout(function(){
            element.val("value = "+attr.value);
        });

    }
});

function MyCtrl($scope) {

}

ใช้การหมดเวลา $ เนื่องจากการโทรโดยตรงหลังจากโหลด dom เพื่อให้การเปลี่ยนแปลงของคุณไม่สามารถใช้ได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.