2013-01-04 47 views
14

Trong mô hình của tôi, tôi có dữ liệu tương tự như:Hiện một danh sách tổng hợp trong angularjs

$scope.list = [{id:0,tags:['tag1','tag2']},{id:2,tags:['tag2']}}; 

Tôi muốn hiển thị một danh sách các thẻ (chứa giá trị độc đáo của 'TAG1' và 'TAG2') với hộp kiểm. Hy vọng rằng một cái gì đó như:

<div ng-repeat="tag in list.tags"> 
    <label class="checkbox"> 
     <input type="checkbox" ng-model="filter.tag" /> 
     {{tag}} 
    </label> 
</div> 

Tôi biết cách lọc danh sách chính dựa trên những gì được kiểm tra nếu tôi mã cứng danh sách, chứ không phải cách tạo danh sách các thẻ duy nhất một cách tự động.

Trả lời

26

Bạn đang tìm kiếm để thực hiện ba hoạt động:

  • Lấy mảng thẻ từ mỗi mục trong $scope.list
  • Flatten này vào một mảng đơn
  • Lấy giá trị duy nhất từ ​​mảng này

Bạn có thể làm điều này với JavaScript thuần túy, nhưng để giúp mọi thứ trở nên dễ dàng hơn, tôi khuyên bạn nên sử dụng Underscore, thư viện cung cấp cho bạn quyền truy cập vào nhiều nội dung thú vị ctions để thao tác và kiểm tra mảng, đối tượng, v.v.

Hãy bắt đầu với mã này:

$scope.list = [ 
    {id: 0, tags: ['tag1', 'tag2']}, 
    {id: 1, tags: ['tag2']}, 
    {id: 2, tags: ['tag1', 'tag3', 'tag4']}, 
    {id: 3, tags: ['tag3', 'tag4']} 
]; 

Bây giờ, chúng ta hãy thực hiện các hoạt động đầu tiên: có được các mảng từ tags sở hữu đối với từng đối tượng trong $scope.list. Dấu gạch dưới cung cấp phương thức pluck, đó chỉ là những gì chúng ta cần.

nhổ_.pluck(list, propertyName)

Một phiên bản thuận tiện về những gì có lẽ là trường hợp sử dụng phổ biến nhất cho bản đồ: chiết xuất một danh sách các giá trị tài sản.

Sử dụng nhổ, chúng ta có thể nhận được như sau:

var tags = _.pluck($scope.list, 'tags'); 
// gives us [['tag1', 'tag2'], ['tag2'], ['tag1', 'tag3', 'tag4'], ['tag3', 'tag4']] 

Bây giờ, chúng tôi muốn flatten mảng đó.

flatten_.flatten(array, [shallow])

flattens một mảng lồng nhau (làm tổ có thể đến bất kỳ độ sâu). Nếu bạn vượt qua nông, mảng sẽ chỉ được san bằng một mức duy nhất.

tags = _.flatten(tags); 
// gives us ['tag1', 'tag2', 'tag2', 'tag1', 'tag3', 'tag4', 'tag3', 'tag4'] 

Cuối cùng, bạn chỉ muốn một phiên bản của mỗi thẻ.

uniq_.uniq(array, [isSorted], [iterator]) Bí danh: unique

Tạo một phiên bản trùng lặp miễn phí của mảng, sử dụng === để kiểm tra sự bình đẳng đối tượng. Nếu bạn biết trước rằng mảng được sắp xếp, chuyển đúng cho isSorted sẽ chạy một thuật toán nhanh hơn nhiều.Nếu bạn muốn tính toán các mục duy nhất dựa trên một phép biến đổi, hãy chuyển một hàm lặp.

tags = _.unique(tags) 
// gives us ['tag1', 'tag2', 'tag3', 'tag4'] 

Chúng ta có thể kết hợp các cùng với chain phương pháp hữu ích gạch để chuỗi này lại với nhau. Hãy tạo một hàm trên phạm vi mà trả về thẻ duy nhất:

$scope.uniqueTags = function() { 
    return _.chain($scope.list) 
    .pluck('tags') 
    .flatten() 
    .unique() 
    .value(); 
}; 

Do đây là một chức năng, nó sẽ luôn luôn trả lại thẻ độc đáo, không có vấn đề nếu chúng ta thêm hoặc loại bỏ các mục trong $scope.list sau khi thực tế.

Bây giờ bạn có thể sử dụng ng-repeat trên uniqueTags để hiển thị mỗi thẻ:

<div ng-repeat="tag in uniqueTags()"> 
    <label class="checkbox"> 
    <input type="checkbox" ng-model="filter.tag" /> 
    {{tag}} 
    </label> 
</div> 

Đây là một jsFiddle làm việc thể hiện kỹ thuật này: http://jsfiddle.net/BinaryMuse/cqTKG/

+0

Hoàn hảo! Và một lời giải thích tuyệt vời là tốt! –

+0

Cảm ơn! Rất vui khi được giúp đỡ.^_^ –

+0

Hiệu suất như thế nào khi sử dụng chức năng tổng hợp như vậy? Bao lâu thì Angular sẽ gọi hàm này? – oldwizard

3

Sử dụng bộ lọc tùy chỉnh để có được một bộ duy nhất/mảng của thẻ, phù hợp để sử dụng với ng-repeat:

.filter('uniqueTags', function() { 
    return function(list) { 
     var tags = {}; 
     angular.forEach(list, function(obj, key) { 
      angular.forEach(obj.tags, function(value) { 
       tags[value] = 1; 
      }) 
     }); 
     var uniqueTags = [] 
     for (var key in tags) { 
      uniqueTags.push(key); 
     } 
     return uniqueTags; 
    } 
}); 

Trước tiên, tôi đặt thẻ vào đối tượng, tự động về mặt tinh tế cho chúng ta sự độc đáo. Sau đó, tôi chuyển đổi nó thành một mảng.

Sử dụng như sau:

<div ng-repeat="tag in list | uniqueTags"> 

Fiddle.


Sau đây có thể không làm những gì tôi nghĩ bạn có thể muốn/mong đợi nó để làm:

<input type="checkbox" ng-model="filter.tag"> 

này không tạo ra $ phạm vi tính filter.tag1filter.tag2 trên phạm vi điều khiển (ví dụ, phạm vi nơi ng-lặp lại được sử dụng). Mỗi lần lặp lại của ng-repeat tạo ra phạm vi con của riêng nó, do đó, mô hình ng ở trên sẽ tạo ra thuộc tính phạm vi filter.tag trên mỗi phạm vi con ng lặp lại, như thể hiện trong fiddle của tôi.

Các vấn đề liên quan