Render <li> with childs using nested ng-repeat - angularjs

Currently I am trying to render a menu with sub menus. As you guys can see I can have sub menus with sub menus too. I was able to create a JavaScript
This is what I have right now
$scope.menu = [
{
icon: 'glyphicon glyphicon-home', title: 'Home', path: '/', tile: false, fn: null, allowAll: true, childs: []
},
{
icon: 'glyphicon glyphicon-home', title: 'Reporting', path: '', tile: false, fn: null, allowAll: true, childs:
[
{ title: 'Compensation Report', path: '/Reports/Compensation', tile: false, fn: null, allowAll: false, childs: [] },
{ title: 'Representative Level Report', path: '/Reports/AgentId', tile: false, fn: null, allowAll: false, childs: [] },
{ title: 'Territory Report', path: '/Reports/Territory', tile: false, fn: null, allowAll: false, childs: [] },
{ title: 'Reporting Access', path: '/Reports/Acces', tile: false, fn: null, allowAll: false, childs: [] }
]
},
{ icon: 'glyphicon glyphicon-home', title: 'Administrator', path: '', tile: false, fn: null, allowAll: true, childs:
[
{ title: 'Users', path: '', tile: false, fn: null, allowAll: true, childs:
[
{ title: 'Users managment', path: '', tile: false, fn: null, allowAll: true, childs: [] },
{ title: 'Invite user', path: '', tile: false, fn: null, allowAll: true, childs: [] },
{ title: 'Create user account', path: '', tile: false, fn: null, allowAll: true, childs: [] }
]
},
]}];
This is the Javascript I am using to render the menu but it only renders the some menu items so I think I need to render the child sub menus
<nav role="navigation" class="navbar navbar-default navbar-bigfont" style="border-width: 0">
<ul class="nav nav-pills nav-stacked">
<li class="portal-text-19b" ng-repeat="item in menu" ng-class="{active: hightlight === item.title && item.path !== '#'}" ng-show="showItem(item.path, item.allowAll)">
<a href="{{item.path}}" ng-click="item.fn()">
<span class="{{item.icon}}"></span>
{{item.title}}
</a>
</li>
</ul>
</nav>

It's quite simple! Inside your current ng-repeat which is ng-repeat="item in menu", you can have another one of those. Something like,
<li class="portal-text-19b" ng-repeat="item in menu" ...>
<a href="{{item.path}}" ng-click="item.fn()">
<span class="{{item.icon}}"></span>{{item.title}}
</a>
<ul>
<li ng-repeat="child in item.childs">
{{child.title}}
<ul>
<li ng-repeat="child2 in child.childs">
{{child2.title}}
</li>
</ul>
</li>
</ul>
</li>
Now, since this has slightly duplicated HTML, you might want to consider creating a template and use it by having ng-include which would be useful in case you want to make this dynamic till N levels.
working plunker example

Related

How to filter role in submenu inside menu array

I have sidebar-items.ts
export const ROUTES: RouteInfo[] = [
{
path: 'dashboard',
title: 'dashboard',
moduleName: 'dashboard',
role: [Role.COMMUNITY_ADMIN, Role.BOARD_MEMBER,Role.CHAIRMAN,Role.OWNER,Role.TENANT],
submenu: [],
},
{
path: '',
title: 'users',
moduleName: 'users',
role: [Role.COMMUNITY_ADMIN, Role.OWNER, Role.TENANT, Role.BOARD_MEMBER, Role.CHAIRMAN],
submenu: [
{
path: 'persons',
title: 'persons',
moduleName: 'persons',
role: [Role.COMMUNITY_ADMIN, Role.OWNER, Role.TENANT, Role.BOARD_MEMBER, Role.CHAIRMAN],
submenu: [],
},
{
path: 'users',
title: 'users',
moduleName: 'users',
role: [Role.COMMUNITY_ADMIN],
submenu: [],
},
{
path: 'owner/list',
title: 'owner',
moduleName: 'owner',
role: [Role.COMMUNITY_ADMIN, Role.OWNER, Role.TENANT, Role.BOARD_MEMBER, Role.CHAIRMAN],
submenu: [],
}
],
},
{....}]
My user login has role "TENANT".
I want to show menu with only Role.TENANT for this I create a filter like code below. This filter is only for menu, not for submenu. I want a filter for submenu, users's submenu. When I login with account that has role TENANT, in menu doesn't show users/users because users/users has only role: [Role.COMMUNITY_ADMIN].
this.allowedCommunity = 'community/create';
this.currentUserLoggedIn = this.authService.currentUserLoggedInAccount;
const userRolesFiltered = this.currentUserLoggedIn.user?.user_roles?.filter(
(userRole: any) =>
formatDate(userRole.end_date, 'yyyy-MM-dd', 'en_US') >=
formatDate(new Date(), 'yyyy-MM-dd', 'en_US')
);
this.sidebarItems = ROUTES.filter(
(x) =>
userRolesFiltered?.some((userRole: any) =>
x.role.includes(userRole.role.name)
)
)
Any idea please?
There you go. Check my implementation at StackBlitz

