Creating a multilevel grid with angular - javascript

I have a requirement where I need to create a multi level grid. Where when user clicks on checkbox a child grid will be opened. Please look at HTML shown below:
<table>
<thead>
<tr>
<th>check</th>
<th>Result</th>
</tr>
</thead>
<tbody>
<tr>
<td><label class="checkbox"><input type="checkbox"><a>test</a></label></td>
<td> Description</td>
</tr>
<tr>
<td colspan="12">
<table>
<tr>
<td><label class="checkbox"><input type="checkbox"><a>testing</a></label></td>
<td>description</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
Something similar as shown here: http://dojo.telerik.com/uQEx.
I have written angular code for it. And I have wrapped tr in *ngfor to show high level. Please look at code shown below:
<tr *ngFor = "let data of values">
<td>
<label class="checkbox">
<input type="checkbox">
</label>
<a (click) = "getTerms(data.id)">{{data.id}}</a>
</td>
<td>{{data.desc}}</td>
</tr>
I need to append another list when I click on a tag that is data.id. I am confused how to append response of sub terms in html. Please let me know if you have any suggestion. Also I don't want to use any plugin for it.
EDIT:
The response I am getting on page load is:
[
{id:1, desc:'test', level: 0},
{id:2, desc:'testing',level: 0}
]
And now when I click on id =1 column I am sending service request:
/get/Term/1
And I am getting response:
[ {id:5, desc: 'test test', level: 1} ]
In the similar way now if I click on id=5 I will get response as:
[ {id:8, desc: 'test test test', level: 2} ]
Hope now its clear.

Tavish the question is: What do you have? What do you want obtain?
...
As you are add items to your data, one idea is that
At first you have
this.data=[
{id:1, desc:'test', level: 0},
{id:2, desc:'testing',level: 0}
]
If click "1" you received
[ {id:5, desc: 'test test', level: 1} ]
It's good that you change your data adding "children". So in getData(data.id,i)
//we pass as argument the index too
getData(id:number,index:number)
{
this.httpClient.get("url"+id).subscribe(res=>{
this.data[index].children=res
}
}
So, your data becomes
this.data=[
{id:1, desc:'test', level: 0,children:
[ {id:5, desc: 'test test', level: 1} ]
},
{id:2, desc:'testing',level: 0}
]
Now you only need to using two *ngFor
<div *ngFor="let level0 of data;let index=i">
{{level0.id}}{{level0.desc}}{{level0.level}}
<button (click)="getData(level0.id,index)">click</button>
<div *ngFor="let level1 of level0.children">
{{level1.id}}{{level1.desc}}{{level1.level}}
</div>
</div>
This idea work if you need fews levels, but if you have more that two or three levels can be a nightmire
With more levels you need, when received the data push in data but add the id_parent
getData(id:number)
{
this.httpClient.get("url"+id).subscribe(res=>{
res.forEach(x=>{
//¡¡IMPORTANT!! not {parent.id,..x}
this.data.push({...x,parent:id})
})
})
}
So, your data becomes
this.data=[
{id:1, desc:'test', level: 0}
{id:2, desc:'testing',level: 0}
{id:5, desc: 'test test', level: 1,parent:1}
]
if you have a function like
listData(parent:number)
{
return (parent)? this.data.filter(x=>x.parent==parent)
: this.data.filer(x=>!x.parent)
}
Your *ngFor can be like
<div *ngFor="let level0 of data;let index=i">
{{level0.id}}{{level0.desc}}{{level0.level}}
<button (click)="getData(level0.id)">click</button>
<div *ngFor="let level1 of listData(level0.id)">
{{level1.id}}{{level1.desc}}{{level1.level}}
</div>
</div>

Example to show a "iterate" component
//app.component
#Component({
selector: 'app-root',
template:`
<app-app-children [children]="data"></app-app-children>
`
})
export class AppComponent {
title = 'app';
isdisabled:boolean=false;
data=[{id:1},
{id:2,
children:[{id:4},{id:5},{id:6,
children:[{id:9},{id:10}]}]},
{id:3,
children:[{id:7},{id:8}]}
];
constructor(){}
}
//app-children
#Component({
selector: 'app-app-children',
template:`
<p>
app-children works!
</p>
<div *ngFor="let item of children">{{item.id}}
<div *ngIf="item.children">
<app-app-children [children]="item.children"></app-app-children>
</div>
</div>`
})
export class AppChildrenComponent {
constructor() { }
#Input() children
}
//If whe can have a table, We can change the template like:
#Component({
selector: 'app-app-children',
template: `
<table>
<thead>
<th>#</th>
</thead>
<tbody>
<tr *ngFor="let item of children">
<td>
<div>
{{item.id}}
</div>
<div *ngIf="item.children">
<app-app-children [children]="item.children"></app-app-children>
</div>
</td>
</table>
`,
styles:[`
table, th, td {
border: 1px solid black;
margin-left:2rem;
}
`
]
})

