Làm thế nào để có được các thuộc tính được đánh giá bên trong một chỉ thị tùy chỉnh


363

Tôi đang cố gắng để có được một thuộc tính được đánh giá từ chỉ thị tùy chỉnh của mình, nhưng tôi không thể tìm thấy cách làm đúng.

Tôi đã tạo jsFiddle này để giải thích.

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

Tôi đang thiếu gì?


Bạn có thể theo liên kết dưới đây để hiểu rõ hơn về chỉ thị. không xác
định.com 2014/02/11 / Từ

Câu trả lời:


573

Lưu ý: Tôi cập nhật câu trả lời này khi tôi tìm giải pháp tốt hơn. Tôi cũng giữ các câu trả lời cũ để tham khảo trong tương lai miễn là chúng vẫn liên quan. Câu trả lời mới nhất và tốt nhất đến trước.

Câu trả lời tốt hơn:

Các chỉ thị trong angularjs rất mạnh mẽ, nhưng cần có thời gian để hiểu được quá trình nào nằm sau chúng.

Trong khi tạo các lệnh, angularjs cho phép bạn tạo một phạm vi tách biệt với một số ràng buộc với phạm vi cha. Các ràng buộc này được chỉ định bởi thuộc tính bạn đính kèm phần tử trong DOM và cách bạn xác định thuộc tính phạm vi trong đối tượng định nghĩa chỉ thị .

Có 3 loại tùy chọn ràng buộc mà bạn có thể xác định trong phạm vi và bạn viết chúng là thuộc tính liên quan đến tiền tố.

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>

Trong trường hợp đó, trong phạm vi chỉ thị (cho dù đó là trong chức năng liên kết hoặc bộ điều khiển), chúng ta có thể truy cập các thuộc tính như thế này:

