Need to properly iterate through my picture array angular 2 - javascript

So I have this function that is a 'next' button that is supposed to go to the next picture in my array. I have also a 'previous' button that has basically the same code but instead of index adding 1 it subtracts 1. That works fine. But this one won't work. When I hit the next button it automatically goes to the last picture in the array and doesn't see the next image. I thought maybe using the pop method but I am not sure, very new to this
export class PictureModalComponent {
model = {
servNum: '',
servDate: '',
caption: '',
url: '',
id: null,
pictures: []
};
constructor(#Inject(MAT_DIALOG_DATA) public data: any, public dialogRef: MatDialogRef<PictureModalComponent>) {
this.model=data;
}
onPreviousClick() {
this.model.pictures.forEach( (picture: AccessApictures, index: number) => {
if (picture.id === this.model.id)
{
let lastPicture: AccessApictures = this.model.pictures[index-1];
this.model.caption = lastPicture.caption;
this.model.servDate = lastPicture.servDate;
this.model.servNum = lastPicture.aservrecno;
this.model.url = lastPicture.filename;
this.model.id = lastPicture.id;
}
});
}
onNextClick() {
this.model.pictures.forEach( (picture: AccessApictures, index: number) => {
if (picture.id === this.model.id ) {
let nextPicture: AccessApictures = this.model.pictures[index+1];
this.model.caption = nextPicture.caption;
this.model.servDate = nextPicture.servDate;
this.model.servNum = nextPicture.aservrecno;
this.model.url = nextPicture.filename;
this.model.id = nextPicture.id;
}
});
}
}

