Translations reflect after click on combo, where combo options are filled dynamically in ngOnInit in Angular 6 - html

I have an autocomplete drop-down to which I bind list of ViewOption (my class)
class ViewOption {
name: string;
ordinal: number;
label: string
}
I create list of viewoption in ngOnInit by calling getViewOptionList,
getViewOptionList(): ViewOptions {
const viewoptions = new ViewOptions();
for (const enumMember in ViewOptionEnum) {
if (ViewOptionEnum.hasOwnProperty(enumMember)) {
const viewoption = new ViewOption();
const ordinalValue = parseInt(enumMember, 10);
if (ordinalValue >= 0) {
viewoption.oridinal = ordinalValue;
viewoption.label = ViewOptionEnum[enumMember].toLowerCase();
this.translate.get(viewoption.label).subscribe(msg => viewoption.name = msg);
viewoptions.push(viewoption);
}
}
}
return viewoptions;
}
<lis-auto-complete-dropdown placeholder="select view" automationid="worklistMaster_ddl_selectView"
[options]="ViewOptions" [(selectedItem)]="selectedViewOption"
(itemSelected)="selectViewOption($event)">
<ng-template let-item="item">
<span title="{{ item }}">
<span>{{ item.name }}</span>
</span>
</ng-template>
</lis-auto-complete-dropdown>
Translation reflect only when user clicks on autocomple dropdown on UI. How to make it fill before user taking any action?

Related

Clear mat-select selection Angular material

I have implemented mat-multi-select option. I have a search functionality and I am able to select multiple options at once. Post selection when I click generate button, I want to clear all the selections. I am able to clear the values from search bar but values are still selected if I open the multi select dropdown. How can I clear those values.
HTML Code
<mat-select [formControl]="cMultiCtrl" [multiple]="true" required (selectionChange)="selectionChange($event)">
<mat-option>
<ngx-mat-select-search placeholderLabel="Search" noEntriesFoundLabel="No Matching Value Found" [formControl]="cMultiFilterCtrl"></ngx-mat-select-search>
</mat-option>
<mat-option *ngFor="let val of filteredCMulti | async" [value]="val.value">
{{val.name}}
</mat-option>
</mat-select>
// the button on click of which I want to clear everything post API call
<button mat-raised-button (click)="generate()" class="download-button">Generate
</button>
TS Code
public filteredCMulti: ReplaySubject<any[]> = new ReplaySubject<any[]>(1);
public cMultiCtrl: FormControl = new FormControl();
ngOnInit() {
this.filteredCMulti.next(this.cvalues.slice());
this.cMultiFilterCtrl.valueChanges
.pipe(takeUntil(this._onDestroy))
.subscribe(() => {
this.filterBanksMulti();
});
}
ngAfterViewInit() {
this.setInitialValue();
}
ngOnDestroy() {
this._onDestroy.next();
this._onDestroy.complete();
}
private setInitialValue() {
this.filteredCMulti
.pipe(take(1), takeUntil(this._onDestroy))
.subscribe(() => {
this.singleSelect.compareWith = (a, b) => a.id === b.id;
});
}
selectionChange(event){
this.cvalue = event.value;
}
private filterBanksMulti() {
if (!this.cvalues) {
return;
}
let search = this.cMultiFilterCtrl.value;
if (!search) {
this.filteredCMulti.next(this.cvalues.slice());
return;
} else {
search = search.toLowerCase();
}
// filter the banks
this.filteredCMulti.next(
this.cvalues.filter(bank => bank.name.toLowerCase().indexOf(search) > -1)
);
}
generate(){
let msg = '';
// some logic here
else{
this.commonService.showSnackBar("Value generated")
this.filteredCMulti = new ReplaySubject; // this clears the search bar but not values, they are still selected
this.table();
}}
})
}
Provide an element ref to your <mat-select>
<mat-select #matRef [formControl]="cMultiCtrl" [multiple]="true" required (selectionChange)="selectionChange($event)">
<mat-option>
<ngx-mat-select-search placeholderLabel="Search" noEntriesFoundLabel="No Matching Value Found" [formControl]="cMultiFilterCtrl"></ngx-mat-select-search>
</mat-option>
<mat-option *ngFor="let val of filteredCMulti | async" [value]="val.value">
{{val.name}}
</mat-option>
</mat-select>
And update your ts file like:
#ViewChild('matRef') matRef: MatSelect;
clear() {
this.matRef.options.forEach((data: MatOption) => data.deselect());
}
You can optimize clear() by conditionally calling deselect()