/* 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 .

"Vẫn ổn" Trả lời:

Vì câu trả lời này đã được chấp nhận, nhưng có một số vấn đề, tôi sẽ cập nhật nó để tốt hơn. Rõ ràng, $parselà một dịch vụ không nằm trong các thuộc tính của phạm vi hiện tại, có nghĩa là nó chỉ có các biểu thức góc và không thể đạt đến phạm vi. {{, các }}biểu thức được biên dịch trong khi angularjs khởi tạo có nghĩa là khi chúng ta cố gắng truy cập chúng trong postlinkphương thức chỉ thị của chúng , chúng đã được biên dịch. (đã {{1+1}}2trong chỉ thị rồi).

Đây là cách bạn muốn sử dụng:

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

Một điều bạn nên chú ý ở đây là, nếu bạn muốn đặt chuỗi giá trị, bạn nên gói nó trong dấu ngoặc kép. (Xem đầu vào thứ 3)

Đây là câu đố để chơi với: http://jsfiddle.net/neuTA/6/

Câu trả lời cũ:

Tôi sẽ không loại bỏ điều này cho những người có thể bị lừa như tôi, lưu ý rằng việc sử dụng $evallà hoàn toàn tốt để làm điều đó, nhưng $parsecó một hành vi khác, có lẽ bạn sẽ không cần sử dụng nó trong hầu hết các trường hợp.

Cách để làm điều đó là, một lần nữa, sử dụng scope.$eval. Nó không chỉ biên dịch biểu thức góc, nó cũng có quyền truy cập vào các thuộc tính của phạm vi hiện tại.

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

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

function MyCtrl($scope) {

}​

Những gì bạn đang thiếu là $eval.

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

Thực hiện biểu thức trên phạm vi hiện tại trả về kết quả. Bất kỳ trường hợp ngoại lệ trong biểu thức được tuyên truyền (chưa được phát hiện). Điều này rất hữu ích khi đánh giá các biểu thức góc.


Cảm ơn đã trả lời, tuy nhiên đây không phải là giải pháp. Tôi đã cập nhật Fiddle với mã của bạn. jsfiddle.net/neuTA/3
Shlomi Schwartz

Trong Chrome, tôi gặp lỗi này khi cố gắng sử dụng phạm vi. $ Parse: Object # <Object> không có phương thức '$ parse'. Nếu tôi tiêm dịch vụ $ parse - function ($ parse) {hàm return (scope ... - thì hãy thử: "value =" + $ parse (attr.value) - có vẻ như nó không hoạt động với tôi một trong hai.
Mark Rajcok

@Mark bạn nói đúng, lạ là nó hoạt động trong ví dụ fiddle ( jsfiddle.net/neuTA/4 ) nhưng không phải trong mã tôi có ... phiên bản góc cạnh?
Shlomi Schwartz

2
Trong phần "Câu trả lời tốt hơn", $scope.textsẽ không được xác định trong chức năng liên kết. Cách mà câu trả lời hiện đang được diễn đạt, có vẻ như nó sẽ không được xác định. Bạn phải sử dụng $ obs () (hoặc $ watch () cũng thực sự hoạt động ở đây) để xem không đồng bộ giá trị nội suy. Xem câu trả lời của tôi và cả stackoverflow.com/questions/14876112/
Kẻ

1
Trong câu trả lời "Vẫn ổn" , có vẻ như $parsedịch vụ được tiêm và sau đó không bao giờ được sử dụng. Tui bỏ lỡ điều gì vậy?
superjos

83

Đối với một giá trị thuộc tính cần được nội suy trong một lệnh không sử dụng phạm vi bị cô lập, ví dụ:

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

sử dụng phương thức Att Att ' $observe:

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

Từ trang chỉ thị ,

quan sát các thuộc tính được nội suy: Sử dụng $observeđể quan sát sự thay đổi giá trị của các thuộc tính có chứa nội suy (ví dụ src="{{bar}}"). Điều này không chỉ rất hiệu quả mà còn là cách duy nhất để dễ dàng nhận được giá trị thực bởi vì trong giai đoạn liên kết, phép nội suy chưa được đánh giá và vì vậy giá trị này được đặt thành tại thời điểm này undefined.

Nếu giá trị thuộc tính chỉ là hằng số, vd

<input my-directive value="123">

bạn có thể sử dụng $ eval nếu giá trị là số hoặc boolean và bạn muốn loại chính xác:

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

Nếu giá trị thuộc tính là hằng chuỗi hoặc bạn muốn giá trị là loại chuỗi trong lệnh của mình, bạn có thể truy cập trực tiếp vào giá trị:

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

Tuy nhiên, trong trường hợp của bạn, vì bạn muốn hỗ trợ các giá trị và hằng số được nội suy, hãy sử dụng $observe.


đây có phải là giải pháp duy nhất bạn tìm thấy?
Shlomi Schwartz

4
Có, và vì trang chỉ thị đề xuất phương pháp này, đây là cách tôi sẽ thực hiện.
Mark Rajcok

7
+1, đây là câu trả lời tốt nhất IMO vì nó không bắt buộc phạm vi trong chỉ thị và cũng bao gồm các thay đổi thuộc tính với $
obs

4

Các câu trả lời khác ở đây rất nhiều chính xác, và có giá trị. Nhưng đôi khi bạn chỉ muốn đơn giản: để có được một giá trị phân tích cú pháp cũ đơn giản tại khởi tạo chỉ thị, mà không cần cập nhật và không gây rối với phạm vi cô lập. Ví dụ, có thể thuận tiện để cung cấp một tải trọng khai báo vào lệnh của bạn dưới dạng một mảng hoặc đối tượng băm trong biểu mẫu:

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

Trong trường hợp đó, bạn có thể cắt theo đuổi và chỉ cần sử dụng một cơ bản tốt đẹp angular.$eval(attr.attrName).

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

Fiddle làm việc .


Tôi không biết bạn đã sử dụng phiên bản góc cũ hay chưa, nhưng tất cả các mẫu mã của bạn đều là javascript không hợp lệ (my-directive-name =) hoặc angular không hợp lệ (angular. $ Eval không tồn tại), vì vậy -1
BiAiB

Ummm ... cho rằng bài đăng này đã hơn một năm tuổi, sẽ không có gì đáng ngạc nhiên nếu một cái gì đó đã bị từ chối. Tuy nhiên, tìm kiếm Google trong 10 giây sẽ tìm thấy cho bạn nhiều tài liệu về $ eval, bao gồm ngay tại SO . Và ví dụ khác mà bạn trích dẫn là một lời mời trong HTML, không phải Javascript.
XML

$ scope. $ eval (attr.val) hoạt động ở góc 1.4. Yêu cầu phạm vi $ được đưa vào hàm liên kết chỉ thị.
Martin Connell

4

Đối với cùng một giải pháp tôi đang tìm kiếm Angularjs directive with ng-Model.
Đây là mã giải quyết vấn đề.

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


Kết quả của tôi là:

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

}

Sử dụng $ timeout vì lệnh gọi sau khi tải dom nên những thay đổi của bạn không được áp dụng

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.