2013-06-05 14 views
13

Tôi đã tạo một chỉ thị với một ràng buộc sử dụng "phạm vi". Trong một số trường hợp, tôi muốn ràng buộc một đối tượng không đổi. Ví dụ, với HTML:AngularJS: cách liên kết một đối tượng cố định với một chỉ thị

<div ng-controller="Ctrl"> 
    <greeting person="{firstName: 'Bob', lastName: 'Jones'}"></greeting> 
</div> 

và JavaScript:

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

app.controller("Ctrl", function($scope) { 

}); 

app.directive("greeting", function() { 
    return { 
     restrict: "E", 
     replace: true, 
     scope: { 
      person: "=" 
     }, 
     template: 
     '<p>Hello {{person.firstName}} {{person.lastName}}</p>' 
    }; 
}); 

Mặc dù công trình này, nó cũng gây ra một lỗi JavaScript:

Error: 10 $digest() iterations reached. Aborting! 

(Fiddle demonstrating the problem)

cách chính xác là gì để liên kết một đối tượng không đổi mà không gây ra lỗi?

Trả lời

10

Đây là giải pháp tôi đã đưa ra, dựa trên câu trả lời @ sh0ber của:

Thực hiện một link chức năng tùy chỉnh. Nếu thuộc tính là JSON hợp lệ, thì đó là một giá trị không đổi, vì vậy chúng tôi chỉ đánh giá nó một lần. Nếu không, hãy xem và cập nhật giá trị như bình thường (nói cách khác, cố gắng hành xử như một ràng buộc =). scope cần phải được đặt thành true để đảm bảo rằng giá trị được gán chỉ ảnh hưởng đến trường hợp này của chỉ thị.

(Example on jsFiddle)

HTML:

<div ng-controller="Ctrl"> 
    <greeting person='{"firstName": "Bob", "lastName": "Jones"}'></greeting> 
    <greeting person="jim"></greeting> 
</div> 

JavaScript:

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

app.controller("Ctrl", function($scope) { 
    $scope.jim = {firstName: 'Jim', lastName: "Bloggs"}; 
}); 

app.directive("greeting", function() { 
    return { 
     restrict: "E", 
     replace: true, 
     scope: true, 
     link: function(scope, elements, attrs) { 
      try { 
       scope.person = JSON.parse(attrs.person); 
      } catch (e) { 
       scope.$watch(function() { 
        return scope.$parent.$eval(attrs.person); 
       }, function(newValue, oldValue) { 
        scope.person = newValue; 
       }); 
      } 
     }, 
     template: '<p>Hello {{person.firstName}} {{person.lastName}}</p>' 
    }; 
}); 
2

Điều này là do nếu bạn sử dụng liên kết trường liên kết phạm vi =, giá trị thuộc tính đang được quan sát cho các thay đổi, nhưng được kiểm tra cho bình đẳng tham chiếu (với !==) thay vì được kiểm tra một cách bình đẳng. Việc chỉ định đối tượng bằng chữ trong dòng sẽ gây ra góc để tạo đối tượng mới bất cứ khi nào thuộc tính được truy cập để nhận giá trị của nó - do đó khi kiểm tra góc bẩn, so sánh giá trị cũ với giá trị hiện tại luôn báo hiệu thay đổi.

Một cách để vượt qua điều đó sẽ thay đổi nguồn góc như mô tả ở đây:

https://github.com/mgonto/angular.js/commit/09d19353a2ba0de8edcf625aa7a21464be830f02.

Nếu không, bạn có thể tạo ra đối tượng của bạn trong bộ điều khiển và tham khảo nó bằng tên trong thuộc tính của phần tử:

HTML

<div ng-controller="Ctrl"> 
    <greeting person="personObj"></greeting> 
</div> 

JS

app.controller("Ctrl", function($scope) 
{ 
    $scope.personObj = { firstName : 'Bob', lastName : 'Jones' }; 
}); 

Tuy nhiên, một cách là tạo đối tượng trong phần tửcủa phần tử gốcchỉ thị và sau đó tham khảo nó theo tên (nhưng điều này ít có thể đọc được):

<div ng-controller="Ctrl" ng-init="personObj = { firstName : 'Bob', lastName : 'Jones' }"> 
    <greeting person="personObj"></greeting> 
</div> 
6

Bạn đang nhận lỗi rằng vì góc đang đánh giá sự biểu hiện mỗi lần. '=' là tên biến.

Dưới đây là hai cách thay thế để đạt được cùng một suy nghĩ mà không có lỗi.

Giải pháp thứ nhất:

app.controller("Ctrl", function($scope) { 
    $scope.person = {firstName: 'Bob', lastName: 'Jones'}; 
}); 

app.directive("greeting", function() { 
    return { 
     restrict: "E", 
     replace: true, 
     scope: { 
      person: "=" 
     }, 
     template: 
     '<p>Hello {{person.firstName}} {{person.lastName}}</p>' 
    }; 
}); 

<greeting person="person"></greeting> 

Giải pháp thứ hai:

app.directive("greeting2", function() { 
    return { 
     restrict: "E", 
     replace: true, 
     scope: { 
      firstName: "@", 
      lastName: "@" 
     }, 
     template: 
     '<p>Hello {{firstName}} {{lastName}}</p>' 
    }; 
}); 

<greeting2 first-name="Bob" last-Name="Jones"></greeting2> 

http://jsfiddle.net/7bNAd/82/

+1

Cảm ơn câu trả lời. Thật không may, giải pháp thứ hai là không thể vì dữ liệu thực tế tôi đang sử dụng được lồng sâu. Trường hợp đầu tiên là có thể nhưng hơi lộn xộn vì có nhiều trường hợp của chỉ thị được sử dụng với các giá trị không đổi (chúng được tạo ra phía máy chủ). –

0

tôi không đặc biệt như sử dụng eval(), nhưng nếu bạn thực sự muốn có được điều này để làm việc với HTML bạn đã cung cấp:

app.directive("greeting", function() { 
    return { 
     restrict: "E", 
     compile: function(element, attrs) { 
      eval("var person = " + attrs.person); 
      var htmlText = '<p>Hello ' + person.firstName + ' ' + person.lastName + '</p>'; 
      element.replaceWith(htmlText); 
     } 
    }; 
}); 
4

Một tùy chọn khác:

app.directive("greeting", function() { 
    return { 
     restrict: "E", 
     link: function(scope,element,attrs){ 
      scope.person = scope.$eval(attrs.person); 
     }, 
     template: '<p>Hello {{person.firstName}} {{person.lastName}}</p>' 
    }; 
}); 
0

tôi đã cùng một vấn đề, tôi giải quyết nó bằng cách phân tích các json theo bước biên dịch:

angular.module('foo', []). 
directive('myDirective', function() { 
    return { 
     scope: { 
      myData: '@' 
     }, 
     controller: function ($scope, $timeout) { 
      $timeout(function() { 
       console.log($scope.myData); 
      }); 
     }, 
     template: "{{myData | json}} a is {{myData.a}} b is {{myData.b}}", 
     compile: function (element, attrs) { 
      attrs['myData'] = angular.fromJson(attrs['myData']); 
     } 
    }; 
}); 

Một nhược điểm là $scope không được cài đặt ban đầu khi bộ điều khiển đầu tiên chạy.

Dưới đây là JSFiddle bằng mã này.