Why ngFor with filter pipe cause infinite loop?

i have this situation.
CheckboxesComponent
<section
*ngFor="let group of model.groups | myfilter:term; let index = index">
<div>
<mat-checkbox
[checked]="group.allChecked"
[ngModel]="group.allChecked"
color="primary">
{{ group.name }}
</mat-checkbox>
</div>
<div>
<ul>
<li *ngFor="let checkbox of groups.checkboxes;">
<mat-checkbox
[checked]="checkbox.check"
[(ngModel)]="checkbox.check"
color="primary">
{{ checkbox.displayName }}
</mat-checkbox>
</li>
</ul>
</div>
</section>
In a second component i have
<mat-form-field
appearance="outline">
<mat-label>
Search by
</mat-label>
<input
matInput
type="text"
[(ngModel)]="filter">
<mat-icon matPrefix fontIcon="icon-search"></mat-icon>
</mat-form-field>
<app-checkbox-group
[datasource]="claims"
[term]="filter"
>
</app-checkbox>
and this pipe
#Pipe({
name: 'roleFilter',
pure: false
})
export class myfilter implements PipeTransform {
transform(groups: [], term: string): [] {
if (!term) {
return groups;
}
return groups
.map(obj => {
return Object.assign({}, obj, {
checkboxes: obj.checkboxes.filter(el => el.displayName.includes(term))
})
})
.filter(obj => obj.checkboxes.length > 0)
}
}
and groups is a dataset like this
[
{
name: 'Group 1',
allChecked: false,
isIndeterminate: false,
checkboxes: [
{
check: true,
displayName: 'first',
name: 'first',
id: '1',
isMandatory: false
},
{
check: false,
displayName: 'second',
name: 'second',
id: '2',
isMandatory: false
},
{
check: false,
displayName: 'third',
name: 'third',
id: '3',
isMandatory: false
},
{
check: false,
displayName: 'fourth',
name: 'fourth',
id: '4',
isMandatory: false
},
{
check: false,
displayName: 'fifth',
name: 'fifth',
id: '5',
isMandatory: false
},
{
check: false,
displayName: 'sixth',
name: 'sixth',
id: '6',
isMandatory: false
},
{
check: false,
displayName: 'seventh',
name: 'seventh',
id: '7',
isMandatory: false
},
{
check: false,
displayName: 'eighth',
name: 'eighth',
id: '8',
isMandatory: false
},
]
}
]
When i start typing on the input filter, to reduce the dataset, if i start typing with a letter that not match with any displayName all groups are hidden and it's work like expected.
The problem appears when i start typing with a letter that match with some of the displayName property of the checkboxes.
I don't understand why but all page freeze and the map function is called infinite times.
The problem seem to be in
Object.assign in the map method. Because if i change this line with
obj.checkboxes = checkboxes: obj.checkboxes.filter(el => el.displayName.includes(term)) it works but replace the original checkboxes with the filtered one.
In accordion to #Andrei comment
try to add trackByIndex(idx) {return idx;} method and pass it to both ngFor insatnces like this *ngFor="let checkbox of groups.checkboxes; trackBy: trackByIndex"
The solution was using the trackBy pipe on both *ngFor directives.

how to get conditional submenus using ng-repeat and ui-router in angular?

