Requirement:
Showing dynamic data using ngx-datatable and use paging using page id
Description :
I have a dynamic data where I am displaying it using the ngx-datatable in angular and here everything works but the issue I m not sure how to apply the paging using the page_id (sent to the server using post body). Here I am getting the page_id along with the API response this is 1st API call. here page_id has to be sent as a body for the very same API for getting the rest of results.
Ex: Suppose I have 20 results in the 1ST API call I will get the 10 records and a page id by using the page id how can I get the next 10 results
What I implemented:
Getting data and displaying it in table basic paging applied
Below is my code :
Result=[];
reorderable: boolean = true;
selected = [];
rows = [];
columns = [];
DataArray = [];
Results = {
"data": [
{
"_score": 0.36464313,
"_type": "data",
"_id": "abcd",
"_source": {
"filter": "data",
"information": {
"product_id": "abcd",
"creation_utctime": "1477335693653"
},
"enduser": "free"
},
"_index": "dell_laptop"
},
{
"_score": 0.36464314,
"_type": "data",
"_id": "abcde",
"_source": {
"filter": "data",
"information": {
"product_id": "abcde",
"creation_utctime": "1477335693653"
},
"enduser": "free"
},
"_index": "lenovo_laptop"
},
{
"_score": 0.36464314,
"_type": "data",
"_id": "abcdef",
"_source": {
"filter": "data",
"information": {
"product_id": "abcde",
"creation_utctime": "1477335693653"
},
"enduser": "free"
},
"_index": "lenovo_laptop"
}
],
"total": 4,
"page_id": "WpNdVJMMjlJVnJTYTFuUklB"
}
LoadInfo(){
this.DataArray = ["filter","information.product_id","information.creation_utctime","enduser"];
this.rows=[];
this.Result = this.Results['data'];
// tslint:disable-next-line: forin
for (var res in this.Result) {
var row = {};
for (var key in this.Result[res]['_source']) {
if (typeof this.Result[res]['_source'][key] === 'object') {
for (var k in this.Result[res]['_source'][key]) {
let temp = key + "." + k;
row[temp] = this.Result[res]['_source'][key][k];
}
} else {
row[key] = this.Result[res]['_source'][key]
}
row['_id'] = this.Result[res]['_id'];
}
this.rows.push(row);
}
console.log(this.rows);
}
onActivate(event) {
// console.log('Activate Event', event);
}
onSelect({ selected }) {
// console.log('Select Event', selected, this.selected);
this.selected.splice(0, this.selected.length);
this.selected.push(...selected);
}
HTML Code:
<button type="button" (click)="LoadInfo()">LoadData</button>
<div>
<ngx-datatable class="material ui" [rows]="rows" [columnMode]="'force'" [headerHeight]="50"
[footerHeight]="50" [limit]="2" [rowHeight]="'auto'" [reorderable]="reorderable" [selected]="selected"
[selectionType]="'checkbox'" [scrollbarH]="true" [sortType]="'multi'" (activate)="onActivate($event)"
(select)='onSelect($event)'>
<ngx-datatable-column [width]="30" [sortable]="true" [canAutoResize]="false" [draggable]="false"
[resizeable]="false" class="uih">
<ng-template ngx-datatable-header-template let-value="value" let-allRowsSelected="allRowsSelected"
let-selectFn="selectFn">
<input type="checkbox" [checked]="allRowsSelected" (change)="selectFn(!allRowsSelected)" />
</ng-template>
<ng-template ngx-datatable-cell-template let-value="value" let-isSelected="isSelected"
let-onCheckboxChangeFn="onCheckboxChangeFn">
<input type="checkbox" [checked]="isSelected" (change)="onCheckboxChangeFn($event)" />
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column *ngFor="let attr of DataArray" [sortable]="true" prop={{attr}} name={{attr}}>
</ngx-datatable-column>
</ngx-datatable>
</div>
Stackblitz link: https://stackblitz.com/edit/angular-secw8u
Note: even though if there is pageid also some times after 10 records there may not be more records also
here api call is simple post request
api : https://xxxx.xxxx..com/<some method>
body: { "key1":"data1","key2":"data2","pageid":"ss"}
here in the first api call we wont send page id as after calling the first api call we will get response in that we will get the pageid and for the second api call i mean for paging then we have to use the pageid
For pagination you need to know total number of pages. Otherwise you need total number
of items along with number of items per page (to derive total number of pages). In your case
you only have a page-id which does not even say the which page you are on. The page-id
only gives you access to next page items.
This API is useful if you implement an infinite scroll feature. Otherwise you can only
implement a more button to which loads new items to table. The link
you provided in comments implements this more button feature.
So you can override the default footer of ngx-datatable to add your more button to load
more items to table.
<ngx-datatable-footer>
<ng-template ngx-datatable-footer-template
let-rowCount="rowCount"
let-pageSize="pageSize"
let-selectedCount="selectedCount"
let-curPage="curPage"
let-offset="offset"
let-isVisible="isVisible">
<div class="datatable-footer-inner selected-count">
<div class="page-count" *ngIf="selectedCount">
<span> {{selectedCount}} selected / </span> {{rowCount}} total
</div>
<div class="datatable-pager">
<ul class="pager">
<li class="pages active" role="button" aria-label="next" *ngIf="rowCount">
Next
</li>
</ul>
</div>
</div>
</ng-template>
</ngx-datatable-footer>
Stackblitz Demo
Related
I need your help about Mat Tree with dynamic Data and lazy loading.
I have found this example here from Angular https://stackblitz.com/run?file=src%2Fapp%2Ftree-dynamic-example.ts
But I have a different structure of data.
I can see the parent but unable to load the children.
The parent it is an Object itself like this.
{
"description":"Test Purpose",
"childDevices":8,
"childLocations":8,
"name":"Test",
"id":"1234556788www2"
}
Then to load more information or children for this I need to create an Get request which gives me back like this.
{
"id": "1234556788www2",
"name": "Test",
"description": "Test Purpose",
"parent": null,
"children": [
"1st Child ID",
"2nd Child ID"
],
"groupsV2": [
"group ID"
],
"usersV2": [
"userID"
],
"tenantId": "Tenant ID",
"devices": [
"device1 ID",
"device2 ID",
"device3 ID",
"device4 ID"
]
}
Now to get its children I need to iterate again through API Get so I can get information about each children and then to show on tree under the parent. I have spent a lot of hours and did not find how to show the children into the tree.
This is the JSON for each children.
{
"id": "1st Child ID",
"name": "TestLocation",
"description": "333",
"parent": "1234556788www2",
"children": [],
"groupsV2": [
],
"usersV2": [
],
"tenantId": "",
"devices": []
}
This is my HTML.
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl" data-cy="locationsMatTree">
<mat-tree-node>
<mat-nested-tree-node
*matTreeNodeDef="let node; when: hasChild"
[class.hidden]="!node.visible"
[class.fe-action]="node.overallFEChildCount > 0"
[class.be-action]="node.overallFEChildCount < node.overallChildCount">
<div class="title mat-tree-node on-hover">
<button mat-icon-button matTreeNodeToggle *ngIf="node.childLocations > 0; else noLocations">
<mat-icon class="mat-icon-rtl-mirror" (click)="loadChildren(node)">
{{ treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}
</mat-icon>
</button>
<ng-template #noLocations>
<button mat-icon-button>
<mat-icon class="no-sub-icon">
<span class="leaf-icon"> • </span>
</mat-icon>
</button>
</ng-template>
<div
class="location-name hide-in-percy"
(click)="loadDevices(node)"
[routerLink]="['/location/details', node.id]"
[id]="'created-' + node.name">
{{ node.name }}
</div>
<icon-button
color="primary"
*ngIf="!isDisabled"
iconName="add"
type="mat-icon-button"
class="action-button action-button-add"
(click)="onNewClicked(node)">
</icon-button>
<icon-button
color="primary"
type="mat-icon-button"
iconName="edit"
class="action-button action-button-edit"
(click)="onEditClicked(node)">
</icon-button>
<icon-button
color="warn"
type="mat-icon-button"
iconName="delete"
class="action-button action-button-delete"
[disabled]="node.action?.generated"
[matTooltipDisabled]="!node.action?.generated"
matTooltip="{{ 'RIGHT_ACTIONS_DISABLED_LEAF' | translate }}"
(click)="deleteLocation(node)">
</icon-button>
</div>
<ul [class.tree-invisible]="!treeControl.isExpanded(node)">
<ng-container matTreeNodeOutlet></ng-container>
</ul>
<mat-progress-bar *ngIf="node.isLoading" mode="indeterminate" class="example-tree-progress-bar"></mat-progress-bar>
</mat-nested-tree-node>
</mat-tree-node>
</mat-tree>
TS Code.
private readonly subscription: Subscription = new Subscription();
dataSource = new MatTreeNestedDataSource<Location>();
hasChild = (_: number, node: Location) => !!node.childLocations && node.childLocations > 0;
this.locationService
.getLocationsForUser(this.pageIndex, this.pageSize)
.pipe(take(1))
.subscribe((response) => {
this.dataSource.data = response.body.content; // here I load all parents
this.flatLocations = this.getFlatLocations();
this.length = response.body.totalElements;
if (response.body.content && response.body.content.length > 0) {
this.location = response.body.content[0];
this.loadDevices(this.location);
}
this.subscribeToRouteParams();
});
}
loadChildren = (location: Location): any => {
this.locationService.getLocation(location.id).subscribe((res) => {
for (let i = 0; i < res.body.children.length; i++) {
this.locationService.getLocation(res.body.children[i]).subscribe((t) => {
return of(t.body); //Here I can see each child data
});
}
});
return false;
};
Your problem is currently that you are nesting subscriptions in your loadChildren function. You probably want to use switchMap and forkJoin to make the calls to this.locationService.getLocation() in parallel and output the response of each call in an array:
loadChildren = (location: Location): any => {
this.locationService.getLocation(location.id).pipe(
switchMap(res => forkJoin(
res.body.children.map(child => this.locationService.getLocation(child))
)
)
).subscribe( resultArray => {
location.childLocations = resultArray;
});
}
I'm looping through an API response and displaying some items (female and male age groups) on a page with the following code:
<ng-container *ngFor="let event of day.availableEvents">
{{ event.name }}
<br>
<ng-container *ngFor="let maleAgeGroup of event.maleAgeGroups">
<span [class.is-active]="status" (click)="updateStatus()" {{ maleAgeGroup }}</span>
</ng-container>
</ng-container>
And, this the JSON data I'm working with:
"days": [
{
"date": "2021-04-14T00:00:00.000Z",
"availableEvents": [
{
"id": 1,
"name": "Event A",
"femaleAgeGroups": ["U13", "U15", "U17"],
"maleAgeGroups": ["U13", "U15", "U17"]
},
{
"id": 2,
"name": "Event B",
"femaleAgeGroups": ["U13", "U15", "U17"],
"maleAgeGroups": ["U13", "U15", "U17"]
},
]
}
]
On click of an age group I want to toggle its status from true or false so that the is-active CSS class is add/removed.
No matter what SO answer I try, I can't get it to work and in most cases end up with an error like 'Cannot assign status to type string'. This is why I've stripped back the above code right to basics - maybe it's because of the way my data is structured?
The example below is what I'm trying to end up with, where the highlighted text has been clicked and is-active class applied:
[class.is-active] needs to 'bind' to a value, it is unclear to me the intent of what you trying to do as a whole, but, each age group needs a state. Thus, you need to update/modify your data either from the server or augment in your component with the status for each age group...
"femaleAgeGroups": [{name:"U13", active:true}, {name:"U15", active:false}, {name:"U17", active:false}], etc..
Then in the template you can bind the css class to the boolean value
<ng-container *ngFor="let maleAgeGroup of event.maleAgeGroups">
<span [class.is-active]="maleAgeGroup.active" (click)="updateStatus(maleAgeGroup)" {{ maleAgeGroup.name }}</span>
</ng-container>
.. and to update you pass the item to update function and update its active state:
updateStatus(item:any): void {
item.active = !item.active;
}
Right now I have a table that is currently showing all the entries form an "events" node in firebase.
However, I only want to show the events created by the logged in user. Right now they are showing events created by all users.
I'm guessing I might be able to use an ng-if directive after the ng-repeat in the tag, however, I am not sure what to put into it.
This is my table:
<table class="table table-striped">
<thead>
<tr>
<th>Title</th><th>Location</th> <th>Actions</th>
</tr>
</thead>
<tbody>
<tr scope="row" ng-repeat="event in events | reverse" >
<td>{{event.title}}</td>
<td>{{event.location}}</td>
<td><button class="btn btn-primary" ng-click="events.$remove(event)"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></button></td>
</tr>
</tbody>
The user object looks like so:
{
"provider": "password",
"uid": "635gt3t5-56fe-400d-b50b-1a6736f8874a",
"token":
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6Im1pY2hhZWwubGFyaXZpZXJlMTk3M0BnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImlhdCI6MTQ2OTEyNTYyOSwidiI6MCwiZCI6eyJwcm92aWRlciI6InBhc3N3b3JkIiwidWlkIjoiNmY5ZmM0NTUtNTZmZS00MDBkLWI1MGItMWE2NzM2Zjg4NzRhIn19.yIzzV7Or7tUlXi-sSWeioNx6LLoQ0U9qnW1X06rpSmA",
"password": {
"email": "xxxxxx.xxxxxx1234#gmail.com",
"isTemporaryPassword": false,
"profileImageURL": "https://secure.gravatar.com/avatar/5f9effbf8cbea69792c595079cf25d38?d=retro"
},
"auth": {
"provider": "password",
"uid": "635gt3t5-56fe-400d-b50b-1a6736f8874a",
"token": {
"email_verified": false,
"email": "xxxxxx.xxxxxx1234#gmail.com",
"exp": 1469212029,
"iat": 1469125629,
"sub": "635gt3t5-56fe-400d-b50b-1a6736f8874a",
"auth_time": 1469125629,
"firebase": {
"identities": {
"email": [
"xxxxxx.xxxxxx1234#gmail.com"
]
}
}
}
},
"expires": 1469212029
}
My controller looks like this:
angular.module('myApp').controller('ChatCtrl', function($scope, user,
Ref, $firebaseArray, $timeout) {
console.dir('user: ' + JSON.stringify(user));
// synchronize a read-only, synchronized array of events, limit to most recent 10
$scope.events = $firebaseArray(Ref.child('events').limitToLast(10));
// display any errors
$scope.events.$loaded().catch(alert);
// provide a method for adding a event
$scope.addEvent = function(newEvent) {
if (newEvent) {
// push a event to the end of the array
$scope.events.$add({
title: newEvent.title,
location: newEvent.location,
createdBy: user.uid,
createdAt: Firebase.ServerValue.TIMESTAMP
})
// display any errors
.catch(alert);
}
};
function alert(msg) {
$scope.err = msg;
$timeout(function() {
$scope.err = null;
}, 5000);
}
});
This is what the users and events look like in firebase:
To get the results that you're looking for, try using an angularjs filter.
In you controller add a function called
$scope.filterByUID = function(event) {
if (event.createdBy === user.uid) {
return true;
}
return false;
}
This function will act as a filter that only let's through events that were created the current user by comparing the event's createdBy to the user's uid.
Then change this line in your html
<tr scope="row" ng-repeat="event in events | reverse" >
To
<tr scope="row" ng-repeat="event in events | reverse | filter:filterByUID" >
This tells angularjs that you want to have your items filtered with the filter we defined in the controller.
Edit: Here's a reference on using custom filters: AngularJS : Custom filters and ng-repeat
I have created a drop down menu for people to edit the location. And all contents are generated by for loop, how may i depends on the feedID to change the region, because the $.post() funcion would change all region but not depends on it's feedID at the moment.
<span class="dropdown">
<span class="result-date" type="button" data-toggle="dropdown">Hong Kong
<span class="caret"></span>
</span>
<ul class="dropdown-menu">
<li id="CTHK">Hong Kong</li>
<li id="CTTW">Taiwan</li><
<li id="CTMA">Macau</li><
<li id="CTCN">China</li><
<li id="CTOT">Others</li>
</ul>
</span>
After the users have made decision of the modification and click on the desire option, it will trigger a $.post() function in order to change the JSON's content via speicifc api
var initRegionEdit = function() {
$("#CTHK").click(function() {
var api = "/domain/path/somemorepaths?id=" + id;
var postData = {
region : "TW"
};
$.post(api_submit, postData).done(function (data) {
console.log(postData)
});
})
}
And afterward it will get into an JSON file and change the region section
"errorCode": null,
"message": "Success",
"result": {
"total": 2,
"data": [
{
"title": "watever",
"content": " lietlie siktl i's fgkkksfsag...",
"region": "Hong Kong",
"feedId": "dfc68cff0a2afc540407d02f554d9508",
},
{
"title": "Visual F",
"content": "STORE \" I'm ...",
"region": "Hong Kong",
"feedId": "e54de354837e3add77b1030e5456e522",
}
how may i depends on the feedID to change the the region?
UPDATE 1:
Let's assume the user click on others ,The outcome I'd like to see is like when the user click on the .section defined by a feedId.
<div class="dfc68cff0a2afc540407d02f554d9508"> //generate by {{feedId}} handle bar js
<span class="dropdown">
<span class="result-date" type="button" data-toggle="dropdown">Hong Kong
<span class="caret"></span>
</span>
<ul class="dropdown-menu">
<li id="CTHK">Hong Kong</li>
<li id="CTTW">Taiwan</li><
<li id="CTMA">Macau</li><
<li id="CTCN">China</li><
<li id="CTOT">Others</li>
</ul>
</span>
</div>
and the javascript api should change the feedId match the section instead of every json's region
{
"title": "Visual F",
"content": "STORE \" I'm ...",
"region": "Others",
"feedId": "e54de354837e3add77b1030e5456e522",
}
P.S I am developing the application by node.js
A solution based on the following procedure:
Reading file -> modifying its contents -> write file would be to use the File System Node Module
Example code:
var fs = require('fs');
// Read file and parse into JSON
var jsonFileObject = JSON.parse(fs.readFileSync('fileName'));
... modify the contents of jsonFileObject as required
// Write the modified contents back to the file
var jsonFileObject = JSON.parse(fs.writeFileSync('fileName', jsonFileObject));
To modify the region based on the feedId in your particular example:
for (var dataIndex in jsonFileObject.result.data) {
if (jsonFileObject.result.data[dataIndex].feedId === feedId) {
jsonFileObject.result.data[dataIndex].region = updatedRegion;
}
}
assuming the JSON file has the same format as your post. If not, it should be fairly straightforward to modify the example to meet your needs.
I've seen so many ways to do this, but most are pretty old and I want to make sure I'm doing this correctly. Right now, the way I'm using isn't working and I feel like I'm missing something.
I'm getting the JSON back fine, I just need to get it to display in a table after I click the button.
Here is the JSON. This is how I'm going to get it from our server, I can't add any "var JSON =" or add any scope like "$scope.carrier" to the data, unless there's a way to add it after I've fetched the data.
{
"carrier":
[
{
"entity": "carrier",
"id": 1,
"parentEntity": "ORMS",
"value": "Medica"
}, {
"entity": "carrier",
"id": 2,
"parentEntity": "ORMS",
"value": "UHG"
}, {
"entity": "carrier",
"id": 3,
"parentEntity": "ORMS",
"value": "Optum"
}, {
"entity": "carrier",
"id": 4,
"parentEntity": "ORMS",
"value": "Insight"
}, {
"entity": "carrier",
"id": 5,
"parentEntity": "ORMS",
"value": "Insight"
}
]
}
Here is the app.js file to bring back the JSON data:
var app = angular.module('myTestApp', []);
app.controller('myController', ['$scope', '$http', function($scope, $http) {
var url = 'test.json';
$scope.clickButton = function() {
$http.get(url).success(function(data) {
console.log(data);
});
}
}]);
And then of course the HTML:
<div class="col-lg-12 text-center">
<button type=button class="btn btn-primary load" ng-click="clickButton()">Click!</button>
<table class="">
<tbody ng-repeat="carrier in carriers">
<tr>
<td>
<h3 class="">{{ module.entity }}</h3>
<h3 class="">{{ module.id }}</h3>
<h3 class="">{{ module.parentEntity }}</h3>
<h3 class="">{{ module.value }}</h3>
</td>
</tr>
</tbody>
</table>
</div>
I'm also wondering if I can use the ng-grid to put this in a table. I know they just upgraded it to ui grid so I'm not sure if this is still a feasible approach.
Also, I'm not getting errors, the data just won't display in the table right now. All I know is its returning the data properly, just not displaying in the table.
Any help is appreciated.
I looked at your plunker seems like you need to:
add angular script
wire the app and the controller
your variable in the repeater is wrong, I change it
take a look to this fixed plunker:
http://plnkr.co/edit/TAjnUCMOBxQTC6lNJL8j?p=preview
$scope.clickButton = function() {
$http.get(url).success(function(returnValue) {
alert(JSON.stringify(returnValue.carrier));
$scope.carriers = returnValue.carrier;
});
}
You never assign the value of the returned array to $scope.carriers.
At the line where you say console.log(data); add this:
$scope.carriers = data.data;
Here is the updated clickButton function (with a variable name change to reduce confusion):
$scope.clickButton = function() {
$http.get(url).success(function(returnValue) {
$scope.carriers = returnValue.data;
});
};