Recursive templating with the KnockoutJs mapping plugin - javascript

I am trying to do recursive templating on a tree using the ko.mapping
plugin, but I can't get to have it rendered, unless I define separate
templates for each level.
In the following case, I want to reuse the mvvmTreeViewGroupTemplate
for mvvmTreeViewSubGroups as well, but this is not being rendered, is
this a bug or not implemented feature?
<div id="treeViewArea">
<ul data-bind='template: {
name: "mvvmTreeViewGroupTemplate",
foreach: MvvmTreeItemGroups,
beforeRemove: function(elem) { $(elem).slideUp() },
afterAdd: function(elem) { $(elem).hide().slideDown() }
}'>
</ul>
</div>
<script type="text/x-jquery-tmpl" id="mvvmTreeViewGroupTemplate"> <li>
<span data-bind="text: Title" class="mvvmTreeItemStyle"/></br/> <ul data-bind='template: {
name: "mvvmTreeViewItemTemplate",
foreach: MvvmTreeItems,
beforeRemove: function(elem) { $ (elem).slideUp() },
afterAdd: function(elem) { $ (elem).hide().slideDown() }
}'>
<ul data-bind='template: {
name: "mvvmTreeViewSubGroupTemplate",
foreach: this.MvvmTreeItemSubGroups,
beforeRemove: function(elem) { $ (elem).slideUp() },
afterAdd: function(elem) { $ (elem).hide().slideDown() }
}'>
</ul>
</ul>
</li>
</script>
<script type="text/x-jquery-tmpl" id="mvvmTreeViewSubGroupTemplate"> <li>
<span data-bind="text: Title" class="mvvmTreeItemStyle"/></br/>
<ul data-bind='template: {
name: "mvvmTreeViewItemTemplate",
foreach: MvvmTreeItems,
beforeRemove: function(elem) { $ (elem).slideUp() },
afterAdd: function(elem) { $ (elem).hide().slideDown() }
}'>
</ul>
</li>
</script>
JSON and script looks like this,
var data = {
MvvmTreeItemGroups: [
{
Id: 1, Title: 'Group 1',
MvvmTreeItemSubGroups: [{
Id: 1, Title: 'Group 11',
MvvmTreeItems: [{ Id: 'i111', Title: 'Item 111' },
{ Id: 'i112', Title: 'Item 112'}]
},
{
Id: 1, Title: 'Group 121',
MvvmTreeItems: [{ Id: 'i121', Title: 'Item
121' }, { Id: 'i122', Title: 'Item 122'}]
}],
MvvmTreeItems: [{ Id: 'i11', Title: 'Item 11' }, { Id:
'i12', Title: 'Item 12'}]
},
{
Id: 2, Title: 'Group 2',
MvvmTreeItemSubGroups: [{
Id: 1, Title: 'Group 211',
MvvmTreeItems: [{ Id: 'i211', Title: 'Item
211' }, { Id: 'i212', Title: 'Item 212'}]
},
{
Id: 1, Title: 'Group 121',
MvvmTreeItems: [{ Id: 'i121', Title: 'Item
121' }, { Id: 'i122', Title: 'Item 122'}]
}],
MvvmTreeItems: [{ Id: 'i21', Title: 'Item 21' },
{ Id: 'i22', Title: 'Item 22'}]
}]
};
var viewModel = ko.mapping.fromJS(data);
console.log(viewModel);
ko.applyBindings(viewModel, treeViewArea);

I got an answer to my question in this google thread. It's not exactly a 'recursive templating' issue, it's because the template doesn't know how to render if there is no array by that name.
There are two ways to fix the issue:
Check if MvvmTreeItemGroups array
actually exists before rendering the
template like so,
{{if $data.MvvmTreeItemGroups }}
<ul data-bind='template: {
name: "mvvmTreeViewGroupTemplate",
foreach: MvvmTreeItemGroups }'>
</ul>
{{/if}}
Use the in keyword to check
MvvmTreeItemGroups really exists
{if 'MvvmTreeItemGroups' in $data}}
<ul data-bind='template: {
name: "mvvmTreeViewGroupTemplate",
foreach: MvvmTreeItemGroups
}'>
</ul>
{{/if}}
The full fiddle is at http://jsfiddle.net/mikekidder/Xs7sy/

Related

filtering items with multiple filters using vue js

I'm learning Vue JS and trying to use multiple filters to filter multiple products.
So, I have an example of 6 filters, product 1 to product 6. And I also have 6 products with different categories. I've coded as below, but when I try to click button filter, to filter the product, it doesn't work. I've tried various ways still not working.
Can anyone help me explain it and deal with this problem?
new Vue({
el: '#app',
data () {
return {
categories:[
{
category_title: 'Product 1',
category_name: "product-1"
},
{
category_title: 'Product 2',
category_name: "product-2"
},
{
category_title: 'Product 3',
category_name: "product-3"
},
{
category_title: 'Product 4',
category_name: "product-4"
},
{
category_title: 'Product 5',
category_name: "product-5"
},
{
category_title: 'Product 6',
category_name: "product-6"
},
],
productItems: [
{
name: 'Pro1',
category: 'product-1'
},
{
name: 'Pro2',
category: 'product-2'
},
{
name: 'Pro3',
category: 'product-3'
},
{
name: 'Pro4',
category: 'product-4'
},
{
name: 'Pro5',
category: 'product-5'
},
{
name: 'Pro6',
category: 'product-6'
},
],
}
},
computed: {
productFilter(){
return this.productItems;
},
filterProduct(){
return this.productItems.filter((product) => product.category == this.categories.category_name)
}
}
})
.product-items span{
margin: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<div id="app">
<div class="product-filter">
<button #click="filterProduct" v-for="cat in categories" :key="cat.id">
{{ cat.category_title }}
</button>
</div>
<div class="product-items">
<span v-for="product in productFilter" :key="product.id">{{ product.name }}</span>
</div>
</div>
Move the filterProduct from the computed properties to methods. Generally actions are to be placed inside methods and the computed properties holds the data which changes with respect to the dependent values used inside the defenition of that particular computed property.
Here I have created a data property called activeCategory which is being set when the user clicks any one of the category button. The productFilter computed property makes use of activeCategory data property. So when ever the data property activeCategory gets updated from the function filterProduct, the productFilter computed property automatically retrns new set of products.
new Vue({
el: '#app',
data() {
return {
categories: [
{
category_title: 'Product 1',
category_name: "product-1"
},
{
category_title: 'Product 2',
category_name: "product-2"
},
{
category_title: 'Product 3',
category_name: "product-3"
},
{
category_title: 'Product 4',
category_name: "product-4"
},
{
category_title: 'Product 5',
category_name: "product-5"
},
{
category_title: 'Product 6',
category_name: "product-6"
},
],
productItems: [
{
name: 'Pro1',
category: 'product-1'
},
{
name: 'Pro2',
category: 'product-2'
},
{
name: 'Pro3',
category: 'product-3'
},
{
name: 'Pro4',
category: 'product-4'
},
{
name: 'Pro5',
category: 'product-5'
},
{
name: 'Pro6',
category: 'product-6'
},
],
activeCategory: null,
}
},
methods: {
filterProduct(category) {
this.activeCategory = category;
}
},
computed: {
productFilter() {
return this.activeCategory ?
this.productItems.filter((product) => product.category == this.activeCategory.category_name) :
this.productItems;
},
}
})
.product-items span {
margin: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w=="
crossorigin="anonymous" referrerpolicy="no-referrer" />
<div id="app">
<div class="product-filter">
<button #click="filterProduct(cat)" v-for="cat in categories" :key="cat.id">
{{ cat.category_title }}
</button>
</div>
<div class="product-items">
<span v-for="product in productFilter" :key="product.id">{{ product.name }}</span>
</div>
</div>
The method filterProduct should be defined inside methods option and add filtered property that will take the filtered products when you click the button, this click button runs the method that takes the current category as parameter :
new Vue({
el: '#app',
data() {
return {
categories: [{
category_title: 'Product 1',
category_name: "product-1"
},
{
category_title: 'Product 2',
category_name: "product-2"
},
{
category_title: 'Product 3',
category_name: "product-3"
},
{
category_title: 'Product 4',
category_name: "product-4"
},
{
category_title: 'Product 5',
category_name: "product-5"
},
{
category_title: 'Product 6',
category_name: "product-6"
},
],
productItems: [{
name: 'Pro1',
category: 'product-1'
},
{
name: 'Pro2',
category: 'product-2'
},
{
name: 'Pro3',
category: 'product-3'
},
{
name: 'Pro4',
category: 'product-4'
},
{
name: 'Pro5',
category: 'product-5'
},
{
name: 'Pro6',
category: 'product-6'
},
],
filtered: []
}
},
computed: {
productFilter() {
return this.productItems;
},
},
methods: {
filterProduct(cat) {
this.filtered = this.productItems.filter((product) => product.category == cat.category_name)
}
}
})
.product-items span {
margin: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous" referrerpolicy="no-referrer"
/>
<div id="app">
<div class="product-filter">
<button #click="filterProduct(cat)" v-for="cat in categories" :key="cat.id">
{{ cat.category_title }}
</button>
</div>
<div class="product-items">
<span v-for="product in filtered" :key="product.id">{{ product.name }}</span>
</div>
<pre>
<pre>
{{filtered}}
</pre>
</div>

filter files based on folder selection in element ui tree with vue

I am using element ui treeview to display folders. There are some files for each folder or their child folder. I have to list out those files based on folder selection. I can filter out those in normal list. But, i am not able to do that using element ui tree view. Please suggest me how to do that for tree node. Here is the sample data:
data: [{
id: 1,
label: 'Level one 1',
type: 'folder',
children: [{
id: 4,
label: 'Level two 1-1',
type: 'folder',
children: [
{ id: 9, label: 'Level three 1-1-1', type: 'file'},
{ id: 10, label: 'Level three 1-1-2', type: 'file' }]
}]
}, {
id: 2,
label: 'Level one 2',
type: 'folder',
children: [
{ id: 5, label: 'Level two 2-1', type: 'file'},
{ id: 6, label: 'Level two 2-2', type: 'file'}]
}, {
id: 3,
label: 'Level one 3',
type: 'folder',
children: [
{ id: 7, label: 'Level two 3-1', type: 'file'},
{ id: 8, label: 'Level two 3-2', type: 'file'}]
}]
Here is my tree's code snippet :
<el-row style="background: #f2f2f2">
<el-col :span="6">
<div class="folder-content">
<el-tree
node-key="id"
:data="data"
accordion
#node-click="nodeclicked"
ref="tree"
style="background: #f2f2f2"
highlight-current
>
<span class="custom-tree-node" slot-scope="{ node, data }">
<span class="icon-folder" v-if="data.type === 'folder'">
<i class="el-icon-folder" aria-hidden="true"></i>
<span class="icon-folder_text" >{{ data.name }}</span>
</span>
</span>
</el-tree>
</div>
</el-col>
<el-col :span="12"><div class="entry-content">
<ul>
<li aria-expanded="false" v-for="(file,index) in files" :key="index">
<span class="folder__list"><input type="checkbox" :id= "file" :value="file">
<i class="el-icon-document" aria-hidden="true"></i>
<span class="folder__name">{{file.name}}</span></span>
</li>
</ul>
</div></el-col>
<el-col :span="6"><div class="preview_content"></div></el-col>
</el-row>
how to list out those files while traversing first folder and it's child node in that tree? Please suggest me on this. I want to display like this below:
If i choose first folder or it's children. Then files associated with this display in a list like 'File Browsing'
When you get the node from the tree you could access the children (node provided by the method doesn't contain any child data), but if you want display the files in a different container and not in the tree you probably search with javascript in the data yourself.
var Main = {
methods: {
nodeclicked(node) {
console.log(this.$refs.tree.getNode(node.id).data.children)
}
},
data() {
return {
data: [{
id: 1,
label: 'Level one 1',
type: 'folder',
children: [{
id: 4,
label: 'Level two 1-1',
type: 'folder',
children: [
{ id: 9, label: 'Level three 1-1-1', type: 'file'},
{ id: 10, label: 'Level three 1-1-2', type: 'file' }]
}]
}, {
id: 2,
label: 'Level one 2',
type: 'folder',
children: [
{ id: 5, label: 'Level two 2-1', type: 'file'},
{ id: 6, label: 'Level two 2-2', type: 'file'}]
}, {
id: 3,
label: 'Level one 3',
type: 'folder',
children: [
{ id: 7, label: 'Level two 3-1', type: 'file'},
{ id: 8, label: 'Level two 3-2', type: 'file'}]
}],
defaultProps: {
children: 'children',
label: 'label'
}
}
}
};
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
#import url("//unpkg.com/element-ui#2.13.2/lib/theme-chalk/index.css");
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui#2.13.2/lib/index.js"></script>
<div id="app">
<el-tree
node-key="id"
:data="data"
:props="defaultProps"
accordion
#node-click="nodeclicked"
ref="tree">
<span class="custom-tree-node" slot-scope="{ node, data }">
<span class="icon-folder">
<i v-if="data.type === 'folder'" class="el-icon-folder"></i>
<i v-else-if="data.type === 'file'" class="el-icon-document"></i>
<span class="icon-folder_text">{{ data.label }}</span>
</span>
</span>
</el-tree>
</div>
var Main = {
data() {
return {
data: [{
id: 1,
name: 'folder 1',
type: 'folder',
children: [{
id: 4,
name: 'subFiles 1-1',
type: 'folder',
children: []
}, {
id: 11,
name: 'files 1-1',
type: 'file'
}, {
id: 12,
name: 'files 1-2',
type: 'file'
}]
},
{
id: 2,
name: 'folder 2',
type: 'folder',
children: []
},
{
id: 3,
name: 'folder 3',
type: 'folder',
children: []
}
]
};
},
methods: {
handleNodeClick() {}
}
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
#import url("//unpkg.com/element-ui#2.13.0/lib/theme-chalk/index.css");
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<script src="//unpkg.com/element-ui#2.13.0/lib/index.js"></script>
<div id="app">
<el-tree :data="data" accordion #node-click="handleNodeClick">
<span class="custom-tree-node" slot-scope="{ node, data }">
<span class="icon-folder">
<i v-if="data.type === 'folder'" class="el-icon-folder" aria-hidden="true"></i>
<i v-else-if="data.type === 'file'" class="el-icon-document" aria-hidden="true"></i>
<span class="icon-folder_text">{{ data.name }}</span>
</span>
</span>
</el-tree>
</div>
ElementUI specifies the data structure of data, so the subtree node are unified push in children property, and then use the type property to distinguish whether it is a folder or a file.

JSTree: move all child nodes

I'm using JSTree, and this is my setup for the contextmenu plugin:
"contextmenu":{
"items": function($node) {
return {
"Remove": {
"separator_before": false,
"separator_after": false,
"label": "Delete group",
"action": function (obj) {
$tree.jstree("get_children_dom", $node).each(function(child){
$tree.jstree("move_node", $tree.jstree("get_node", child, true), "#", "last", function(node, parent, pos){
alert(1);
});
});
$tree.jstree("delete_node", $node);
}
}
};
}
}
basically, I want the children of the group that's being deleted to be moved upwards. The function I've currently got should place the nodes at the end, but how can I place them on the deleted node's place? Also, the current code doesn't work - what am I doing wrong?
Last but not least, how can I check the node type before removing?
Thanks in advance
basically, I want the children of the group that's being deleted to be moved upwards.
If by upwards you mean get into the position of the node that got deleted, check the following example:
var data = [{
'text': 'item 1',
'children': [{
text: 'item 1-1',
children: [{
text: 'item 1-1-1',
children: [{
text: 'item 1-1-1-1'
}, {
text: 'item 1-1-1-2'
}]
}, {
text: 'item 1-1-2'
}, {
text: 'item 1-1-3'
}]
}, {
text: 'item 1-2',
children: [{
text: 'item 1-2-1'
}, {
text: 'item 1-2-2'
}]
}, {
text: 'item 1-3',
children: [{
text: 'item 1-3-1'
}, {
text: 'item 1-3-2'
}]
}, {
text: 'item 1-4',
children: [{
text: 'item 1-4-1'
}, {
text: 'item 1-4-2'
}]
}]
}, {
'text': 'item 2',
children: [{
text: 'item 2-1',
children: [{
text: 'item 2-1-1'
}, {
text: 'item 2-1-2'
}]
}, {
text: 'item 2-2',
children: [{
text: 'item 2-2-1'
}, {
text: 'item 2-2-1'
}]
}, {
text: 'item 2-3'
}]
}, {
'text': 'item 3',
children: [{
text: 'item 3-1',
children: [{
text: 'item 3-1-1'
}, {
text: 'item 3-1-2'
}]
}, {
text: 'item 3-2'
}]
}, {
'text': 'item 4 (you cannot delete this one)',
'disableDelete': true,
children: [{
text: 'item 4-1'
}, {
text: 'item 4-2'
}, {
text: 'item 4-3'
}]
}];
var $tree = $('#jstree_demo').jstree({
'plugins': ['contextmenu'],
'core': {
'animation': 0,
'check_callback': true,
'themes': {
'stripes': true
},
'data': data
},
'contextmenu': {
'items': function($node) {
return {
'Remove': {
'separator_before': false,
'separator_after': false,
'label': 'Delete group',
'action': function(obj) {
if ($node.original.disableDelete) {
document.write('deletion is forbidden for this node');
return;
}
var nodes = $node.children.slice(0); // jstree behaves erratic if we try to move using $node.children directly, so we will clone the array to prevent this issue
var $row = $(obj.reference[0].closest('li'));
$tree.jstree('move_node', nodes, $node.parent, $row.index());
$tree.jstree('delete_node', $node);
}
}
};
}
}
});
<div id="jstree_demo"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/jstree.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css">
Last but not least, how can I check the node type before removing?
I´ve added a small sample to show you how you can accomplish it. Check the declaration of a custom attribute disableDeletion to a node:
var data = [{'text': 'item 4 (you cannot delete this one)', 'disableDelete': true}]
And the validation in the context menu action:
if ($node.original.disableDelete) {
document.write('deletion is forbidden for this node');
return;
}

NG-Repeat to produce a list with dynamic classes

I'm trying to print as an li any item that matches the index of the first loop.
For example: first loop makes li's classed as cat1_li, 2nd one as cat2_li, 3rd one as cat3_li
However the closest I can get prints them all out in separate lists.
Having real trouble getting my head around using ng-repeats inside other ng-repeats and can't seem to find where the issue is. I can see why it is printing multiple lists, but not how to remedy this behaviour.
Would anyone be willing to take a look?
HTML:
<div>
<ul class="tasks" ng-repeat="cat in taskCategories.categories">
{{ cat }}
<li ng-repeat="tasks in tasklist.tasks | orderBy:'category' | filter: {category: cat}" class="cat{{ index + 1 }}_li">
<span>
<span class="panel_title">
{{ tasks.title }}
</span>
</span>
</li>
</ul>
</div
Object:
app.controller('MainController', ['$scope', function($scope) {
$scope.taskCategories = {
categories: [
'work',
'chores',
'learning',
'lifting'
]
};
$scope.tasklist = {
tasks: [{
title: 'Email Gregory',
category: 'work'
}, {
title: 'Clean the Kitchen',
category: 'chores'
}, {
title: 'AngularJS',
category: 'learning'
}, {
title: 'Hose Car',
category: 'chores'
}, {
title: 'Email Jethro',
category: 'work'
}, {
title: '400 lbs',
category: 'lifting'
}
]
};
}]);
You can use $parent.$index if you're trying to use the Category's index inside the nested ng-repeat:
var app = angular.module('app', []);
app.controller('MainController', ['$scope',
function($scope) {
$scope.taskCategories = {
categories: [
'work',
'chores',
'learning',
'lifting'
]
};
$scope.tasklist = {
tasks: [{
title: 'Email Gregory',
category: 'work'
}, {
title: 'Clean the Kitchen',
category: 'chores'
}, {
title: 'AngularJS',
category: 'learning'
}, {
title: 'Hose Car',
category: 'chores'
}, {
title: 'Email Jethro',
category: 'work'
}, {
title: '400 lbs',
category: 'lifting'
}]
};
}
]);
.cat1_li {
background-color: yellow;
}
.cat2_li {
background-color: cyan;
}
.cat3_li {
background-color: pink;
}
.cat4_li {
background-color: lime;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="MainController">
<ul class="tasks" ng-repeat="cat in taskCategories.categories">
{{ cat }}
<li ng-repeat="tasks in tasklist.tasks | orderBy:'category' | filter: {category: cat}" class="cat{{ $parent.$index + 1 }}_li">
<span>
<span class="panel_title">
{{ tasks.title }}
</span>
</span>
</li>
</ul>
</div>
UPDATE
After reading your comment in the question, you don't need two ng-repeat at all:
var app = angular.module('app', []);
app.controller('MainController', ['$scope',
function($scope) {
$scope.taskCategories = {
categories: [
'work',
'chores',
'learning',
'lifting'
]
};
$scope.tasklist = {
tasks: [{
title: 'Email Gregory',
category: 'work'
}, {
title: 'Clean the Kitchen',
category: 'chores'
}, {
title: 'AngularJS',
category: 'learning'
}, {
title: 'Hose Car',
category: 'chores'
}, {
title: 'Email Jethro',
category: 'work'
}, {
title: '400 lbs',
category: 'lifting'
}]
};
$scope.mappedTasks = $scope.tasklist.tasks.map(function(task) {
task.category = $scope.taskCategories.categories.indexOf(task.category);
return task;
}).sort(function(a, b) {
return a.category > b.category;
});;
console.log($scope.mappedTasks);
}
]);
.cat1_li {
background-color: yellow;
}
.cat2_li {
background-color: cyan;
}
.cat3_li {
background-color: pink;
}
.cat4_li {
background-color: lime;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="MainController">
<ul class="tasks">
<li ng-repeat="task in mappedTasks" class="cat{{ task.category + 1 }}_li">
<span>
<span class="panel_title">
{{ task.title }}
</span>
</span>
</li>
</ul>
</div>
Each ng-repeat creates a child scope with the passed data, and also adds an additional $index variable in that scope.
So what you need to do is reach up to the parent scope, and use that $index.
HTML :
<li ng-repeat="tasks in tasklist.tasks | orderBy:'category' | filter: {category: cat}" class="cat{{ $parent.$index + 1 }}_li">

Issues with angular-filter and opening accordion on page load

Since adding angular-filter the accordion is not opening on page load. I guess it's something to do with the code is-open="group.isOpen" and group not being valid in that instance anymore?, cause the code's now using key, value? What am I doing wrong?
Here's a plunker of the issue.
** edit: (( And, as requested in the comments, here's a plunker of the accordion opening on page load before adding angular-filter )).
here's the html:
<div ng-controller="AccordionDemoCtrl">
<accordion close-others="oneAtATime">
<accordion-group is-open="group.isOpen" ng-repeat="(key, value) in groups | groupBy: 'title'">
<accordion-heading><i class="glyphicon-plus"></i> {{ key }}
<i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': group.isOpen, 'glyphicon-chevron-right': !group.isOpen}"></i>
</accordion-heading>
<p ng-repeat="group in value"> {{ group.content }}</p>
<ul>
<li ng-repeat="group in value">
{{ group.list }}
</li>
</ul>
</accordion-group>
</accordion>
</div>
Here's the js:
angular.module('app', ['ui.bootstrap','ngSanitize','angular.filter']);
angular.module('app').controller('AccordionDemoCtrl', function ($scope) {
$scope.oneAtATime = true;
$scope.groups = [
{
title: 'title 1',
content: 'content 1',
isOpen: true,
list: 'item0a'
},
{
title: 'title 2',
content: 'Content 2',
list: 'item0b'
},
{
title: 'title 3',
content: 'Content 3',
list: 'item0c'
},
{
title: 'title 4',
content: 'content 4',
list: 'item0d'
},
{
title: 'title 5',
content: 'content 5',
list: 'item0e'
},
{
title: 'title 1',
list: 'item1a'
},
{
title: 'title 1',
list: 'item2a'
},
{
title: 'title 1',
list: 'item3a'
},
{
title: 'title 2',
list: 'item1b'
},
{
title: 'title 2',
list: 'item2b'
},
{
title: 'title 3',
list: 'item1c'
},
{
title: 'title 3',
list: 'item2c'
},
{
title: 'title 4',
list: 'item1d'
},
{
title: 'title 5',
list: 'item1e'
}
];
});
I don't know if you have a specific reason for your object structure, but something like
{
title: 'title 1',
content: 'content 1',
isOpen: true,
list: ['item1a',
'item2a',
'item3a']
}
makes more sense to me. Now you basically don't need to change the actual view much at all, other than adding the list.
<ul>
<li ng-repeat="item in group.list">
{{ item }}
</li>
</ul>
http://plnkr.co/edit/LBNQ2ukERMEzH3gKQ4M6?p=preview

Categories

Resources