Angular flatten inner array

I am trying to get a value from nested array from a Form Values
[['A'], ['B']]-- from this array
['A','B'] --trying to get value like this
stackblitz example
https://stackblitz.com/edit/angular-4d5vfj-p5adyk?file=main.ts
<button (click)="addNewChipList()">Add new Chip</button><br><br>
<form [formGroup]="myForm">
<ng-container formArrayName="names"
*ngFor="let item of myForm.get('names').controls; let i = index;">
<mat-form-field class="example-chip-list" [formGroupName]="i">
<mat-chip-list #chipList >
<mat-chip *ngFor="let val of item.value.val"
[selectable]="selectable"
[removable]="removable"
(removed)="removeChip(item, val)">
{{val}}
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
</mat-chip>
<input [placeholder]="item.value.name"
[matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="addOnBlur"
(matChipInputTokenEnd)="addChip($event, item)">
</mat-chip-list>
<mat-error>Atleast 1 name need to be added</mat-error>
</mat-form-field>
</ng-container>
<button (click)="save()">save</button><br><br>
</form>
component.ts
The componet file where I am trying to get form value
export class ChipListValidationExample implements OnInit {
#ViewChild('chipList') chipList: MatChipList;
public myForm: FormGroup;
// name chips
visible = true;
selectable = true;
removable = true;
addOnBlur = true;
readonly separatorKeysCodes: number[] = [ENTER, COMMA];
// data
data = {
names: [this.initName('name1'), this.initName('name2', [['A'],
['B']])]
}
constructor(private fb: FormBuilder) {
this.myForm = this.fb.group({
names: this.fb.array(this.data.names, this.validateArrayNotEmpty)
});
}
ngOnInit() {
this.myForm.get('names').statusChanges.subscribe(
status => this.chipList.errorState = status === 'INVALID'
);
}
initName(name: string, val: string[] = []): FormControl {
return this.fb.control({ name, val});
}
validateArrayNotEmpty(c: FormControl) {
if (c.value && c.value.length === 0) {
return {
validateArrayNotEmpty: { valid: false }
};
}
return null;
}
addChip(event: MatChipInputEvent, ctrl: FormControl): void {
const input = event.input;
const value = event.value;
// Add name
if ((value || '').trim()) {
const control = ctrl;
control.value.val.push(value.trim());
console.log(control.value);
}
// Reset the input value
if (input) {
input.value = '';
}
}
removeChip(ctrl, val) {
const idx = ctrl.value.val.findIndex(item => item === val);
ctrl.value.val.splice(idx, 1);
}
addNewChipList() {
const items = this.myForm.get('names') as FormArray;
items.push(this.initName(`name${items.length + 1}`));
}
save(){
console.log("FormValues",this.myForm.value)
}
}
I am trying to get the form value as ['A','B']
Flattening some arrays is a trivial task with plain JS. Using Array.flat, you can do this in one line.
The flat() method creates a new array with all sub-array elements concatenated into it recursively up to the specified depth.
Syntax
var newArray = arr.flat([depth]);
Parameters
depth Optional
The depth level specifying how deep a nested array structure should be flattened. Defaults to 1.
Return value
A new array with the sub-array elements concatenated into it.
//one level deep
var x = [['A'], ['B']];
console.log(x.flat());
// => ['A', 'B']
//2 levels deep
var y = [[['A', 'B'], ['C']], ['D']];
console.log(y.flat(2));
// => ['A', 'B', 'C', 'D']

Show me the selected toggle button in Angular 4 and the other slice

I have made toggle buttons like component in Angular so I can use everywhere I need, but I am trying to solve something and I need your help.
I need only to show me the selected toggle button so the other toggle button don't show.
When I click in the selected toggle button then show me the other toggle buttons for example like expand and collapse, if I click the selected toggle button than show me the everything what is in that array.
The selected toggle button comes from another component with ngModel which tells by the component which is selected
I have tried slice but didn't work.
This is the component of toggle button.
<div id="toggle-button" fxLayout="row" fxLayoutAlign="start end">
<label [style.width]="labelWidth" [style.paddingRight]="label.length > 0 ? '10px' : '0'">
{{label}}
</label>
<div *ngFor="let option of options | slice:0:1; let first = first; let last = last" [ngClass]="{'first': first, 'last': last, 'selected': option.value === value, 'divider' : !last, 'clickable': !readonly, 'not-selectable': readonly}"
[style.width]="optionWidth" (click)="select(option.value)" fxLayout="row" fxLayoutAlign="center center">
<span (click)="options.length">{{option.text}}</span>
</div>
</div>
export class ToggleButtonComponent implements OnInit, ControlValueAccessor {
#Input() options: ToggleOption[] = []
#Input() label = ""
#Input() value: any
#Input() labelWidth = ""
#Input() optionWidth = ""
#Input() readonly = false
#Output() toggle = new EventEmitter<any>()
onChangeCallback: (selected: any) => void = () => { }
onTouchedCallback: (selected: any) => void = () => { }
constructor() {
}
ngOnInit() {
console.log(this.value)
}
writeValue(selected: any): void {
this.value = selected
}
registerOnChange(callback: (selected: any) => void): void {
this.onChangeCallback = callback
}
registerOnTouched(callback: (selected: any) => void): void {
this.onTouchedCallback = callback
}
select(selected: any) {
if (!this.readonly) {
this.value = selected
this.onChangeCallback(selected)
this.onTouchedCallback(selected)
this.toggle.emit(selected)
}
}
}
export interface ToggleOption {
text: string
value: any
}
And this is another component where I declare the toggle buttons.
readonly categoryOptions: ToggleOption[] = [
{ text: "BUS", value: 0 },
{ text: "BOS", value: 1 },
{ text: "BIS", value: 2 }
]
<app-toggle-button label="Category" labelWidth="75px" [options]="categoryOptions" [(ngModel)]="valueItem.category"></app-toggle-button>
Aim
The aim to have multiple toggle buttons with multiple options.
Solution
You need to have toggleState variable to show/hide other buttons.
A variable value to check for current selected buttons which.
You just need to tweak in your ts and html file as -
ts
Add new variable called toggleState to hold toggle state and change the state whenever select function is called.
toggleState = false;
select(selected: any) {
if (!this.readonly) {
this.value = selected
this.onChangeCallback(selected)
this.onTouchedCallback(selected)
this.toggle.emit(selected)
this.toggleState = !this.toggleState; //<-- toggle state here
}
}
html
Just check for current value and toggle state using the syntax *ngIf="option.value == value || toggleState
"
<div id="toggle-button" fxLayout="row" fxLayoutAlign="start end">
<label [style.width]="labelWidth" [style.paddingRight]="label.length > 0 ? '10px' : '0'">
{{label}}
</label>
<div *ngFor="let option of options; let first = first; let last = last" [ngClass]="{'first': first, 'last': last, 'selected': option.value === value, 'divider' : !last, 'clickable': !readonly, 'not-selectable': readonly}"
[style.width]="optionWidth" (click)="select(option.value)" fxLayout="row" fxLayoutAlign="center center"
*ngIf="option.value == value || toggleState">
<span (click)="options.length">{{option.text}}</span>
</div>
</div>
Toggle false on click anywhere else
You can use the HostListener to handle this -
constructor(private elementRef: ElementRef) {}
#HostListener('document:click', ['$event'])
public documentClick(event: MouseEvent): void {
const targetElement = event.target as HTMLElement;
// Check if the click was outside the element
if (targetElement && !this.elementRef.nativeElement.contains(targetElement)) {
this.toggleState = false; //<-- you can emit if required.
}
}
Note : Ideally this kind scenario should be handle through Directive.
I would use a Pipe, so like this?
https://stackblitz.com/edit/angular-5u5wn1

Delete option for user if he is the owner of the perticular property

I want to show delete option for a user if he has created perticular exhibit. I am getting a current user id from getCurrentUser service and i am getting an array of exhibits in which thre is a field "userId".
I am trying to match id of the current user and userId from Exhibits array in such a way that if there is a match, then only user will get delete option for perticular exhibit but I am unable to do it in proper way.
Below is my code:
-------------------------------------------------------------------------------
ngOnInit() {
this.getCurrentUser();
this.getIsSupervisor();
this.spinnerService.show();
let allRoutesOption = Route.emptyRoute();
allRoutesOption.title = 'ALL';
this.routes = [allRoutesOption];
this.getAllExhibits();
this.routeService.getAllRoutes(1, 100)
.then(
data => this.routes = this.routes.concat(data.items)
).catch(
error => console.error(error)
);
this.getPage(1);
}
ngOnDestroy() {
this.spinnerService.hide();
}
getIsSupervisor() {
this.supervisorGuard.isSupervisor().then(
(response: boolean) => {
this.isSupervisor = response;
});
}
getCurrentUser() {
this.userService.getCurrent()
.then(
(response) => {
this.currentUserId = response.id;
this.exhibitService.getAllExhibits(1, this.maxNumberOfMarkers)
.then(
(data) => {
this.allExhibits = data.items;
for (let exhibit of this.allExhibits) {
this.exhibitsUserIds.push(exhibit.userId);
if (this.exhibitsUserIds !== this.currentUserId) {
this.canDelete = false;
} else {
this.canDelete = true;
}
}
}
);
}
);
}
-------------------------------------------------------------------------------
My Html:
----------------------------------------
<md-nav-list>
<md-list-item [routerLink]="['/mobile-content/exhibits/view', exhibit.id]" ng-blur="true" *ngFor="let exhibit of exhibits | paginate: { id: 'server',
itemsPerPage: exhibitsPerPage,
currentPage: currentPage,
totalItems: totalItems }">
<img md-list-avatar *ngIf="previewsLoaded && previews.has(exhibit.id); else exhibitIcon" [src]="previews.get(exhibit.id)"
alt="{{ 'image preview' | translate }}" [ngStyle]="{'width.px': 48, 'height.px': 48}">
<ng-template #exhibitIcon>
<md-icon md-list-icon class="type-icon" [ngStyle]="{'font-size.px': 40, 'height.px': 40, 'width.px': 40}">place</md-icon>
</ng-template>
<h2 md-line>{{ exhibit.name }} ({{ exhibit.status | translate }})
<hip-star-rating class="fix-position" *ngIf="exhibit.ratings" [rating]='exhibit.ratings' [exhibitId]='exhibit.id'></hip-star-rating>
</h2>
<p md-line>{{ exhibit.description }}</p>
<p md-line>
<span class="latitude">{{ exhibit.latitude }}</span>,
<span class="longitude">{{ exhibit.longitude }}</span>
</p>
<p *ngIf="exhibit.tags.length > 0" md-line>
<span *ngFor="let tag of exhibit.tags" class="tag-name">{{ tag }}</span>
</p>
<button md-icon-button click-stop-propagation color="primary" [routerLink]="['/mobile-content/exhibits/edit', exhibit.id]"
title="{{ 'edit' | translate }}">
<md-icon>{{ !inDeletedPage ? 'edit' : 'remove_red_eye'}}</md-icon>
</button>
<div *ngIf="canDelete">
<button md-icon-button click-stop-propagation color="warn" (click)="deleteExhibit(exhibit)" *ngIf="!exhibit.used && !inDeletedPage"
title="{{ 'delete' | translate }}">
<md-icon>delete_forever</md-icon>
</button>
</div>
</md-list-item>
----------------------------------------
Can someone help me to figure it out?
I am new to Angular myself but maybe I can still help. I am noticing a couple things that may be causing issues.
Firstly
When you are iterating across this.allExhibits, I noticed that the this.canDelete is just one value that you keep reassigning after each iteration. By the end it only represents the 'deleteability' of only the last exhibit.
Perhaps you can create some sort of object or array to map against the for..of iteration of this.allExhibits. That way you can store each resolved value of this.canDelete without overwriting it on each iteration.
example.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-example',
templateUrl: './example.component.html'
})
export class ExampleComponent {
currentUser:object = {
name: 'User A',
id: 'A'
};
exhibits:object[] = [
{
title: 'Exhibit A',
id: 'A'
},
{
title: 'Exhibit B',
id: 'B'
},
{
title: 'Exhibit C',
id: 'C'
}
];
constructor() { }
deleteExhibit(index) {
this.exhibits = this.exhibits.filter((_, i) => i != index);
}
}
example.component.html
<div *ngFor="let exhibit of exhibits; let i=index">
<h3>{{exhibit.title}}</h3>
<button *ngIf="exhibit.id == currUser.id" (click)="deleteExhibit(i)">DELETE</button>
<hr/>
</div>
Secondly
I presume the getCurrentUser() is something that happens as the component instantiates. In that case, the *ngIf must await the resolved value of this.canDelete before it can either display or hide the delete button.
Since getCurrentUser() appears to resolve sometime after the component's initial rendering of the view, it maybe be possible that setting the value of this.canDelete is not triggering Angular's change detection.
Perhaps try ChangeDetectorRef.detectChanges() after you resolve the final value of this.canDelete. ChangeDetectorRef is importable from #angular/core and instantiable in the component's constructor: constructor(private changeDetectorRef:ChangeDetectorRef) {}.
Hopefully this helps!