I am trying to build a menu where only some of the menu items would have submenus. I am using ng-repeat and ui-router. For example, these are a reduced set of main menu items:
vm.routes = [
{ name: 'Home', path: 'home' },
{ name: 'Streams', path: 'streams' },
{ name: 'Overlays', path: 'overlays' },
{ name: 'Admin', path: 'admin' }
];
In the HTML I am using this:
<li class="vic-menu-item"
ng-repeat="route in vm.routes"
<a ui-sref="{{route.path}}">{{route.name}}</a>
</li>
Now I want to add sub-menus to the Admin menu. But other menu items may not have submenus, like Home for example. So its inclusion would have to be conditional on some property in my vm.routes array.
Question: using the Angular way, how do I use conditionals in the ng-repeat so I can build the menu ul tag with optional sub menu items?
I have looked at some of the similar questions but they are confusing to me and none appear to answer this specific technique.
Thanks,
-Andres
First of all you should create array with same structure
vm.routes = [
{ name: 'Home', path: 'home', submenu:[
{name: 'Submenu-1', path: ''},
{name: 'Submenu-2', path: ''}
] },
{ name: 'Streams', path: 'streams' },
{ name: 'Overlays', path: 'overlays' },
{ name: 'Admin', path: 'admin' }
];
After that in html:
<li class="vic-menu-item"
ng-repeat="route in vm.routes"
<a ui-sref="{{route.path}}">{{route.name}}</a>
<ul ng-if='route.submenu'>
<li ng-repeat='submenu in route.submenu'>
<a ui-sref="{{submenu.path}}">{{submenu.name}}</a>
</li>
</ul>
</li>

How do I skip default sorting in ng-repeat?