Related

Alpine Js does not sync data for x-text and x-model, if used in a loop on table row

I am trying to loop over employees array and I want show employee salary and an input field to edit employee salary. The problem is that Alpine does not sync data from input field to the text element.
Example:
<tbody>
<template x-for="employee in employees">
<tr>
<td>
<input x-model="employee.basic_salary">
</td>
<td x-text="employee.basic_salary"></td>
</tr>
</template>
</tbody>
<script>
function data() {
return {
employees: [
{id:1, basic_salary: 6000},
{id:2, basic_salary: 7000},
]
}
}
</script>
template needs to have 1 direct element descendent. So wrapping it's contents in a div should do the trick.
<template x-for="user in users">
<div>
<p x-text="user.name"></p>
<input type="text" x-model="user.name">
</div>
</template>
I believe your markup would've caused alpine to log a warning.
I tried to move the inside the the and it works but It breaks the table, so it does not work as expected:(
Here is what i tried:
<template x-for="employee in employees">
<tbody>
<tr>
<td>
<input x-model="employee.basic_salary">
</td>
<td x-text="employee.basic_salary"></td>
</tr>
</tbody>
</template>
<script>
function data() {
return {
employees: [
{id:1, basic_salary: 6000},
{id:2, basic_salary: 7000},
]
}
}
</script>
One should never put a loop before <tbody>
Follow #Hugos advice.
I had the same issue. I ended up using div set to grid. you can set up the column width using template grid columns

how to count items in nested ng-repeat without $index

I want to count iteration of ng-repeat, when condition match.
I've tried $index but it print for all itration/items in nested ng-repeat
Fiddle link :https://jsfiddle.net/gdr7p1zj/1/
<tbody ng-controller="MainCtrl">
<tr ng-repeat-start="a in test1">
<td>{{a.categoryName}}(count_here)</td>
</tr>
<tr ng-repeat-end ng-repeat="b in test" ng-if="a.categoryId==b.categoryId">
<td>{{b.name}}</td>
</tr>
</tbody>
i want like this
category_one(4) <=item count 4 items in this category so 4 will display
item1
item2
item3
item4
category_two(2)
item5
item6
<!-- this is in controller -->
$scope.test1=[{
categoryId:'1',categoryName:'category one'
},
{
categoryId:'2',categoryName:'category two'
}]
$scope.test = [
{categoryId:'1',name:'cate 1 elem0'},
{categoryId:'1',name:'cate 1 elem1'},
{categoryId:'2',name:'cate 2 elem'}
];
});
An option is to create a function (getCount) in the controller which do the count, something like this:
$scope.getCount = function(categoryId) { // returns the count by matching by categoryId
return $scope.test.filter(function(element) { // first, filter elements in `test`
return element.categoryId === categoryId; // matching by categoryId
}).length; // then, return the count of how many results we got after the filter
}
And in the html call that function like this:
<tbody ng-controller="MainCtrl">
<tr ng-repeat-start="a in test1">
<td>{{a.categoryName }} ({{getCount(a.categoryId)}})</td> <!-- <-- call the function in order to display the count -->
</tr>
<tr ng-repeat-end ng-repeat="b in test" ng-if="a.categoryId == b.categoryId">
<td>{{b.name}}</td>
</tr>
</tbody>
See a demo here: https://jsfiddle.net/lealceldeiro/v9gj1ok4/11/
Thanks for your help. But i get expected output without any functions call or filters
Here fiddle Link: https://jsfiddle.net/wk3nzj96/
htmlCode:
<div ng-app='myapp' >
<div ng-controller="MainCtrl">
<table ng-init="$scope.counter=0">
<tr ng-repeat-start="cate in mainCategory">
<td> {{cate.name}} ({{$scope.counter[$index]}})</td></tr>
<tr ng-repeat="itemsItr in items" ng-init="$scope.counter[$parent.$parent.$index]=$scope.counter[$parent.$parent.$index]+1" ng-if="itemsItr.mid==cate.id">
<td>{{itemsItr.name}}</td>
</tr>
<tr ng-repeat-end ng-if="false"></tr>
</table>
</div>
</div>
and ControllerCode:
(function() {
angular.module('myapp', []).controller('MainCtrl', function($scope) {
$scope.mainCategory = [
{ name: "categoryOne",id:1 },
{ name: "categoryTwo",id:2 }
];
$scope.items = [
{ name: "item1FromCateOne" ,mid:1 },
{ name: "item2FromCateOne",mid:1 },
{ name: "item3FromCateOne" ,mid:1 },
{ name: "item1FromCateTwo",mid:2 }
];
});
Is this Standard way to do this?

Remove a row on ng-click using angularJs

I have a table with the default empty row(Row further includes a columns with different functionalities).Dynamically a multiple rows can be added by click of a button as per the requirement.i want to remove a row when i click a remove-icon from the same-row(remove-icon included in one of the column).
<td class="text-right"> <a href="" class="remove-service" ng-click="vm.removeService(service,true)">
<img src="images/delete.png"></a>  </td>
i have a above column to be included in all rows.whenever i click on remove-icon from any of the rows.(i want to remove a particular row from which icon is clicked)
vm.removeService = function (service, removeService) {
}
Here,is the ng-repeat code for the row.
<tr class="bx--table-row" ng-repeat="service in vm.servicesWithCountries track by $index">
vm.servicesWithCountries = [{ serviceName:"", countries: []}];
Hi you can do like below : Assuming your ng-repeat object will be like somewhat mention below.
<table>
<tr ng-repeat="item in items | orderBy: 'id'">
<td>
<span>
Hello, {{item.name}}!
</span>
</td>
<td>
Delete
</td>
</tr>
</table>
and in Controller :
$scope.items = [{
name: 'item 1',
id: '4'
}, {
name: 'item 2',
id: '3'
}, {
name: 'item 3',
id: '2'
}, {
name: 'item 4',
id: '1'
}];
$scope.delete = function(item) {
$scope.items.splice($scope.items.indexOf(item), 1);
}
refer this fiddle for complete code
You can use splice and indexOf on your service
<tr class="bx--table-row" ng-repeat="service in vm.servicesWithCountries track by $index">
<td class="text-right"> <a href="" class="remove-service"
ng-click="vm.removeService(service, true)">
<img src="images/delete.png"></a>  </td>
</tr>
Like:
ng-click="vm.removeService(service,true)"
JS
vm.removeService = function(service, removeService) {
if(removeService === true){
var index = vm.servicesWithCountries.indexOf(service);
vm.servicesWithCountries.splice(index, 1);
}
}

How to list parent child categories if parent child div is not inside one another?

I want to list categories and sub categories but problem is parent categories div is different and child div is different that is child div is not inside parent div so i am not getting how to list parent - child categories.
This is my code:
<!-- Parent Div Section -->
<div ng-repeat="item in MyData">
<table >
<tr>
<td >{{ item.parentCategoryName }}</td>
</tr>
</table>
</div>
<!-- Child Categories Div Section -->
<div>
<table >
<tr>
<td >{{ item.ChildCategoryName }}</td>
</tr>
</table>
</div>
Json Structure:
[
{
"parentCategoryName": "Electronics",
"SubCategories": [
{
"ChildCategoryName": "Mobile",
},
{
"ChildCategoryName": "TV",
},
{
"ChildCategoryName": "Ac",
}
]
},
{
"parentCategoryName": "Sports",
"SubCategories": [
{
"ChildCategoryName": "Shoes",
},
{
"ChildCategoryName": "Bike",
},
{
"ChildCategoryName": "Bags",
}
]
}
]
Expected output:
Note:Although it seems like both parent child div are same but there are lots of codes which i havent shown for better understanding that is why both div are in different section.So i cant change that because above design will render same output as shown in expected output.
You can use ng-repeat-start and ng-repeat-end. See the code below
<table>
<tr ng-repeat-start="item in MyData">
<td>{{item.parentCategoryName}}</td>
</tr>
<tr ng-repeat-end>
<td> </td>
<td colspan="2">
<table>
<tr ng-repeat="subCategory in item.SubCategories">
<td>{{subCategory.ChildCategoryName}}</td>
</tr>
</table>
</td>
</tr>
</table>
Rest you can alter according you. Hope it helps.

Loop through array in array with Angular

Working on a TODO-app to learn Angular. I have this hardcoded array as a "database" and i want to display the tasks in the view.
var taskLists = [
{name: 'Todays todo', id: '1', tasks:['Make coffe', 'another task']},
{name: 'Tomorrow i will do this', id: '2', tasks:['Code well', 'go to bed']}
];
So how do i iterate through an array inside an array in an angular-view?
I tried to have two ng-repeats but cant get it to work right. I want to display the tasks one by one as <td>, not just the whole tasks array as one <td> as it does right now ['Make coffe', 'another task']
This is what the view looks like.
<h2 ng-repeat="object in list">{{object.name}}</h2>
<table>
<thead>
<tr>Tasks</tr>
</thead>
<tr>
<td ng-repeat="task in list">{{task.tasks}}</td>
</tr>
</table>
You have a problem in your logic.
Fist your HTML tag from child must be inside the parent.
<div class="parent" ng-repeat="object in list">
<h2>{{object.name}}</h2>
<table>
<thead>
<tr>Tasks</tr>
</thead>
<tr ng-repeat="task in object.tasks">
<td>{{item}}</td>
</tr>
</table>
</div>
Try this and check if it works.

Categories