외부 의존 관계가 없는 디렉티브별 AngularJS 그룹
저는 Angular에 처음 와서 문제를 해결하는 가장 좋은 방법을 배우고 싶습니다.제 목표는 헤더별로 그룹을 만드는 재사용 가능한 수단을 확보하는 것입니다.저는 효과적인 솔루션을 만들었는데, 컨트롤러 내에서 스코프 기능이 아니라 지시가 되어야 한다고 생각합니다만, 어떻게 해야 할지, 지시가 올바른 방법인지조차 잘 모르겠습니다.어떤 의견이라도 주시면 감사하겠습니다.
jsFiddle에서 작업 중인 현재 접근 방식 보기
HTML에서는 ng-repeat을 사용한 단순한 목록입니다.여기서 ng-show에서 new Grouping() 함수를 호출합니다.함수는 전체 목록, 그룹화할 필드 및 현재 인덱스에 대한 참조를 전달합니다.
<div ng-app>
<div ng-controller='TestGroupingCtlr'>
<div ng-repeat='item in MyList'>
<div ng-show="newGrouping($parent.MyList, 'GroupByFieldName', $index);">
<h2>{{item.GroupByFieldName}}</h2>
</div>
{{item.whatever}}
</div>
</div>
</div>
컨트롤러에는 new Grouping() 함수가 있습니다.이 함수는 첫 번째 항목을 제외하고 전류를 이전 항목과 단순 비교하고 일치에 따라 true 또는 false를 반환합니다.
function TestGroupingCtlr($scope) {
$scope.MyList = [
{GroupByFieldName:'Group 1', whatever:'abc'},
{GroupByFieldName:'Group 1', whatever:'def'},
{GroupByFieldName:'Group 2', whatever:'ghi'},
{GroupByFieldName:'Group 2', whatever:'jkl'},
{GroupByFieldName:'Group 2', whatever:'mno'}
];
$scope.newGrouping = function(group_list, group_by, index) {
if (index > 0) {
prev = index - 1;
if (group_list[prev][group_by] !== group_list[index][group_by]) {
return true;
} else {
return false;
}
} else {
return true;
}
};
}
출력은 다음과 같습니다.
그룹 1
- abc
- 방어하다
그룹 2
- ghi
- jkl
- 하지 않다
뭔가 더 좋은 방법이 있을 것 같아요.나는 이것을 내가 재사용할 수 있는 공통 유틸리티 기능으로 하고 싶다.이게 지시가 되어야 하나요?리스트의 이전 아이템을 참조할 수 있는 방법이 전체 리스트와 현재 인덱스를 통과하는 방법보다 더 나은 방법이 있을까요?이에 대한 지시는 어떻게 접근해야 할까요?
어떤 조언이라도 감사합니다.
업데이트: 외부 의존 관계가 필요 없는 답변을 찾고 있습니다.언더스코어/로더쉬 또는 앵글필터 모듈을 사용하는 좋은 솔루션이 있습니다.
데릴
이는 Darryl의 솔루션을 수정한 것으로 파라미터별로 여러 그룹을 만들 수 있습니다.또한 $parse를 사용하여 매개 변수별로 그룹화된 중첩 속성을 사용할 수 있습니다.
내포된 다중 모수 사용 예제
HTML
<h1>Multiple Grouping Parameters</h1>
<div ng-repeat="item in MyList | orderBy:'groupfield' | groupBy:['groupfield', 'deep.category']">
<h2 ng-show="item.group_by_CHANGED">{{item.groupfield}} {{item.deep.category}}</h2>
<ul>
<li>{{item.whatever}}</li>
</ul>
</div>
필터(Javascript)
app.filter('groupBy', ['$parse', function ($parse) {
return function (list, group_by) {
var filtered = [];
var prev_item = null;
var group_changed = false;
// this is a new field which is added to each item where we append "_CHANGED"
// to indicate a field change in the list
//was var new_field = group_by + '_CHANGED'; - JB 12/17/2013
var new_field = 'group_by_CHANGED';
// loop through each item in the list
angular.forEach(list, function (item) {
group_changed = false;
// if not the first item
if (prev_item !== null) {
// check if any of the group by field changed
//force group_by into Array
group_by = angular.isArray(group_by) ? group_by : [group_by];
//check each group by parameter
for (var i = 0, len = group_by.length; i < len; i++) {
if ($parse(group_by[i])(prev_item) !== $parse(group_by[i])(item)) {
group_changed = true;
}
}
}// otherwise we have the first item in the list which is new
else {
group_changed = true;
}
// if the group changed, then add a new field to the item
// to indicate this
if (group_changed) {
item[new_field] = true;
} else {
item[new_field] = false;
}
filtered.push(item);
prev_item = item;
});
return filtered;
};
}]);
LoDash/Undercore 또는 기능 라이브러리를 이미 사용하고 있는 경우 _.groupBy()(또는 이와 유사한 이름) 함수를 사용하여 이를 수행할 수 있습니다.
컨트롤러 내:
var movies = [{"movieId":"1","movieName":"Edge of Tomorrow","lang":"English"},
{"movieId":"2","movieName":"X-MEN","lang":"English"},
{"movieId":"3","movieName":"Gabbar Singh 2","lang":"Telugu"},
{"movieId":"4","movieName":"Resu Gurram","lang":"Telugu"}];
$scope.movies = _.groupBy(movies, 'lang');
템플릿 내:
<ul ng-repeat="(lang, langMovs) in movies">{{lang}}
<li ng-repeat="mov in langMovs">{{mov.movieName}}</li>
</ul>
이것에 의해, 다음과 같이 됩니다.
영어
- 엣지 오브 투모로우
- 엑스맨
텔루구
- 가바르 싱 2
- 레수 구람
더 좋은 점은, 이 기능을 매우 쉽게 필터로 변환할 수 있다는 것입니다.특성을 기준으로 요소를 그룹화하기 위한 보일러 플레이트 코드가 많지 않다는 것입니다.
업데이트: 여러 키를 기준으로 그룹화
여러 키를 사용하여 그룹화하는 것이 매우 유용합니다.예, LoDash 사용(소스):
$scope.movies = _.groupBy(movies, function(m) {
return m.lang+ "-" + m.movieName;
});
이 방법을 권장하는 이유 업데이트: 필터 사용ng-repeat/ng-options필터가 빠르게 실행되지 않으면 심각한 성능 문제가 발생합니다.★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★곧게될 될거!!!!!
ng-repeat 내에서 그룹화를 처리하기로 최종 결정한 것은 다음과 같습니다.지시문과 필터에 대해 자세히 읽었는데, 어느 쪽이든 이 문제를 해결할 수 있지만 필터 접근법이 더 나은 선택인 것 같습니다.그 이유는 데이터만 조작하면 되는 상황에 필터가 더 적합하기 때문입니다.디렉티브는 DOM 조작이 필요한 경우에 적합합니다.이 예에서는 DOM을 그대로 두고 데이터를 조작하기만 하면 됩니다.저는 이것이 가장 큰 유연성을 준다고 느꼈습니다.
jsFiddle에서 작업하는 그룹화에 대한 최종 접근 방식을 확인하십시오.데이터를 동적으로 추가할 때 리스트가 어떻게 동작하는지 보여주기 위해 작은 폼도 추가했습니다.
여기 HTML이 있습니다.
<div ng-app="myApp">
<div ng-controller='TestGroupingCtlr'>
<div ng-repeat="item in MyList | orderBy:'groupfield' | groupBy:'groupfield'" >
<h2 ng-show="item.groupfield_CHANGED">{{item.groupfield}}</h2>
<ul>
<li>{{item.whatever}}</li>
</ul>
</div>
<form role="form" ng-submit="AddItem()">
<input type="text" data-ng-model="item.groupfield" placeholder="Group">
<input type="text" data-ng-model="item.whatever" placeholder="Item">
<input class="btn" type="submit" value="Add Item">
</form>
</div>
</div>
여기 Javascript가 있습니다.
var app=angular.module('myApp',[]);
app.controller('TestGroupingCtlr',function($scope) {
$scope.MyList = [
{groupfield: 'Group 1', whatever: 'abc'},
{groupfield: 'Group 1', whatever: 'def'},
{groupfield: 'Group 2', whatever: 'ghi'},
{groupfield: 'Group 2', whatever: 'jkl'},
{groupfield: 'Group 2', whatever: 'mno'}
];
$scope.AddItem = function() {
// add to our js object array
$scope.MyList.push({
groupfield:$scope.item.groupfield,
whatever:$scope.item.whatever
});
};
})
/*
* groupBy
*
* Define when a group break occurs in a list of items
*
* @param {array} the list of items
* @param {String} then name of the field in the item from the list to group by
* @returns {array} the list of items with an added field name named with "_new"
* appended to the group by field name
*
* @example <div ng-repeat="item in MyList | groupBy:'groupfield'" >
* <h2 ng-if="item.groupfield_CHANGED">{{item.groupfield}}</h2>
*
* Typically you'll want to include Angular's orderBy filter first
*/
app.filter('groupBy', function(){
return function(list, group_by) {
var filtered = [];
var prev_item = null;
var group_changed = false;
// this is a new field which is added to each item where we append "_CHANGED"
// to indicate a field change in the list
var new_field = group_by + '_CHANGED';
// loop through each item in the list
angular.forEach(list, function(item) {
group_changed = false;
// if not the first item
if (prev_item !== null) {
// check if the group by field changed
if (prev_item[group_by] !== item[group_by]) {
group_changed = true;
}
// otherwise we have the first item in the list which is new
} else {
group_changed = true;
}
// if the group changed, then add a new field to the item
// to indicate this
if (group_changed) {
item[new_field] = true;
} else {
item[new_field] = false;
}
filtered.push(item);
prev_item = item;
});
return filtered;
};
})
사용하고 있는 어플리케이션에서는, 필터는 앱 전체에서 재사용 가능한 필터로서 설정합니다.
지령적 접근법이 마음에 들지 않았던 것은 HTML이 지령적이어서 재사용이 불가능하다는 것이었습니다.
이전 필터 방식은 마음에 들었지만 목록이 항상 다이제스트 사이클에 두 번 통과해야 하기 때문에 효율적이지 않은 것 같습니다.저는 긴 목록을 다루고 있기 때문에 문제가 될 수 있습니다.또한 이전 항목에 대한 변경 여부를 확인하는 단순한 확인처럼 직관적으로 보이지 않았습니다.또한 이 새로운 필터는 다른 필드 이름으로 필터에 다시 배관하는 것만으로 처리할 수 있는 여러 필드에 대해 필터를 쉽게 사용할 수 있도록 하고 싶었습니다.
my groupBy 필터에 대한 다른 코멘트 - 여러 그룹화가 어레이를 여러 번 통과시키는 것을 알고 있기 때문에 필드별로 여러 그룹의 배열을 받아들이도록 수정하여 어레이를 한 번만 통과하면 됩니다.
조언해 주셔서 감사합니다.Angular의 지침과 필터에 대해 더 많은 것을 배울 수 있었습니다.
건배, 대릴
다음은 지시 기반 솔루션과 이를 시연하는 JSFiddle 링크입니다.디렉티브를 사용하면 각 인스턴스가 그룹화할 항목의 필드 이름을 지정할 수 있으므로 두 개의 다른 필드를 사용하는 예가 있습니다.항목 수에 선형 실행 시간이 있습니다.
<div ng-app='myApp'>
<div ng-controller='TestGroupingCtlr'>
<h1>Grouping by FirstFieldName</h1>
<div group-with-headers to-group="MyList" group-by="FirstFieldName">
</div>
<h1>Grouping by SecondFieldName</h1>
<div group-with-headers to-group="MyList" group-by="SecondFieldName">
</div>
</div>
</div>
angular.module('myApp', []).directive('groupWithHeaders', function() {
return {
template: "<div ng-repeat='(group, items) in groups'>" +
"<h2>{{group}}</h2>" +
"<div ng-repeat='item in items'>" +
"{{item.whatever}}" +
"</div>" +
"</div>",
scope: true,
link: function(scope, element, attrs) {
var to_group = scope.$eval(attrs.toGroup);
scope.groups = {};
for (var i = 0; i < to_group.length; i++) {
var group = to_group[i][attrs.groupBy];
if (group) {
if (scope.groups[group]) {
scope.groups[group].push(to_group[i]);
} else {
scope.groups[group] = [to_group[i]];
}
}
}
}
};
});
function TestGroupingCtlr($scope) {
$scope.MyList = [
{FirstFieldName:'Group 1', SecondFieldName:'Group a', whatever:'abc'},
{FirstFieldName:'Group 1', SecondFieldName:'Group b', whatever:'def'},
{FirstFieldName:'Group 2', SecondFieldName:'Group c', whatever:'ghi'},
{FirstFieldName:'Group 2', SecondFieldName:'Group a', whatever:'jkl'},
{FirstFieldName:'Group 2', SecondFieldName:'Group b', whatever:'mno'}
];
}
AngularJS에는 정보 그룹을 표시하는 데 도움이 되는 세 가지 지침이 있습니다.이러한 지시사항은 ngRepeat, ngRepeatStart 및 ngRepeatEnd입니다.나는 AngularJS에서 그룹을 보여주는 블로그 게시물을 발견했다.요점은 다음과 같습니다.
<body ng-controller="OrdersCtrl">
<div ng-repeat-start="customer in customers" class="header">{{customer.name}}</div>
<div ng-repeat="order in customer.orders">{{order.total}} - {{order.description}}</div>
<div ng-repeat-end><br /></div>
</body>
사용법을 익히면 매우 강력한 지침입니다.
JoshMB의 코드는 동일한 뷰의 동일한 데이터 세트에 여러 필터가 있는 경우 올바르게 작동하지 않습니다.두 번째로 데이터 세트의 필터링된 버전을 그룹화할 때 원래 개체에서 동일한 속성이 변경되므로 이전에 필터링된 버전에서 그룹 구분이 해제됩니다.
추가 필터 파라미터로 CHANGED 속성의 이름을 추가함으로써 이 문제를 해결했습니다.다음은 제가 업데이트한 코드 버전입니다.
/*
* groupBy
*
* Define when a group break occurs in a list of items
*
* @param {array} the list of items
* @param {String} then name of the field in the item from the list to group by
* @param {String} then name boolean attribute that indicated the group changed for this filtered version of the set
* @returns {array} the list of items with an added field name named with "_new"
* appended to the group by field name
*
* @example <div ng-repeat="item in MyList | filter:'a' | groupBy:'groupfield':'Agroup_CHANGED'" >
* <h2 ng-if="item.Agroupfield_CHANGED">{{item.groupfield}}</h2>
* <!-- now a differen filtered subset -->
* <div ng-repeat="item in MyList | filter:'b' | groupBy:'groupfield':'Bgroup_CHANGED'" >
* <h2 ng-if="item.Bgroupfield_CHANGED">{{item.groupfield}}</h2>
*
* Typically you'll want to include Angular's orderBy filter first
*/
app.filter('groupBy', ['$parse', function ($parse) {
return function (list, group_by, group_changed_attr) {
var filtered = [];
var prev_item = null;
var group_changed = false;
// this is a new field which is added to each item where we append "_CHANGED"
// to indicate a field change in the list
//var new_field = group_by + '_CHANGED'; //- JB 12/17/2013
var new_field = 'group_by_CHANGED';
if(group_changed_attr != undefined) new_field = group_changed_attr; // we need this of we want to group different filtered versions of the same set of objects !
// loop through each item in the list
angular.forEach(list, function (item) {
group_changed = false;
// if not the first item
if (prev_item !== null) {
// check if any of the group by field changed
//force group_by into Array
group_by = angular.isArray(group_by) ? group_by : [group_by];
//check each group by parameter
for (var i = 0, len = group_by.length; i < len; i++) {
if ($parse(group_by[i])(prev_item) !== $parse(group_by[i])(item)) {
group_changed = true;
}
}
}// otherwise we have the first item in the list which is new
else {
group_changed = true;
}
// if the group changed, then add a new field to the item
// to indicate this
if (group_changed) {
item[new_field] = true;
} else {
item[new_field] = false;
}
filtered.push(item);
prev_item = item;
});
return filtered;
};
}]);
편집: 커스텀 필터 어프로치를 다음에 나타냅니다. Groups는 현재 목록에서 그룹 배열을 생성하기 위해 범위 내의 필터 함수에 의해 작성됩니다.목록 항목을 추가/삭제하면 그룹 배열이 다이제스트 주기마다 재설정되므로 업데이트가 바인딩됩니다.
HTML
<div ng-app="myApp">
<div ng-controller='TestGroupingCtlr'>
<div ng-repeat='group in getGroups()'>
<h2>{{group}}</h2>
<ul>
<!-- could use another scope variable as predicate -->
<li ng-repeat="item in MyList | groupby:group">{{item.whatever}}</li>
</ul>
</div>
</div>
</div>
JS
var app=angular.module('myApp',[]);
app.filter('groupby', function(){
return function(items,group){
return items.filter(function(element, index, array) {
return element.GroupByFieldName==group;
});
}
})
app.controller('TestGroupingCtlr',function($scope) {
$scope.MyList = [{ GroupByFieldName: 'Group 1', whatever: 'abc'},
{GroupByFieldName: 'Group 1',whatever: 'def'},
{GroupByFieldName: 'Group 2',whatever: 'ghi' },
{GroupByFieldName: 'Group 2',whatever: 'jkl'},
{GroupByFieldName: 'Group 2',whatever: 'mno' }
];
$scope.getGroups = function () {
var groupArray = [];
angular.forEach($scope.MyList, function (item, idx) {
if (groupArray.indexOf(item.GroupByFieldName) == -1)
groupArray.push(item.GroupByFieldName)
});
return groupArray.sort();
}
})
http://blog.csdn.net/violet_day/article/details/17023219#t2
<!doctype html>
<html ng-app>
<head>
<script src="lib/angular/angular.min.js"></script>
<script>
function TestCtrl($scope) {
$scope.items = [
{ id: 0, name: "Red"},
{ id: 1, name: "Red"},
{ id: 2, name: "Red"},
{ id: 3, name: "Red"},
{ id: 4, name: "Yellow"},
{ id: 5, name: "Orange"}
];
}
</script>
</head>
<body ng-controller="TestCtrl">
<ul ng-repeat="a in items" ng-if="a.name!=items[$index-1].name">
{{ a.name }}
<li ng-repeat="b in items" ng-if="a.name==b.name">
{{ b.id }}
</li>
</ul>
</body>
</html>
try this:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-example58-production</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.5/angular.min.js"></script>
</head>
<body ng-app="">
<script>
function Ctrl($scope) {
$scope.friends =
[{name:'John', phone:'555-1212', age:10},
{name:'Mary', phone:'555-9876', age:19},
{name:'Mike', phone:'555-4321', age:21},
{name:'Adam', phone:'555-5678', age:35},
{name:'John', phone:'555-1212', age:10},
{name:'John', phone:'555-1212', age:10},
{name:'John', phone:'555-1212', age:10},
{name:'John', phone:'555-1212', age:10},
{name:'Julie', phone:'555-8765', age:29},
{name:'Mike', phone:'555-4321', age:21},
{name:'Adam', phone:'555-5678', age:35},
{name:'Mike', phone:'555-4321', age:21},
{name:'Adam', phone:'555-5678', age:35},
{name:'Mike', phone:'555-4321', age:21},
{name:'Adam', phone:'555-5678', age:35}]
}
</script>
<div ng-controller="Ctrl">
<div ng-init="friendx=(friends|orderBy:'age')"> </div>
<table class="friend" ng-repeat="friend in friendx">
<tr>
<td ng-if="friendx[$index].age!=friendx[$index-1].age">{{friend.age}}</td>
</tr>
<tr>
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
<td>{{friend.age==friendx[$index].age}}</td>
</tr>
</table>
</div>
</body>enter code here
</html>
[http://plnkr.co/edit/UhqKwLx1yo2ua44HjY59?p=preview][1]
[1]: http://plnkr.co/edit/UhqKwLx1yo2ua44HjY59?p=preview
언급URL : https://stackoverflow.com/questions/19992090/angularjs-group-by-directive-without-external-dependencies
'source' 카테고리의 다른 글
| React의 재료 UI에서 스크롤 가능한 목록 구성 요소 (0) | 2023.03.29 |
|---|---|
| 순환 의존관계 발견: $http <- $templateFactory <- $view <- $state (0) | 2023.03.29 |
| 블록 범위 변수를 다시 닫을 수 없습니다. (0) | 2023.03.29 |
| 리액트를 사용하려고 합니다.바디 스타일을 설정하는 DOM (0) | 2023.03.29 |
| 메뉴 항목 및 페이지 목록에 대한 li 클래스 & ID 삭제 (0) | 2023.03.29 |