Refreshing angular ag-grid data

I have an ag-grid which is rendering table from a .json file and an external Quick filter that is searching through ag-grid on key input on the filter. After someone searches the search term is displayed in the form of angular material chip with a "X" sign to close the chip with remove function. I want to reload the ag-grid to its default state once someone cancel/close the chip and also to include multiple filters in it using the chip. Here is my sample code, but I'm struggling with setting it up.
Html-
<div class="container">
<mat-form-field class="demo-chip-list" *ngIf="gridApi">
<mat-chip-list #chipList>
<div style="width:100%; margin-left:10%;"><label><span class="search-button">Search Funds</span></label>.
<input class="search-input"
[ngModel]="filterText"
(ngModelChange)=
"gridApi.setQuickFilter
($event)"
[matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="addOnBlur"
(matChipInputTokenEnd)="add($event)" />.
</div><br/><div style="width:100%; margin-left:10%;"><mat-chip *ngFor="let fruit of fruits"
[selectable]="selectable"
[removable]="removable"
(click)="onGridReady(params)"
(remove)="remove(fruit)">
{{fruit.name}}
<mat-icon matChipRemove *ngIf="removable" ><sup>x</sup></mat-icon></mat-chip></div></mat-chip-list>.
</mat-form-field>
<div class="header" style="display:inline"></div><div> <ag-grid-angular
style="position:absolute;padding-left:5%; bottom:0px;width: 90%; height: 350px;" #agGrid id="myGrid" class="ag-fresh" [columnDefs]="columnDefs"
[animateRows]="true"
[enableRangeSelection]="true"
[enableSorting]="true"
[enableFilter]="true"
[pagination]="true"
(gridReady)="onGridReady($event)">
</ag-grid-angular></div></div>
Component-
#Component({
selector:
'app-funds-table',
templateUrl:
'./funds-table.component.html',
styleUrls:
['./funds-table.component.css']
})
export class
FundsTableComponent
implements OnInit {
visible: boolean = true;
selectable: boolean = true;
removable: boolean = true;
addOnBlur: boolean = true;
// Enter, comma
separatorKeysCodes = [ENTER, COMMA];
fruits = [
{ name: 'ABC' }
];
add(event: MatChipInputEvent): void
{
let input = event.input;
let value = event.value;
// Add our fruit
if ((value || '').trim()) {
this.fruits.push({ name:
value.trim() });
}
// Reset the input value
if (input) {
input.value = '';
}
}
remove(fruit: any): void {
let index =
this.fruits.indexOf(fruit);
if (index >= 0) {
this.fruits.splice(index, 1);
}
}
private gridApi;
private gridColumnApi;
private columnDefs;
private filterText = "";
ngOnInit() {}
constructor(private http:
HttpClient ){
this.columnDefs = [{headerName:
"Ticker", field: "Ticker"},
{headerName: "Id", field: "Id"},
{headerName: "Utilities", field:
"Utilities"}
];
}
onGridReady(params) {
this.gridApi = params.api;
this.gridColumnApi =
params.columnApi;
this.http.get
("/fundsData/fund_info.json". )
.subscribe
(data =>
{this.gridApi.setRowData(data);
});
}
}
According doc:
You can reset filter via direct api call
api.setQuickFilter(''); - empty for reset filter