Creating an index variable should help you by removing your costly loops. It's basically 2 bytes that will save your performance.
index = 0;
model = {
servNum: '',
servDate: '',
caption: '',
url: '',
id: null,
pictures: []
};
constructor(#Inject(MAT_DIALOG_DATA) public data: any, public dialogRef: MatDialogRef<PictureModalComponent>) {
this.model = data;
data.pictures.forEach((pic, index) => {
if (pic.id === data.id) {
this.index = index;
break;
}
}
}
// update index with looping to the last picture if on first
onPreviousClick() {
this.index = this.index === 0 ? this.model.pictures.length - 1 : this.index - 1;
this.updateModel(this.model.pictures[this.index]);
}
// updating index with looping to the first picture if on last
onNextClick() {
this.index = this.index === this.model.pictures.length - 1 ? 0 : this.index + 1;
this.updateModel(this.model.pictures[this.index]);
}
// common logic into a single function
updateModel(pic: AccessApictures) {
this.model.caption = pic.caption;
this.model.servDate = pic.servDate;
this.model.servNum = pic.aservrecno;
this.model.url = pic.filename;
this.model.id = pic.id;
}

Related

Using custom Angular Material Sort?

I want to create a dropdown (or mat-select) to use as a sorting mechanism instead of the Angular Material Sort Header. So, if I for example click on the 'username' inside the dropdown, I want the table to sort by the username (instead of clicking on the header).
How can I do it? Any documentation online on how to achieve this?
Thank you for any help.
As required, I attach some code:
ngOnInit(): void {
this.filteredOptions = this.myControl.valueChanges.pipe(
startWith(""),
map((value) => this._filter(value))
);
}
ngAfterViewInit() {
this.providersAdmin.sort = this.sort;
}
getAllAdmins() {
this.isLoading = true;
this.homeService.getAllAdmins().subscribe(
(response) => {
this.admins = response;
this.providersAdmin = new MatTableDataSource(this.admins);
this.isLoading = false;
},
(error) => {}
);
}
sortTableBy(event: any) {
const sortState: Sort = {
active: "username",
direction: "desc",
};
this.sort.active = sortState.active;
this.sort.direction = sortState.direction;
this.sort.sortChange.emit(sortState);
console.log(event);
}
The sortTableBy method is the one I found on here but nothing happens.
I added matSort on the mat-table and I added mat-sort-header on the header cell.
EDIT:
Hi, I managed to fix the problem by writing the following:
sortTableBy(event: any) {
const sortState: Sort = {
active: "username",
direction: "desc",
};
this.sort.active = sortState.active;
this.sort.direction = sortState.direction;
this.sort.sortChange.emit(sortState);
this.providersAdmin.sort = this.sort;
}
There is an example for you:
Exmaple
Your sort function has a wrong implementation, this is work for me:
sortData(fieldName: string) {
if (!fieldName) {
return;
}
const sortState: MatSortable = {
id: fieldName,
start: 'desc',
disableClear: true
};
this.sort.sort(sortState);
}
I am going to set up an example which you can adapt easily:
compare(a: number | string, b: number | string, isAsc: boolean) {
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}
sortData() {
let isAsc = this.sort.direction != "" ?
event.direction == SortDirection.asc :
true;
let data = this.dataSource.data.slice();
data.sort((a, b) => {
switch (this.myChosenSort) {
case 'healthCareCenterName':
return this.compare(a.healthCareCenterName, b.healthCareCenterName, isAsc);
case 'address':
return this.compare(a.address, b.address, isAsc);
case 'contact':
return this.compare(a.contact, b.contact, isAsc);
default:
return 0;
}
});
this.dataSource = new MatTableDataSource<ServiceProviderTable>(data);
}
To change the sort.direction you need to play around a little bit with the code, maybe directly from the dropdown and hardcoding the isAsc when calling the compare method, depending on the value of the this.myChosenSort.

Calling JSON.parse from a function returns Undefined

I need to store a JSON in a variable. I have the next function:
retrieve(){
JSON.parse(localStorage.getItem('todos'));
}
And i'm trying to store the return of the function in the variable.
this.todos = this.retrieve()
But I'm getting:
model.js:5 Uncaught TypeError: Cannot read property 'retrieve' of undefined
If i do this instead, it works:
this.todos = JSON.parse(localStorage.getItem('todos'));
Why is that happening?
Edit: Full code
export default class Model {
constructor(){
this.view = null;
this.todos = this.retrieve();
if(!this.todos || this.todos.length < 1){
this.todos = [
{
id: 0,
title: 'default',
description: 'default',
completed: false,
}
]
this.id = 1;
}
this.id = this.todos[this.todos.length - 1].id + 1;
retrieve(){
return JSON.parse(localStorage.getItem('todos'));
}
}
Thanks everyone.
Your class level methods should be outside the constructor.
localStorage.setItem('todos', JSON.stringify([{id:1},{id:2},{id:3}]))
class Model {
constructor(){
this.view = null;
this.todos = this.retrieve();
if(!this.todos || this.todos.length < 1){
this.todos = [
{
id: 0,
title: 'default',
description: 'default',
completed: false,
}
]
this.id = 1;
}
this.id = this.todos[this.todos.length - 1].id + 1;
}
retrieve(){
return JSON.parse(localStorage.getItem('todos'));
}
}
console.log(new Model());

Cannot read property 'push' of null in angular 9

i get flat list from server and i must create a tree that list .
this is my model :
export interface ClaimManagerList {
id: number;
title: string;
parentId: number;
isChilde: boolean;
childs: Childes[];
}
export interface Childes {
id: number;
title: string;
parentId: number;
isChilde: boolean;
}
and in this code i convert flat list to tree list -> childs add to this property childs :
return this.claimsManagerService.getAll(this.searchParam).pipe(
map(data => {
data['records'].forEach(element => {
let model = {} as ClaimManagerList;
if (element.parentId == null) {
model.id = element.id;
model.isChilde = element.isChilde;
model.parentId = element.parentId;
model.title = element.title;
data['records'].forEach(child => {
if (child.parentId == element.id) {
let childe = {} as Childes;
childe.id = child.id;
childe.isChilde = child.isChilde;
childe.parentId = child.parentId;
childe.title = child.title;
model.childs.push(childe)
}
})
this.claims.push(model)
}
})
return this.claims;
})
but it show me error in this line :
model.childs.push(childe)
Cannot read property 'push'
whats the problem ? how can i solve this problem ?
This happening as model.childs is not set to an empty array at the beginning. We can resolve this like:
if(!model.childs) model.childs = [] as Childes[];
model.childs.push(childe) // This line should work fine now.
I'm going to propose some changes to your code to order to improve this. I hope these changes will be useful for you.
return this.claimsManagerService.getAll(this.searchParam).pipe(
map((data: any) => {
data.records.forEach((element: any) => {
let model: ClaimManagerList = {};
if (element.parentId == null) {
model.id = element.id;
model.isChilde = element.isChilde;
model.parentId = element.parentId;
model.title = element.title;
model.childs = [];
data.records.forEach((child: any) => {
if (child.parentId == element.id) {
let childe = {} as Childes;
childe.id = child.id;
childe.isChilde = child.isChilde;
childe.parentId = child.parentId;
childe.title = child.title;
model.childs.push(childe)
}
})
this.claims.push(model)
}
})
return this.claims;
})

Values are not setting into model class in angular7?

I am building up an ecommerce web in which i have my order Model class like this
import { User } from './user.model';
export class Order {
constructor(){}
amount: Number = 0;
status: String = "";
date: String = '';
products: [any];
userId: String = '';
user : User;
}
And user model like this
export class User{
name: string = '';
email: string = '';
country: string = '';
city: string = '';
mobileNumber: string = '';
address: string = '';
postalCode : string = '';
nearBy : string = '';
_id : string = '';
}
And i have a cart.service.ts in which i am calculating an order total amount like this
// Total amount
public getTotalAmount(): Observable<number> {
return this.cartItems.pipe(map((product: CartItem[]) => {
return products.reduce((prev, curr: CartItem) => {
return prev + curr.product.price * curr.quantity;
}, 0);
}));
}
Now on my checkout.component.ts i am binding values to order Model class like this
isUserLoggedIn: boolean = false;
orderRawData: any;
order: Order;
placeOrder() {
this.cartService.getTotalAmount().subscribe(total=>{
if (total) {
console.log('amount : ',total);
this.order.amount = total;
this.orderRawData = JSON.parse(localStorage.getItem('cartItem'));
if (this.isUserLoggedIn) {
this.order.userId = this.user._id;
}else{
this.order.user = this.user;
}
this.orderRawData.forEach((item,index)=>{
this.order.products.push({
id : item.product._id,
quantity : item.quantity
})
})
this.order.date = new Date().toLocaleString();
this.order.status = 'Pending';
console.log(this.order);
}
})
}
But it's giving me an error like this
What am i doing wrong?
You have declared order:
order: Order;
Bu it never gets assigned an object, so is undefined when you try to updated one of its properties.
this.order.amount = total;
// <--- undefined here
You somehow need to assign order, for example:
this.orderService.getOrder().subscribe(order => {
this.order = order;
});
Before you can consider updating properties on it.
Your order follows the 'Order' format but it's not initialize.
You can do like this:
1) order: Order = {
amount: 0,
status: null,
....
}
or 2) Update the Order class like:
export class Order {
amount: Number;
status: String;
...
constructor(_amount: Number = 0, _status: String = '', ...) {
this.amount = _amount;
this.status = _status;
....
}
}
In the component:
order: Order = new Order();

PrimeNG p-table header select all persistence with lazy loading and pagination

Current config (cannot update it to latest):
"#angular/cli": "^7.3.9",
"primeng": "7.0.5",
I have a PrimeNG p-table that has lazy loaded data with pagination.
There is an issue open for it on PrimeNG GitHub too - https://github.com/primefaces/primeng/issues/8139
Stackblitz link is already attached in that issue so didn't create a new one.
Scenario:
One 1st page, some rows are selected via checkbox selection.
On 2nd page, Select All checkbox from the header is selected and all rows on 2nd page is auto-selected.
Now when navigated to the first page, the selections from here are reset. But the Select All checkbox in the header is still checked.
Would like to know if anyone has a workaround for this issue?
Any help is appreciated.
Edit:
Solution found in another similar GitHub issue: https://github.com/primefaces/primeng/issues/6482
Solution:
https://github.com/primefaces/primeng/issues/6482#issuecomment-456644912
Can someone help with the implementation of the override in an Angular 7/8 application. Not able to understand as how to get the TableHeaderCheckbox reference and override the prototype.
Well, the solution to the problem is still not added to the PrimeNG repo and so even the latest package does not have it solved.
For time being, use the solution mentioned in the question under Edit
To answer the question that I have asked under the Edit, check below:
// In some service file:
import { Table, TableHeaderCheckbox } from 'primeng/table';
import { ObjectUtils } from 'primeng/components/utils/objectutils';
import { uniq, each, intersection, map, remove } from 'lodash';
#Injectable()
export class BulkSelectAllPagesService {
overridePrimeNGTableMethods() {
TableHeaderCheckbox.prototype.updateCheckedState = function () {
const currentRows = map(this.dt.value, this.dt.dataKey);
const selectedRows = map(this.dt.selection, this.dt.dataKey);
this.rowsPerPageValue = this.dt.rows;
const commonRows = intersection(currentRows, selectedRows);
return commonRows.length === currentRows.length;
};
Table.prototype.toggleRowsWithCheckbox = function (event, check) {
let _selection;
if (!check) {
_selection = this.value.slice();
each(_selection, (row) => {
const match = {}; match[this.dataKey] = row[this.dataKey];
remove(this._selection, match);
});
} else {
_selection = check ? this.filteredValue ? this.filteredValue.slice() : this.value.slice() : [];
each(this._selection, (row) => {
const match = {}; match[this.dataKey] = row[this.dataKey];
remove(_selection, match);
});
this._selection = this._selection.concat(_selection);
}
this.preventSelectionSetterPropagation = true;
this.updateSelectionKeys();
this.selectionChange.emit(this._selection);
this.tableService.onSelectionChange();
this.onHeaderCheckboxToggle.emit({
originalEvent: event,
affectedRows: _selection,
checked: check
});
};
}
// In app.component.ts
import { Component, OnInit } from '#angular/core';
import { BulkSelectAllPagesService } from 'PATH_TO_THE_FILE/bulk-select-all-pages.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
constructor(
private bulkSelectAllPagesService: BulkSelectAllPagesService) {
}
ngOnInit() {
this.bulkSelectAllPagesService.overridePrimeNGTableMethods();
}
}
Ofcourse need to include the service file in the providers[] in the app.module.ts
Will create a stackblitz and add later.
Improved version to handle rowspan grouped data:
overridePrimeNGTableMethods() {
TableHeaderCheckbox.prototype.updateCheckedState = function () {
const currentRows = map(this.dt.value, this.dt.dataKey);
const uniqueCurrentRows = uniq(currentRows);
const selectedRows = map(this.dt.selection, this.dt.dataKey);
this.rowsPerPageValue = this.dt.rows;
const commonRows = intersection(currentRows, selectedRows);
if (currentRows.length) {
return commonRows.length === uniqueCurrentRows.length;
} else {
return false;
}
};
Table.prototype.toggleRowWithCheckbox = function (event, rowData) {
const findIndexesInSelection = (selection: any = [], data: any = {}, dataKey: any) => {
const indexes = [];
if (selection && selection.length) {
selection.forEach((sel: any, i: number) => {
if (data[dataKey] === sel[dataKey]) {
indexes.push(i);
}
});
}
return indexes;
};
this.selection = this.selection || [];
const selected = this.isSelected(rowData);
const dataKeyValue = this.dataKey ? String(ObjectUtils.resolveFieldData(rowData, this.dataKey)) : null;
this.preventSelectionSetterPropagation = true;
if (selected) {
const selectionIndexes = findIndexesInSelection(this.selection, rowData, this.dataKey);
const selectedItems = this.selection.filter((val: any) => {
return val[this.dataKey] === rowData[this.dataKey];
});
this._selection = this.selection.filter((val: any, i: number) => {
return selectionIndexes.indexOf(i) === -1;
});
this.selectionChange.emit(this.selection);
selectedItems.forEach((selectedItem: any, index: number) => {
this.onRowUnselect.emit({ originalEvent: event.originalEvent, index: event.rowIndex + index, data: selectedItem, type: 'checkbox' });
});
delete this.selectionKeys[rowData[this.dataKey]];
} else {
let rows = [rowData];
if (dataKeyValue) {
rows = this.value.filter(val => {
return (val[this.dataKey]).toString() === dataKeyValue;
});
}
this._selection = this.selection ? this.selection.concat(rows) : rows;
this.selectionChange.emit(this.selection);
this.onRowSelect.emit({ originalEvent: event.originalEvent, index: event.rowIndex, data: rowData, type: 'checkbox' });
if (dataKeyValue) {
this.selectionKeys[dataKeyValue] = 1;
}
}
this.tableService.onSelectionChange();
if (this.isStateful()) {
this.saveState();
}
};
Table.prototype.toggleRowsWithCheckbox = function (event, check) {
let _selection;
if (!check) {
_selection = this.value.slice();
each(_selection, (row) => {
const match = {}; match[this.dataKey] = row[this.dataKey];
remove(this._selection, match);
});
} else {
_selection = check ? this.filteredValue ? this.filteredValue.slice() : this.value.slice() : [];
each(this._selection, (row) => {
const match = {}; match[this.dataKey] = row[this.dataKey];
remove(_selection, match);
});
this._selection = this._selection.concat(_selection);
}
this.preventSelectionSetterPropagation = true;
this.updateSelectionKeys();
this.selectionChange.emit(this._selection);
this.tableService.onSelectionChange();
this.onHeaderCheckboxToggle.emit({
originalEvent: event,
affectedRows: _selection,
checked: check
});
};
}

Categories