0

I have two components and a service in angular 4, the component one add a contact and the component two shows the contacts. I'm using HttpClient and Observables to get all the contacts, but when I add one contact my second component is not updating the contacts. I do it all this stuff through a service.The contacts displays well on start but they are not updating when I fire add() function.

My contact.service.ts

@Injectable()
export class ContactService {

  constructor(private http: HttpClient, private messageService: MessageService) { }

  private contactsUrl = 'api/contacts';

  getContacts(): Observable<Contact[]> {
      return this.http.get<Contact[]>(this.contactsUrl)
        .pipe(
            tap(contacts => this.log(`Fetched contacts`)),
            catchError(this.handleError('getContacts', []))
        );
  }


  getContact(id: number): Observable<Contact> {
      const url = `${this.contactsUrl}/${id}`;
      return this.http.get<Contact>(url).pipe(
      tap(_ => this.log(`fetched contact id=${id}`)),
      catchError(this.handleError<Contact>(`getContact id=${id}`))
    );
  }

  addContact (contact: Contact): Observable<Contact> {
      return this.http.post<Contact>(this.contactsUrl, contact, httpOptions).pipe(
          tap((contact: Contact) => this.log(`${contact.name} added to list`)),
          catchError(this.handleError<Contact>('addContact'))
      );

  }

}

My contact-item.component.ts

   export class ContactItemComponent implements OnInit {

  contacts: Contact[] = [];

  constructor(private contactService: ContactService) { }


  ngOnInit(){
      console.log("init");
      this.getContacts();
  }

  getContacts(): void {
      this.contactService.getContacts().subscribe(contacts => this.contacts = contacts);
  }

}

My contact-add.component.ts

export class ContactAddComponent {

  contacts: Contact[] = [];

  constructor(private contactService: ContactService) { }
 add(name: string, surname: string, phoneNumber: number): void {
      name = name.trim();
      surname = surname.trim();
      phoneNumber = phoneNumber;

      if(!name || !surname || !phoneNumber) { return; }
      this.contactService.addContact({ name, surname, phoneNumber } as Contact)
        .subscribe(contact => {
            this.contacts.push(contact);
        })
  }
Sergio
  • 127
  • 3
  • 12

3 Answers3

2

Sergio, when you add a "contact", you make: 1) add the contact in dbs, 2.-add the contact to the array contact in the contact-add.component, nothing else. You must do "something more" to contact-item.component take acount the change.

When we subscribe to contactService.getContact() ONLY make "getContact" one time.

Solutions:

1.- the service store the contact, and we use a getter to get/set the values In your contactService

@Injectable()
export class ContactService {

  contacts: Contact[];  //declare a variable contacts
  private contactsUrl = 'api/contacts';

  constructor(private http: HttpClient, private messageService: MessageService) { }

  getContacts(): Observable<Contact[]> {
      if (this.contacts)   //If yet exist, simply
          return Observable.of(this.contacts);

      return this.http.get<Contact[]>(this.contactsUrl)
        .pipe(
            tap(contacts =>{
                this.log(`Fetched contacts`)
            }),
            catchError(this.handleError('getContacts', []))
        );
  }

  getContact(id: number): Observable<Contact> {
     ...
  }

  addContact (contact: Contact): Observable<Contact> {
      ...    
  }

}

Then, you can have in your contact-items.component

  export class ContactItemComponent implements OnInit {
  //contacts is a "getter", not a variable
  get contacts(){
        return this.contactService.contacts;
  }
  set contacts(value)
  {
      this.contactService.contacts=value;
  }
  constructor(private contactService: ContactService) { }

  ngOnInit(){
      this.getContacts();
  }

  getContacts(): void {
      this.contactService.getContacts().subscribe((contacts)=>{
           //see that really you're changing this.contactService.contacts
            this.contacts=contacts;
      })
  }

And in your ContactAdd-component

export class ContactAddComponent {
      //yes, in your contactAddComponent you have a getter
      //contacts is a "getter", not a variable
      get contacts(){
            return this.contactService.contacts;
      }
      set contacts(value)
      {
          this.contactService.contacts=value;
      }

  constructor(private contactService: ContactService) { }
 add(name: string, surname: string, phoneNumber: number): void {
      name = name.trim();
      surname = surname.trim();
      phoneNumber = phoneNumber;

      if(!name || !surname || !phoneNumber) { return; }
      this.contactService.addContact({ name, surname, phoneNumber } as Contact)
        .subscribe(contact => {
            //really you push contact of your contactService.contact
            this.contacts.push(contact);
        })
  }

2.-Make a subscribe to a Observable in your service as Andrey indicate you.

Your service

@Injectable()
export class ContactService {

  constructor(private http: HttpClient, private messageService: MessageService) { }
  //declara a Subject
  private contactsSource = new Subject<Contact>();

  // Declare a observable
  contactsObservable = this.contactsSource.asObservable();

  private contactsUrl = 'api/contacts';

  getContacts(): Observable<Contact[]> {
     ...
  }
  getContact(id: number): Observable<Contact> {
      ...
  }

  addContact (contact: Contact): Observable<Contact> {
      return this.http.post<Contact>(this.contactsUrl, contact, httpOptions).pipe(
          tap((contact: Contact) => {
              this.log(`${contact.name} added to list`)
              this.contactSource.next(contact) //<--say that contactObservable "change"
            }),
          catchError(this.handleError<Contact>('addContact'))
      );
  }
}

then, your contacts-item.component subcribe the observable

export class ContactItemComponent implements OnInit {

  contacts: Contact[] = [];

  constructor(private contactService: ContactService) { }


  ngOnInit(){
      console.log("init");
      this.getContacts();
      this.subscribeChange();
  }

  getContacts(): void {
      this.contactService.getContacts().subscribe(contacts => this.contacts = contacts);
  }
  subscribeChange():void{
      this.contactService.contactsObservable.subscribe(
            contact=> this.contact.push(contact))
  }
}

As you can see, http.get is a "special observable" that have NOT a "next" to change

Eliseo
  • 50,109
  • 4
  • 29
  • 67
0

You need a way to communicate one component with other. The solution can be to use BehaviorSubject. Here is an example BehaviorSubject vs Observable?

ContactService

1) You need to create BehaviorSubject that will keep the last value of contacts.

    private contacts: BehaviorSubject<Contact[]> = new BehaviorSubject<Contact[]>([]);

2) If need to add new value to contacts, need to call next method with updated contacts on BehaviorSubject object.

    this.contacts.next(updatedContacts);

3) In ContactItemComponent you need to subscribe on BehaviorSubject contacts. And when next method is called you will get updated contacts

akushylun
  • 151
  • 1
  • 5
-1

You need to emit event from your contact-add.component.ts so your contact-item.component.ts can fire getContacts() to receive new updatet list of contacts.

More on components interaction you can find here

Joe Belladonna
  • 1,349
  • 14
  • 20