How to use debounce time in an angular input search while searching through the list of items.Should i use RXJS and how to put it in my function filterReports? Can you please show me in code what i should do?
Code here:
protected reports: Array<ReportGroupModel> = [];
protected filteredReports: Array<ReportGroupModel> = [];
constructor(
protected route: ActivatedRoute,
protected httpService: HttpService
) {
super(route);
this.titleIcon = 'fa-bar-chart';
this.titleSufixKey = 'reports';
this.filterReports = debounce(this.filterReports, 500);
}
filterReports(filter: any) {
const searchText = filter.target.value?.toUpperCase();
if (searchText) {
let newFilteredReports: Array<ReportGroupModel> = [];
for (let reportGroup of this.reports) {
let foundItems = reportGroup.items.filter(x => x.title.toUpperCase().includes(searchText));
if (foundItems && foundItems.length > 0) {
newFilteredReports.push(new ReportGroupModel(reportGroup.header, foundItems));
}
}
this.filteredReports = newFilteredReports;
}
else
this.filteredReports = this.reports;
}
ngOnInit() {
super.ngOnInit();
this.filteredReports = this.reports;
}
and here is html
<div class="d-flex">
<input search="text" class="form-control mw-200p ml-auto" (keyup)="component.filterReports($event)" autofocus placeholder="{{ 'search' | translate }}"/>
</div>
<div class="d-flex flex-wrap pl-2">
<div *ngFor="let report of component?.filteredReports" class="pr-5 pt-2" style="width: 350px;">
<h3>{{report.header | translate}}</h3>
<ul class="list-unstyled pl-1">
<li *ngFor="let item of report.items">
<i class="fa {{item.icon}} mr-h"></i>
<a class="link" [routerLink]="item.path"> {{item.title}} </a>
<p *ngIf="item.description" class="text-muted">{{item.description}}</p>
</li>
</ul>
</div>
</div>
The easiest way to solve this issue is to use the reactive form module. however, if you want to stick with ngModel, you can do something like this.
searchChanged = new Subject();
protected reports: Array<ReportGroupModel> = [];
protected filteredReports: Array<ReportGroupModel> = [];
and update the subject every time the keyup event update
<div class="d-flex">
<input search="text" class="form-control mw-200p ml-auto" (keyup)="onKeyUp($event)" autofocus placeholder="{{ 'search' | translate }}"/>
</div>
//.ts
onKeyUp(value: string) {
this.searchChanged.next(value)
}
Now, you can use searchChanged subject to debounce the event update
constructor(
protected route: ActivatedRoute,
protected httpService: HttpService
) {
...
this.searchChanged.pipe(
debounceTime(300)
// You better add takeUntil or something to unsubscribe this observable when this component destroy
).subscribe(value => {
this.filterReports(value);
})
}
Related
I'm working on building a set of filters, so I'm just trying to make use of the salesChannels array content in my view, which only gets populated when clicking the button with the test() function. The log in ngOnInit outputs an empty array the first time, but works correctly after pressing the button.
The getOrganisationChannels returns an observable.
What causes this behavior and how do I handle it properly? I tried using an eventEmitter to try and trigger the populating but that doesn't work.
TYPESCRIPT
export class SalesChannelFilterComponent implements OnInit {
constructor(
public organizationService: OrganizationService
) { }
#Input() organizationId: any;
salesChannels: Array<any> = [];
selectedChannels: Array<any> = [];
allSelected: Array<any> = [];
ngOnInit() {
this.getChannels();
console.log(this.salesChannels);
}
getChannels() {
this.organizationService.getOrganizationChannels(this.organizationId).subscribe(
salesChannels => {
this.salesChannels = salesChannels;
})
}
test() {
console.log(this.salesChannels);
}
}
HTML
<div>
{{ salesChannels | json }}
</div>
<button (click)="test()">test</button>
<div *ngFor="let channel of salesChannels; let i = index;" class="checkbox c-checkbox">
<label>
<input type="checkbox">
<span class="fa fa-check"></span>{{channel.name}}
</label>
</div>
This is expected behaviour since you are populating the salesChannel in the subscription of an Observable. It's recommended that you use aysnc pipe to let angular check for changes and update the view accordingly.
Component.ts :
export class SalesChannelFilterComponent implements OnInit {
constructor(
public organizationService: OrganizationService
) { }
#Input() organizationId: any;
salesChannels$!: Observable<Array<any>>;
selectedChannels: Array<any> = [];
allSelected: Array<any> = [];
ngOnInit() {
this.getChannels();
console.log(this.salesChannels);
}
getChannels() {
this.salesChannels$ = this.this.organizationService.getOrganizationChannels(this.organizationId);
}
test() {
console.log(this.salesChannels);
}
}
In your template:
<button (click)="test()">test</button>
<div *ngFor="let channel of salesChannels$ | async; let i = index;" class="checkbox c-checkbox">
<label>
<input type="checkbox">
<span class="fa fa-check"></span>{{channel.name}}
</label>
</div>
More details: https://angular.io/api/common/AsyncPipe
I recommend using AsyncPipe here:
<div>{{ salesChannels | async}}</div>
and in .ts:
salesChannels = this.organizationService.getOrganizationChannels(this.organizationId)
I am trying to access this data sample (comments) in firebase realtime database from my Nativescript Angular application to render the comments in the xml code below.
This code works fine with JSON data imported to my database but not with the auto-generated alphanumeric ids created by firebase after initiating the push() request. (If you substitute the string ids with integers the code will work fine).
Firebase screenshot.
Typescript file
export class DishdetailComponent implements OnInit {
dish: Dish;
id: number;
comment: Comment;
errMess: string;
avgstars: string;
numcomments: number;
favorite: boolean;
showComments: boolean = false;
cardImage: View;
commentList: View;
cardLayout: View;
constructor(private dishservice: DishService,
private route: ActivatedRoute,
private routerExtensions: RouterExtensions,
private favoriteService: FavoriteService,
private vcRef: ViewContainerRef,
private modalService: ModalDialogService,
private http: HttpClient,
private page: Page,
#Inject('baseURL') private baseURL) { }
ngOnInit() {
this.route.params
.pipe(switchMap((params: Params) => this.dishservice.getDish(params['id'])))
.subscribe(dish => {
this.dish = dish
this.favorite = this.favoriteService.isFavorite(this.dish.id);
//compute the average of the comments and the render the length of the array
const commentIds = Object.keys(this.dish.comments);
this.numcomments = commentIds.length;
let total = 0;
commentIds.forEach(commentId => total += this.dish.comments[commentId].rating);
this.avgstars = (total/this.numcomments).toFixed(2) || null;
},
errmess => this.errMess = <any>errmess);
}
onDrawerButtonTap(): void {
const sideDrawer = <RadSideDrawer>app.getRootView();
sideDrawer.showDrawer();
}
**Html template file**
<Label row="1" height="40" class="p-10 m-t-10 h3" text="Comments"></Label>
<ListView id="commentList"
row="2"
height="300"
[items]="dish.comments"
class="list-group p-10"
[visibility]="(showComments) ? 'visible': 'collapsed'">
<ng-template let-comment="item">
<StackLayout class="list-group-item">
<Label class="list-group-item-heading" [text]="comment.comment" textWrap="true"></Label>
<StackLayout orientation="horizontal">
<Label class="list-group-item-text" [text]="comment.rating"></Label>
<Label class="list-group-item-text" text=" Stars"></Label>
</StackLayout>
<StackLayout orientation="horizontal">
<Label class="list-group-item-text" [text]="'-- ' + comment.author + ', '"></Label>
<Label class="list-group-item-text" [text]="comment.date | date"></Label>
</StackLayout>
</StackLayout>
</ng-template>
</ListView>
What am I doing wrong in my code?
I would highly appreciate your review and correction.
I have marketing "campaign" in application.
There is also table of all campaigns which user can edit.
After editing campaign and click Save there in table of campaign appear duplication of that campaign.
After refresh the page that duplication of campaign disappears.
That means there is duplicate of campaign which is not saved in database, only in that table (list) which after refreshing page disappears.
Expected result: On save editing campaign there shouldn't appear duplication of campaign.
Does someone have some idea what can be the problem?
Here is typescript file of list component:
export class NanoCampaignListComponent implements OnChanges {
#Input() advertiserId: string;
#Input() advertiserIoId: string;
#Input() insertionOrderTargeting: string;
// public advertiserId: string;
// public advertiserIoId: string;
public campaignListDto: CampaignListDto;
public campaignListDtoData: CampaignListDtoData;
public columnsArray = TABLE_COLUMNS;
private subscription: Subscription;
constructor(private campaignModel: CampaignModel,
private modalModel: ModalModel,
private activatedRoute: ActivatedRoute) {
// this.subscribeToRouteParamsChange();
}
public ngOnChanges(): void {
this.getListDto();
this.getListDtoData();
}
public getCampaignModel(): CampaignModel {
return this.campaignModel;
}
// public ngOnDestroy() {
// this.subscription.unsubscribe();
// }
public openModal(id: string): void {
const data = {
id: id,
advertiserId: this.advertiserId,
insertionOrderId: this.advertiserIoId,
insertionOrderTargeting: this.insertionOrderTargeting
};
this.modalModel.add(CampaignModel.ENTITY_NAME, data);
}
private getListDto(): void {
this.campaignListDto = this.campaignModel.getListDto(this.advertiserIoId);
// this.campaignListDto = this.campaignModel.getListDto(this.advertiserId);
}
private getListDtoData(): void {
this.campaignModel
.getListDtoData(this.campaignListDto)
.catch(error => console.log(error))
}
Here is the INITIAL list component.html for table:
<div class="nano-f nano-f-c">
<div class="nano-f-40 nano-f-r">
<nano-add-new-button class="nano-bc-yellow hover-effect" (click)="openModal('new')">
</nano-add-new-button>
</div>
<nano-table *ngIf="campaignListDto.isLoaded === true"
[#fadeIn]="'in'"
[tableList]="campaignListDto.list"
[columnsArray]="columnsArray"
[pageCount]="5"
[sortField]="'impressions'"
[sortInverse]="true">
<ng-template let-campaignListDtoData="item">
<div class="nano-table-entity-name-cell">
<span [tooltip]="campaignListDtoData.name"
[routerLink]="['/private/advertiser/' + advertiserId + + '/advertiserIo/' + advertiserIoId +'/campaign/' + campaignListDtoData.id]"
class="nano-c-p">
{{ campaignListDtoData.name }}
</span>
<span>
{{ campaignListDtoData.id }}
</span>
</div>
<div class="nano-table-number-cell">
<span>
{{ campaignListDtoData.revenue | number:'.2-2' }}
</span>
</div>
<div class="nano-table-number-cell">
<span>
{{ campaignListDtoData.cost | number:'.2-2' }}
</span>
</div>
<div class="nano-table-number-cell">
<span>
{{ campaignListDtoData.impressions | number }}
</span>
</div>
<div class="nano-table-number-cell">
<span>
{{ campaignListDtoData.clicks | number }}
</span>
</div>
<div class="nano-table-number-cell">
<span>
{{ campaignListDtoData.CTR | number }}
</span>
</div>
<div class="nano-table-number-cell">
<span>
{{ campaignListDtoData.conversions | number }}
</span>
</div>
<div class="nano-table-actions-cell">
<span class="btn-tables-default"
(click)="openModal(campaignListDtoData.id)">
<i class="fa fa-external-link"></i>
</span>
<nano-entity-toggle-button #entityToggleButton>
</nano-entity-toggle-button>
<nano-campaign-duplicate [campaignId]="campaignListDtoData.id">
</nano-campaign-duplicate>
<!--<nano-entity-duplicate-button [entityId]="campaignListDtoData.id"-->
<!--[entityModel]="getCampaignModel()">-->
<!--</nano-entity-duplicate-button>-->
<nano-entity-delete-button [entityId]="campaignListDtoData.id"
[entityName]="campaignListDtoData.name"
[entityParentId]="advertiserIoId"
[entityModel]="getCampaignModel()">
</nano-entity-delete-button>
</div>
<div class="nano-table-number-cell">
<span>
<i class="fa fa-circle"
[class.nano-c-green]="campaignListDtoData.status === 1"
[class.nano-c-red]="campaignListDtoData.status === 2"></i>
</span>
</div>
<nano-campaign-edit *ngIf="entityToggleButton.isOpen"
[campaignId]="campaignListDtoData.id"
[advertiserId]="advertiserId"
[advertiserIoId]="advertiserIoId"
[insertionOrderTargeting]="insertionOrderTargeting"
class="nano-table-preview-cell">
</nano-campaign-edit>
</ng-template>
</nano-table>
<div *ngIf="campaignListDto.isLoaded === true"
class="nano-f-30 nano-f-r">
<span class="nano-m-a5">
Statistics are for last 7 days.
</span>
</div>
<nano-loading-indicator *ngIf="campaignListDto.isLoaded === false"
[#flyIn]="'in'">
</nano-loading-indicator>
nano-campaign-edit comp:
export class NanoCampaignEditComponent implements OnInit {
#Input() advertiserId: string;
#Input() advertiserIoId: string;
#Input() campaignId: string;
#Input() insertionOrderTargeting: string;
#Output() campaignIdChange = new EventEmitter();
#ViewChild('mappingComponent') mappingComponent: NanoMappingComponent;
public campaignDto: CampaignDto;
public entityName = CampaignModel.ENTITY_NAME;
constructor(public campaignModel: CampaignModel) {
}
public ngOnInit(): void {
this.getDto();
this.getDtoData();
}
public onCampaignSave(): void {
this.campaignIdChange.emit(this.campaignDto.id);
this.campaignModel.deliveryLimitationConflictCheck(this.campaignDto.id);
}
// retrive campaign which is clicked to be edited
private getDto(): void {
this.campaignDto = this.campaignModel.getDto(this.campaignId, this.advertiserId, this.advertiserIoId);
}
// retrive all data of campaign which is clicked to be edited
private getDtoData(): void {
this.campaignModel
.getDtoData(this.campaignDto)
.catch(error => console.log(error))
}
nano-campaing-edit- template: (small part of code)
<nano-entity-footer [entityDto]="campaignDto"
[entityModel]="campaignModel"
[parentForm]="campaignForm"
(onEntityDtoSave)="onCampaignSave()">
</nano-entity-footer>
nano-table.ts, here is part of code which maybe can be important for this case:
export class NanoTableComponent {
#ContentChild(TemplateRef) template: TemplateRef<any>;
#Input() columnsArray: Array<NanoTableHeader> = [];
#Input() isAsync: boolean = false;
#Input() nanoTable: NanoTable;
#Input() pageCount: number = 10;
#Input() pageCountOptions: Array<number> = [5, 10, 20];
#Input() sortField: string | null = null;
#Input() sortInverse: boolean = false;
#Input() useQueryParams: boolean = true;
#Input() hasExport: boolean = false;
#Input()
public set tableList(value: Array<any>) {
this._tableList = value;
this.onTableListChange();
}
public get tableList(): Array<any> {
return this._tableList;
}
private _tableList: Array<any> = [];
constructor(private router: Router,
private activatedRoute: ActivatedRoute) {
}
}
nano-table template:
<div *ngIf="tableList.length > 0"
class="nano-table-grid nano-mt-5">
<div class="nano-f-r">
<input type="text" [(ngModel)]="searchWord" class="nano-white-smoke-input" placeholder="Search"/>
<button *ngIf="hasExport === true && isExportInProgress === false"
type="button"
class="nano-f-250 nano-white-smoke-button nano-ml-2"
(click)="exportReport()">
<span>
CSV
</span>
</button>
<nano-loading-indicator *ngIf="isExportInProgress === true"
class="nano-loading-bar-small nano-loading-bar-overlay nano-f-250 "
style="margin-left: 2px"></nano-loading-indicator>
</div>
<div class="nano-table-row nano-table-grid-header">
<div class="{{column.columnClass}}"
*ngFor="let column of columnsArray"
[ngClass]="{'sort-active':sortField === column.columnField}"
(click)="onSortFieldChange(column.columnField)">
<span>
{{column.columnName}}
</span>
<i class="fa"
[class.fa-sort-asc]="sortInverse === true && sortField === column.columnField"
[class.fa-sort-desc]="sortInverse === false && sortField === column.columnField">
</i>
</div>
</div>
<div class="nano-f-c nano-table-row-wrapper"
*ngFor="let item of getFilteredList()">
<div class="nano-table-row">
<ng-template [ngTemplateOutlet]="template"
[ngOutletContext]="{item: item}">
</ng-template>
</div>
</div>
<nano-table-footer [pageNumber]="pageNumber"
[pageCount]="pageCount"
[pageCountOptions]="pageCountOptions"
[length]="getLengthForFooter()"
(onPageNumberChange)="onPageNumberChange($event)"
(onPageCountChange)="onPageCountChange($event)">
</nano-table-footer>
I'm trying to include a reset button on a Reactive form in Angular 5. For all form fields, the reset is working perfectly except for the multiple checkboxes, which are dynamically created.
Actually the reset apparently happens for the checkboxes as well, but the result is not reflected in the view.
service.component.html
<form [formGroup]="f" (ngSubmit)="submit()">
<input type="hidden" id="$key" formControlName="$key">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name" formControlName="name">
</div>
<br/>
<p>Professionals</p>
<div formArrayName="prof">
<div *ngFor="let p of professionals | async; let i = index">
<label class="form-check-label">
<input class="form-check-input" type="checkbox (change)="onChange({name: p.name, id: p.$key}, $event.target.checked)" [checked]="f.controls.prof.value.indexOf(p.name) > -1"/>{{ p.name }}</label>
</div>
<pre>{{ f.value | json }}</pre>
</div>
<br/>
<button class="btn btn-success" type="submit" [disabled]="f?.invalid">Save</button>
<button class="btn btn-secondary" type="button" (click)="resetForm($event.target.checked)">Reset</button>
</form>
service.component.ts
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormControl, FormGroup, Validators, FormArray } from '#angular/forms'
import { AngularFireAuth } from 'angularfire2/auth';
import { Router, ActivatedRoute } from '#angular/router';
import { AngularFireDatabase, FirebaseListObservable, FirebaseObjectObservable} from 'angularfire2/database';
import { Service } from './service';
export class ServiceComponent implements OnInit {
f: FormGroup;
userId: string;
$key: string;
value: any;
services: FirebaseListObservable<Service[]>;
service: FirebaseObjectObservable<Service>;
professionals: FirebaseListObservable<Service[]>;
profs: FirebaseListObservable<Service[]>;
constructor(private db: AngularFireDatabase,
private afAuth: AngularFireAuth,
private route: ActivatedRoute,
private router: Router,
private fb: FormBuilder) {
this.afAuth.authState.subscribe(user => {
if(user) this.userId = user.uid
this.services = db.list(`services/${this.userId}`);
})
this.afAuth.authState.subscribe(user => {
if(user) this.userId = user.uid
this.professionals = this.db.list(`professionals/${this.userId}`);
})
}
ngOnInit() {
// build the form
this.f = this.fb.group({
$key: new FormControl(null),
name: this.fb.control('', Validators.required),
prof: this.fb.array([], Validators.required)
})
}
onChange(name:string, isChecked: boolean) {
const profArr = <FormArray>this.f.controls.prof;
if(isChecked) {
profArr.push(new FormControl(name));
console.log(profArr.value);
} else {
let index = profArr.controls.findIndex(x => x.value == name)
profArr.removeAt(index);
console.log(profArr.value);
}
}
resetForm(){
let profArr = <FormArray>this.f.controls.prof;
this.f.controls.name.setValue('');
profArr.controls.map(x => x.patchValue(false));
this.f.controls.$key.setValue(null);
}
}
service.ts
export class Service {
$key: string;
name: string;
professionals: string[];
}
The result of the code above, displayed by line <pre> {{f.value | json}} </ pre> is:
When I fill out the form:
{
"$key": null,
"name": "Test service",
"prof": [
{
"name": "Ana Marques",
"id": "-LEZwqy3cI3ZoYykonWX"
},
{
"name": "Pedro Campos",
"id": "-LEZz8ksgp_kItb1u7RE"
}
]
}
When I click on Reset button:
{
"$key": null,
"name": "",
"prof": [
false,
false
]
}
But checkboxes are still selected:
What is missing?
I would stop using FormControls to handle what is basically state.
You have some code which loads the professionals property of the component. Just add to that data a checked property and change the type of professionals to Service[]:
this.db.list(`professionals/${this.userId}`)
.subscribe(professionals => {
professionals.forEach(p => p.checked = false);
this.professionals = professional;
});
Btw, you don't have a checked property on your Service type, so either you extend it or transform professionals in something else (i.e. a CheckableService).
The template becomes:
<div *ngFor="let p of professionals; let i = index">
<label class="form-check-label">
<input class="form-check-input" type="checkbox (change)="onChange(p, $event.target.checked)" [checked]="p.checked"/>{{ p.name }}</label>
</div>
And the onChange method becomes:
onChange(professional: Service, isChecked: boolean) {
professional.checked = isChecked;
this.profArr = this.professionals.filter(p => p.checked);
}
It seems a lot cleaner to me (you will need to adjust for the checked parameter not being in the Service type, but the code is simply adaptable). No messing with controls, only data cleanly flowing through your component.
You are not referencing your checkboxes. Give them a name using the index.
<div formArrayName="prof">
<div *ngFor="let p of professionals | async; let i = index">
<label class="form-check-label">
<input [formControlName]="i" class="form-check-input" type="checkbox (change)="onChange({name: p.name, id: p.$key}, $event.target.checked)" [checked]="f.controls.prof.value.indexOf(p.name) > -1"/>{{ p.name }}</label>
</div>
<pre>{{ f.value | json }}</pre>
</div>
This is my checkall checkbox
<input type="checkbox" value="a" (click)="checkAll" [checked]="checkAll">
This i a regular checkbox, i used this whithin an ngfor
<input type="checkbox" value="a" (click)="check(object)" name="checkbox" [checked]="true">
And the checkall checkboxes function
let elements = document.getElementsByTagName('input');
if (this.checkAll) {
this.checkAll = false;
for (let i = 0; i < elements.length; i++) {
if (elements[i].type == 'checkbox') {
elements[i].checked = false;
}
}
}
else....the other way
I am not very experienced in Angular 4 so I am not sure how this problem works. I get the following error;
ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'true'. Current value: 'false'.
Here is my setup. I have a component "menus.component.ts" (MenusComponent) which loads other components using <router-outlet></router-outlet> The error happens at LocalService.placementListShow.value in the code below.
<div class="row">
...
</div>
<!-- end row -->
<div class="row">
<div class="col-md-4 col-sm-4 col-xs-12 col-xl-3" *ngIf="LocalService.placementListShow.value">
<div class="card m-b-20" *ngIf="LocalService.AllPlacements.Loaded && !LocalService.AllPlacements.Loading">
...
<button type="button" class="list-group-item" [ngClass]="{'active': LocalService.AllPlacements.Active.value==placement.id }" (click)="LocalService.AllPlacements.Activate(placement.id)" *ngFor="let placement of LocalService.AllPlacements.Placements">{{placement.title}}</button>
...
</div>
<single-element-loader *ngIf="LocalService.AllPlacements.Loading"></single-element-loader>
</div><!-- end col-->
<div class="col-md-4 col-sm-4 col-xs-12 col-xl-3" *ngIf="LocalService.menuListShow.value">
...
<a [routerLink]="[menu.id]" class="list-group-item" [ngClass]="{'active': LocalService.PlacementMenus.Active.value==menu.id }" (click)="LocalService.PlacementMenus.Activate(menu.id)" *ngFor="let menu of LocalService.PlacementMenus.Menus">{{menu.title}}</a>
...
<single-element-loader *ngIf="LocalService.PlacementMenus.Loading"></single-element-loader>
</div><!-- end col-->
<div class="col-md-8 col-sm-8 col-xs-12 col-xl-9">
<router-outlet></router-outlet>
</div><!-- end col-->
</div>
The idea is that the component will load child components using Angular router. I want to control the visibility of certain widgets of the main component in the child components so I have setup a local service.
#Injectable()
export class LocalService {
menuListShow = new BehaviorSubject(false);
placementListShow = new BehaviorSubject(true);
Menu: Menu = new Menu();
AllPlacements: AllPlacements = new AllPlacements();
PlacementMenus: PlacementMenus = new PlacementMenus();
constructor(
private MenuService: MenuService,
private route: ActivatedRoute,
) {
}
changeMenuComponents(componentName: string): void {
alert ("Changing to: "+ componentName)
let menuState = {
'placementList': (that): void => {
that.menuListShow.next(false);
that.placementListShow.next(true);
},
'menuList': (that): void => {
that.placementListShow.next(false);
that.menuListShow.next(true);
}
};
menuState[componentName](this);
}
}
For example. I have a MenuComponent and EditLinkComponent which will be loaded in the MenusComponent. There are 2 widgets which I would like to show depending on what component is loaded in the main component. But using the service I get the error above.
The following is not so important but to give you more idea about what I am trying to do.
I would like to show a menu placement listing when the user is seeing the index of the MenusComponent and when I click on the menu placement it should show menus in that placement and when I click on the menu it should show links in the menu. When I click on edit link it should show the EditLinkComponent. This happens through Angular router for example; #cms/menus then #cms/menus/{menuid} then #cms/menus/{menuid}/links/{linkid}/edit
The problem is if I refresh at; #cms/menus/{menuid}/links/{linkid}/edit I have get the error;
ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'true'. Current value: 'false'.
And I think this has to do with attempting to load the placement object and menu object from the server.
Here's how to fix it (you need to manually trigger a change detection):
#Injectable()
export class LocalService {
menuListShow = new BehaviorSubject(false);
placementListShow = new BehaviorSubject(true);
Menu: Menu = new Menu();
AllPlacements: AllPlacements = new AllPlacements();
PlacementMenus: PlacementMenus = new PlacementMenus();
constructor(
private changeDetectorRef: ChangeDetectorRef,
private MenuService: MenuService,
private route: ActivatedRoute,
) {
}
changeMenuComponents(componentName: string): void {
alert ("Changing to: "+ componentName)
let menuState = {
'placementList': (that): void => {
that.menuListShow.next(false);
that.placementListShow.next(true);
},
'menuList': (that): void => {
that.placementListShow.next(false);
that.menuListShow.next(true);
}
};
menuState[componentName](this);
this.changeDetectorRef.detectChanges();
}
}