Miomir Dancevic
Miomir Dancevic

Reputation: 6852

Shared service example Angular 5

I know this question has been asked several times, but problem is that nobody tried to make a some fiddle or show results of code. This is what i have, i need to update values in other component based on value in some other component, but that is not just value,I have call function again in some other component. I have some component that goes to database and update values, on second hand I have other component that read those values from database from service. This is example of my code

tasks.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import { environment } from '../../environments/environment';
import { Tasks } from './tasks';

@Injectable()
export class TasksProvider {

    constructor(private http: HttpClient) { }

    createNewTask(name: Name) : Observable<any> {
        return this.http.post(environment.apiUri + 'tasks', { name, finished: false },
         { responseType: 'text' });
    }

    updateTask(id: Id, name: Name, finished: boolean) : Observable<any> {
        return this.http.put(environment.apiUri + 'tasks/' + id, { name, finished },
         { responseType: 'text' });
    }

    getAllTasks(): Observable<Tasks[]> {
        return this.http.get(environment.apiUri + 'tasks')
            .map<any, Tasks[]>(data => data.map(Tasks.fromObject));
    }
}

app.component.html

<app-tasks-list></app-tasks-list>
<app-tasks-add-new></app-tasks-add-new>

As you may see I have not child components, that is my main problem

tasks-list.component.ts

import {Component} from '@angular/core';

import { Tasks } from '../services/tasks';
import { TasksProvider } from '../services/tasks.service'; 

@Component({
  selector: 'app-tasks-list',
  templateUrl: './tasks-list.component.html',
  styleUrls: ['./tasks-list.component.scss']
})

export class TasksListComponent {
  tasks: Array<Tasks>;

  constructor(private tasksProvider: TasksProvider) { }

  ngOnInit() { 
    this.getTasksList();
  }

  displayedColumns: string[] = ['id', 'name', 'finished'];
  private getTasksList() {
        this.tasksProvider.getAllTasks()
            .subscribe(tasks => {
                this.tasks = tasks;
            });
  } 

  public updateCheckboxValue(id: number, name: string, event: any){
    this.tasksProvider.updateTask(id, name, event.checked).subscribe(
      result => {},
      () => {
        alert('Something went wrong');
      })
  }
}

tasks-add-new.component.ts

import { Component, OnInit, Inject } from '@angular/core';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';

import { Tasks } from '../services/tasks';
import { TasksProvider } from '../services/tasks.service'; 

export interface DialogData {
  name: string;
}

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

  ngOnInit() {
  }

  constructor(public dialog: MatDialog, private tasksProvider: TasksProvider) {}

  openDialog(): void {
    const dialogRef = this.dialog.open(TasksAddNewDialog, {
      width: '250px',
      data: {name: this.animal}
    });

    dialogRef.afterClosed().subscribe(result => {
      this.name = result
      this.tasksProvider.createNewTask(this.name).subscribe(
        result => {},
        () => {
          alert('Something went wrong');
        })
  }
}
}
@Component({
  selector: 'tasks-add-new-dialog',
  templateUrl: 'tasks-add-new-dialog.html'
})

export class TasksAddNewDialog {
  constructor(
    public dialogRef: MatDialogRef<TasksAddNewDialog>,
    @Inject(MAT_DIALOG_DATA) public data: DialogData) {}

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

You see now when i call function in tasks-add-new.component.ts like

 this.tasksProvider.createNewTask(this.name).subscribe(
        result => {},
        () => {
          alert('Something went wrong');
        })

I need to call again function in tasks-list.component.ts

  private getTasksList() {
        this.tasksProvider.getAllTasks()
            .subscribe(tasks => {
                this.tasks = tasks;
            });
  } 

Does any body have idea how i can do that the best practice?

Upvotes: 0

Views: 508

Answers (1)

dhilt
dhilt

Reputation: 20744

On of the possible approach is to use Subjects.

1) Store task list on the service and provide subscribable Subject

private tasks: Array<Task>;
public $tasks: BehaviorSubject<Array<Task>>;

constructor(private http: HttpClient) {
  this.$tasks = new BehaviorSubject([]);
  ...
}

getAllTasks() {
 this.http.get(environment.apiUri + 'tasks')
    .subscribe(data => {
      this.tasks = data;
      this.$tasks.next(this.tasks);
    });
}

updateTask(params) {
  this.http.post(/* params */).subscribe((task) => {
    this.tasks = this.tasks.map(t => t.id !== task.id ? t : task);
    this.$tasks.next(this.tasks);
  });
}

createTask(...) {
  // again, do a request, update this.tasks and call $tasks.next
  ...
}

2) Make one service Subject subscription on the component instead of multiple service methods Observable listeners and update component's list automatically each time the service source has been changed

tasks: Array<Tasks>;

constructor(private tasksProvider: TasksProvider) {
  this.tasksProvider.$tasks.subscribe(tasks => this.tasks = tasks);
}

ngOnInit() { 
  this.tasksProvider.getAllTasks();
}

public updateCheckboxValue(id: number, name: string, event: any){
  this.tasksProvider.updateTask(id, name, event.checked);
}

Upvotes: 1

Related Questions