get result in parent component after selectAll and unSelectAll event emitted from child (Angular)

Viewed 487

Hi I am now working on angular to build a multiselect dropdown using ng-multiselect-dropdown(https://www.npmjs.com/package/ng-multiselect-dropdown).

I used parent-child component communication through event emitter: in child.component.ts:

import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {IDropdownSettings} from 'ng-multiselect-dropdown';

export interface IMultiDropdownConfig {
  placeholder: string;
  header: string;
  dropdownSettings: IDropdownSettings;
}

@Component({
  selector: 'app-multi-dropdown',
  templateUrl: './multi-dropdown.component.html',
  styleUrls: ['./multi-dropdown.component.scss']
})
export class MultiDropdownComponent implements OnInit {

  @Input() dropdownItems: any[];

  @Input() selectedItems;

  @Input() header: string;

  @Input() placeholder: string;

  @Input() dropdownSettings: IDropdownSettings;

  @Input() loading;

  @Output() itemSelectEvent = new EventEmitter();

  @Output() itemDeselectEvent = new EventEmitter();

  @Output() selectAllEvent = new EventEmitter();

  @Output() deselectAllEvent = new EventEmitter();

  @Output() selectedItemsChange = new EventEmitter();

  constructor() { }

  ngOnInit(): void {
  }

  onSelectItem(event) {
    this.selectedItemsChange.emit(this.selectedItems);
  }
  onDeselectItem(event) {
    this.selectedItemsChange.emit(this.selectedItems);
  }
  onSelectAll(event) {
    this.selectedItemsChange.emit(this.selectedItems);
  }

  onDeselectAll(event) {
    this.selectedItemsChange.emit(this.selectedItems);
  }
}

in child.component.html:

<div class="multi-dropdown-item">
  <div class="multi-dropdown-header">{{header}}</div>
  <div *ngIf="!this.loading" class="multi-dropdown-body">
    <ng-multiselect-dropdown
      [placeholder]="placeholder"
      [data]="dropdownItems"
      [(ngModel)]="selectedItems"
      [settings]="dropdownSettings"
      (onSelect)="onSelectItem($event)"
      (onDeSelect)="onDeselectItem($event)"
      (onSelectAll)="onSelectAll($event)"
      (onDeSelectAll)="onDeselectAll($event)">
    </ng-multiselect-dropdown>
  </div>
</div>

Then in parent.component.html:

          <app-multi-dropdown
            [loading]="filterPropertiesMap.get(filterEntry).loading"
            [dropdownItems]="filterPropertiesMap.get(filterEntry).items"
            [(selectedItems)]="filterPropertiesMap.get(filterEntry).selectedItems"
            [dropdownSettings]="filterPropertiesMap.get(filterEntry).setting"
            [placeholder]="filterPropertiesMap.get(filterEntry).name"
            [header]="filterPropertiesMap.get(filterEntry).name"
            (itemSelectEvent)="onItemSelect($event)"
            (itemDeselectEvent)="onItemDeselect($event)"
            (selectAllEvent)="onSelectAll($event)"
            (deselectAllEvent)="onDeselectAll($event)"

          ></app-multi-dropdown>

in parent.component.ts I didn't do anything but log:

  onItemSelect($event) {
    console.log("onItemSelect");

  }
  onItemDeselect($event) {
    console.log("onItemDeselect");

  }

  onSelectAll($event) {
    console.log("onSelectAll");

  }

  onDeselectAll($event) {
    console.log("onDeselectAll");

  }

in above code filterPropertiesMap defines settings. You may see that what I am doing is in child component, I created eventemitters for select, deselect, and in the function I emitt this.selectedItems.

But I don't think this is a good way to implement this, and actually, it doesn't work well. sometimes, when I select or deselect, it doesn't changed immediately.

So how to implement this? when I select deselect, selectAll, deselectAll. my parent component can react immediately and correctly.

Also the weird thing is: when I load the page, I will have some default values chose, for example 6,7,8,9. Then I select all and it still 6,7,8,9. and then after that I deselect all agin select all, the field will change to all(for example 1,2,3,4,5,6,7,8,9). Does event emitter has delay or will ignore some choices??

Edit:

I tried to extract all the necessary snippets of code to build a project here: https://drive.google.com/file/d/1BlV2EtdwZhqqpkdiC0_mlaw_r3w6Bder/view?usp=sharing

I hope when I all the event(select, deselect, selectAll, deselectAll) can be emitted and received by parent component

sorry one mistake: the app-multi-dropdown tag in parent component should be app-child

2 Answers

You can use it as part of an Angular reactive form, and just emit every time it value changes.

The HTML of the child component could be something like this:

<form [formGroup]="myForm">
    <ng-multiselect-dropdown
        name="dropdownItems"
        [settings]="dropdownSettings"
        [placeholder]="placeholder"
        [data]="dropdownItems"
        formControlName="dropdownItems">
   </ng-multiselect-dropdown>
</form>

And the .ts file:

import { IDropdownSettings } from 'ng-multiselect-dropdown';
import { FormGroup, FormBuilder } from '@angular/forms';
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';

@Component({
    selector: 'app-child',
    templateUrl: './child.component.html',
    styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit {

    @Input() placeholder: string;
    @Input() defaultValues: any[];
    @Input() dropdownItems: any[];
    @Input() dropdownSettings: IDropdownSettings;
    @Output() onDropdownChange = new EventEmitter();
    
    myForm: FormGroup;
    
    constructor(
        private _fb: FormBuilder
    ) { }

    ngOnInit(): void {
        this.myForm = this._fb.group({
            dropdownItems: ['']
        });

        // Set default values as real values
        this.onDropdownChange.emit(this.defaultValues);

        this.myForm.valueChanges.subscribe(val => {
            this.onDropdownChange.emit(val.dropdownItems)
        })
    }

}

EDIT: Based on your last update:

With the code you updated and my peace of code, I build this functional project running on stackblitz: https://stackblitz.com/edit/angular-ivy-g7w3dz

Hope I got the logic properly

you haven't set up your emitter properly. do this:

@Output()selectAllEvent = new EventEmitter<any>();

onSelectAll(event) {
  this.selectAllEvent.emit(this.selectedItems);
}

then on the parent:

<app-multi-dropdown
   (selectAllEvent)="onSelectAll($event)"
></app-multi-dropdown>
Related