I am displaying data I get from a json but ng-repeat display it in asc order I want to just display as it is in the json and skip sorting.
Here is my html:
div ng-repeat="(key, data) in List">
<div class="component-title">{{key}}</div>
<div ng-repeat="(key, value) in data">
<div ng-repeat="(key, item) in value">
<div class="row test-label" ng-if="key === 'name'">
<div class="pull-left">
<span class="glyphicon glyphicon-ban-circle"></span>
{{item}}
</div>
<div class="pull-right">
<ul class="pull-right nav nav-pills">
<li><span class="glyphicon glyphicon-pencil"></span></li>
<li>|</li>
<li><span class="glyphicon glyphicon-ban-circle"></span></li>
</ul>
</div>
</div>
</div>
</div>
</div>
My .js file:
(function () {
'use strict';
angular.module('main')
.directive('chTest', function () {
return {
templateUrl: '/templates/chTestDirective.html',
controller: ['$scope', function ($scope) {
$scope.List = {
n: [
{
is_enabled: false,
status: '',
id: 'set',
name: 'set',
result: ''
},
{
is_enabled: false,
status: 'failed',
id: 'list_of_tests',
name: 'list_of_tests',
result: ''
},
{
is_enabled: false,
status: 'failed',
id: 'single_test',
name: 'single_test',
result: ''
},
{
is_enabled: false,
status: 'passed',
id: 'all_tests',
name: 'all_tests',
result: ''
},
{
is_enabled: false,
status: 'passed',
id: 'specific_regex',
name: 'specific_regex',
result: ''
}
],
c: [
{
is_enabled: false,
status: 'running',
id: 'boot-runcommand-delete',
name: 'bootruncommanddelete',
result: ''
},
{
is_enabled: false,
status: 'failed',
id: 'boot_runcommand_delete_custom_image',
name: 'boot_runcommand_delete_custom_image',
result: ''
},
{
is_enabled: false,
status: 'running',
id: 'boot-runcommand-delete-with-disk',
name: 'bootruncommanddeletewithdisk',
result: ''
}
],
ce: [
{
is_enabled: false,
status: '',
id: 'create_delete_node_group_templates',
name: 'create_delete_node_group_templates',
result: ''
},
{
is_enabled: false,
status: 'running',
id: 'create_scale_delete_cluster',
name: 'create_scale_delete_cluster',
result: ''
},
{
is_enabled: false,
status: 'failed',
id: 'create_and_delete_cluster',
name: 'create_and_delete_cluster',
result: ''
},
{
is_enabled: false,
status: 'passed',
id: 'create_and_list_node_group_templates',
name: 'create_and_list_node_group_templates',
result: ''
}
],
k: [
{
is_enabled: false,
status: 'passed',
id: 'boot',
name: 'boot',
result: ''
},
{
is_enabled: false,
status: 'failed',
id: 'list-servers',
name: 'listservers',
result: ''
}
],
neutron: [
{
is_enabled: false,
status: 'running',
id: 'create-and-delete-node',
name: 'createanddeletenode',
result: ''
},
{
is_enabled: false,
status: 'passed',
id: 'create-and-list-node',
name: 'createandlistnode',
result: ''
}
],
g: [
{
is_enabled: false,
status: 'running',
id: 'create-and-delete-image',
name: 'createanddeleteimage',
result: ''
},
{
is_enabled: false,
status: 'passed',
id: 'create-and-list-image',
name: 'createandlistimage',
result: ''
},
{
is_enabled: false,
status: 'failed',
id: 'create-image-and-boot-instances',
name: 'createimageandbootinstances',
result: ''
},
{
is_enabled: false,
status: 'failed',
id: 'list_images',
name: 'list_images',
result: ''
}
],
s: [
{
is_enabled: false,
status: 'passed',
id: 'keystone',
name: 'keystone',
result: ''
},
{
is_enabled: false,
status: '',
id: 'token_validate_cinder',
name: 'token_validate_cinder',
result: ''
},
{
is_enabled: false,
status: '',
id: 'token_validate_neutron',
name: 'token_validate_neutron',
result: ''
},
{
is_enabled: false,
status: 'failed',
id: 'token_validate_heat',
name: 'token_validate_heat',
result: ''
},
{
is_enabled: false,
status: 'passed',
id: 'token_validate_glance',
name: 'token_validate_glance',
result: ''
},
{
is_enabled: false,
status: 'passed',
id: 'token_validate_nova',
name: 'token_validate_nova',
result: ''
}
],
q: [
{
is_enabled: false,
status: 'failed',
id: 'nova-update-and-delete',
name: 'novaupdateanddelete',
result: ''
},
{
is_enabled: false,
status: 'running',
id: 'nova-update',
name: 'novaupdate',
result: ''
},
{
is_enabled: false,
status: 'passed',
id: 'neutron-update',
name: 'neutronupdate',
result: ''
},
{
is_enabled: false,
status: 'passed',
id: 'cinder-update',
name: 'cinderupdate',
result: ''
},
{
is_enabled: false,
status: 'passed',
id: 'cinder-update-and-delete',
name: 'cinderupdateanddelete',
result: ''
}
]
};
}]
};
});
})();
How can be this achieved? Thanks
In short: Upgrade to 1.4
https://docs.angularjs.org/api/ng/directive/ngRepeat
You need to be aware that the JavaScript specification does not define
the order of keys returned for an object. (To mitigate this in Angular
1.3 the ngRepeat directive used to sort the keys alphabetically.)
Version 1.4 removed the alphabetic sorting. We now rely on the order
returned by the browser when running for key in myObj. It seems that
browsers generally follow the strategy of providing keys in the order
in which they were defined, although there are exceptions when keys
are deleted and reinstated. See
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_issues
If this is not desired, the recommended workaround is to convert your
object into an array that is sorted into the order that you prefer
before providing it to ngRepeat. You could do this with a filter such
as toArrayFilter or implement a $watch on the object yourself.

Get zabuto_calendar options

I use plugin zabuto_calendar(https://github.com/zabuto/calendar).
$("#calendar").zabuto_calendar({
language: "ru",
show_previous: true,
show_next: true,
cell_border: true,
today: true,
show_days: true,
weekstartson: 1,
nav_icon: {
prev: '<i class="fa fa-chevron-circle-left"></i>',
next: '<i class="fa fa-chevron-circle-right"></i>'
},
ajax: {
url: "/cabinet/info/show_calendar_data.php",
modal: true
}
});
It's need update calendar when select input changed. I must to add new GET parameter "category", which is select input's value, in ajaxSettings.url.
How can I get $("#calendar") options?

Resources