Home > other >  How to pass form value from one component to a list in another component using angular services?
How to pass form value from one component to a list in another component using angular services?

Time:11-22

I have been trying to develop a todo list project. I have four components : Daily, Item-List, Item and Add-task dailog enter image description here

The daily component holds a form and a list that displays the values from form. What I am looking to do next is to have a form in a dialog modal(new separate component) and add its value to the list in the daily component

Below are the codes:

Daily.service.ts

This service code is used to get the value from the form and add it to the list.

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { DailyTask } from './models';

@Injectable({ providedIn: 'root' })
export class DailyService {

private _dailies$ = new BehaviorSubject<DailyTask[]>([]); 
public getDailies(): DailyTask[] {
return this._dailies$.getValue();
}

public setDailies(data: DailyTask[]): void {
 this._dailies$.next(data);
}

public getDailiesObservable(): Observable<DailyTask[]> {
return this._dailies$.asObservable();
}

public createTask(newTask: string): void{

console.log( "check");

  const dailyTask: DailyTask = { task: newTask, type: 'DAILY' };
  this.setDailies([dailyTask, ...this.getDailies()])
 }
}

Daily.component.Ts

export class DailyComponent implements OnInit {
  public dailyList$: Observable<DailyTask[]> | null = null;

  constructor(private _dailyService: DailyService) {}

  ngOnInit(): void {
    this.dailyList$ = this._dailyService.getDailiesObservable();
  }

  public addDaily(name: string) {
     this._dailyService.createTask(name);
  }
 }

Daily.component.html

 <mat-card>
   <h1>Daily</h1>
    <app-item-list
      [type]="'DAILY'"
      [initialData$]="dailyList$"
      [onAddItem]="addDaily.bind(this)"
    ></app-item-list>
 </mat-card>

Item-List.component.html

<div>
 <mat-form-field appearance="outline">
 <input
  matInput
  placeholder="Add a Task"
  (keyup.enter)="addTask()"
  autocomplete="off"
  [formControl]="nameControl"/>
 </mat-form-field>
</div>

<ng-container *ngIf="filteredData$ | async as data">

<app-item
  [value]="value"
  *ngFor="let value of data; index as index"
  (inputDataChange)="removeTask(data, index)"
>
</app-item>
</ng-container>

Item-list.component.ts

export class ItemListComponent implements OnInit {
   
   nameControl = new FormControl('');
   @Input() public type: ItemType | null = null;
   @Input() public onAddItem: Function | null = null;
   
   constructor(private _homeService: HomeService) {}
   ngOnInit(): void {}

   addTask() {
     if (this.onAddItem) {
        this.onAddItem(this.nameControl.value);
        this.nameControl.reset();
   }
 }

item.component.html

<div >
  <div  [ngClass]="{ 'line-through': value.task }">
     {{ value.task | uppercase }}
  </div>
</div>

item.component.ts

export class ItemComponent implements OnInit {
  @Input()
   value: any;
  constructor() {}
  ngOnInit(): void {}
 }

The above set of codes works perfect, below the code for new component task-dailog

task-dailog.html

<div mat-dialog-title >
   <h1 >Create Daily</h1>
</div>

<div mat-dialog-content>
    <div >
      <p>Task</p>
    <mat-form-field appearance="outline">
    <input
        matInput
        placeholder="Add a new Task"
        autocomplete="off"
        (keyup.enter)="addDailogTask()"
        [formControl]="nameControl"
    />
    </mat-form-field>
    </div>
</div>

task-dailog.component.ts

export class TaskDialogComponent implements OnInit {
  nameControl = new FormControl('');

  constructor(
    public dialogRef: MatDialogRef<TaskDialogComponent>,
    private _dailyService: DailyService,
  ) {}

  ngOnInit(): void {}

  onNoClick(): void {
    this.dialogRef.close();
  }

  addDailogTask(){
    const value$ =   this.nameControl.value;
    this.nameControl.reset();
    console.log(value$);
  }
 }

Here in the task-dialog.component.ts, I have the function to get the value from the form, but I am stuck here and dont know how to proceed. I would like to learn how to send this form value from TaskDialogComponent to the list inside DailyComponent.

Here is also the Stackblitz for the project.

Can someone help me with this . I am relatively new to angular and would really appreciate the help. Thanks in advance!.

CodePudding user response:

this._dailyService.createTask(value$); could be called from anywhere as long you use the same service instance. this will trigger this.dailyList$ the observable and update the list too.

export class TaskDialogComponent implements OnInit {
      nameControl = new FormControl('');
      @Input() public onAddDialogItem: Function | null = null;
    
      constructor(
        public dialogRef: MatDialogRef<TaskDialogComponent>,
        private _dailyService: DailyService,
      ) {}
    
      ngOnInit(): void {}
    
      onNoClick(): void {
        this.dialogRef.close();
      }
    
      addDailogTask(){
        const value$ =   this.nameControl.value;

        // you can add it from the service
        this._dailyService.createTask(value$);
        this.nameControl.reset();
        console.log(value$);
      }
     }

CodePudding user response:

You need to cast your form value type first, By default form.value can be return as any that isn't match createTask function that require only string

Solution

addDailogTask() {
  const value$ = this.nameControl.value as string;
  ...
}

CodePudding user response:

So basically you can get the value from the dialog in the afterClosed().subscribe method.

add-task-btn.component.ts:

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


     ...

    dialogRef.afterClosed().subscribe((result) => {
      if (result) {
      this.dailyService.setDailies(`${result}`); // <--<< you can get the value returned from the dialog here.
      } else {
        console.log('The dialog was closed');
      }
    });
  }
}

But I see a major problem with your code and how it is structured.

  1. You want to structure your code in the same way as how your UI is structured. So it would be easier to work with. for example:

    src
      app
      L home
        L components
           L ProfileBar
           L SearchBar
           L add-task-btn
           L ... 
        L services
        L models 
        L home.component.ts
        L home.component.scss
        L home.component.html
    
  2. Not sure why you decided to put each component in a separate module. Which is unnecessary technically the login page could be in one module and everything else that is in the homecomponent could be in another module. That way you can lazyload the HomeModule. Also you have to import the DailyModule in other modules just to use DailyService.

  3. When you have a deeply nested component. I would recommend to use a state management library (I use ngxs) its a lot easier to work with so you don't have to pass values through the @input decorator. Also it would make your code a lot more readable.

  • Related