1

I am slowing migrating from AngularJS to Angular(2+) and just stumbled across something.

In AngularJS, I used a lot of factories and services for sharing data across directives, controllers and other services. It is easy to update the service in one place and have it update everywhere else automatically.

However, I am trying to use a service in a similar manner in Angular 5 and "nothing is happening" when I change the service variables.

I've seen some solutions that involve creating functions that "pull" the new data or suggestions to update the "Angular change service" to bind events to the variable.

However, my app has many variables used in many locations. It doesn't seem correct that I have to subscribe to every single variable separately, within every single component that uses them, and change the service to emit a change for every single one.

Am I just missing something?

Thanks!!! Wayne

For Example:

A component for a home page.

import { Component, OnInit } from '@angular/core';
import { HomeButtonDirective } from '../home-button.directive';

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

  constructor() { }

  ngOnInit() {
  }

}

The directive for a button that is reused on the home page:

import { Directive, Input, OnInit, HostListener } from '@angular/core';
import { InfoService } from './info.service';

@Directive({
  selector: '[appHomeButton]',
  providers: [InfoService]
})
export class HomeButtonDirective {
  @HostListener('click', ['$event']) onclick($event) {
    this.info.showHome = false;
  }
  constructor(private info: InfoService) { }

  ngOnInit() {
  }

}

The app component. Will show the home page if showHome===true:

import { Component } from '@angular/core';
import { InfoService } from './info.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  providers: [InfoService]
})
export class AppComponent {
  title = 'Testing a service';
  showHome = true;

  constructor(private info: InfoService) {
    this.showHome = this.info.showHome; // this works...showHome becomes false per the service (see below)
  }

 }

And finally, the service:

import { getTestBed } from '@angular/core/testing';
import { Injectable } from '@angular/core';

@Injectable()

export class InfoService {
    showHome = false;
 }
7
  • Where is your service? Post your code Commented Mar 27, 2018 at 12:55
  • I know we require code to be posted, but Wayne's observations are correct and the question can be answered without additional information/code. Commented Mar 27, 2018 at 12:58
  • Thanks JasonK. The question is code-agnostic. I can post some generic sample code, if it helps, but I didn't see the need to clutter the question with a few components, a directive and a service Commented Mar 27, 2018 at 13:04
  • @JasonK - Then you must be clairvoyant. There are many possibilities as to why "nothing happens" when a variable is "updated". Maybe a local copy is made of the data at the controller level, maybe the service is declared in multiple modules which results in multiple instances of the service instead of a singleton, maybe the data is not updated but an observable is or visa-versa, maybe the update does not actually change the service copy.... the list goes on. Commented Mar 27, 2018 at 13:10
  • @Igor Agreed. I'll try to post an answer to what I think is the problem. Commented Mar 27, 2018 at 13:11

3 Answers 3

5

It is easy to update the service in one place and have it update everywhere else automatically.?

Register the service with an Angular module rather than a component.

from Angular docs

Angular module providers (@NgModule.providers) are registered with the application's root injector. Angular can inject the corresponding services in any class it creates. Once created, a service instance lives for the life of the app and Angular injects this one service instance in every class that needs it.

To summarize

If we want an instance of a dependency to be shared globally and share state across the application we configure it on the NgModule.

If we want a separate instance of a dependency to be shared across each instance of a component and it’s children we configure it on the components providers property.

Sign up to request clarification or add additional context in comments.

1 Comment

You hit the nail on the head!!!! That is exactly what I was unclear about. Changing the code accordingly fixed it! I removed "providers:[InfoService]" from each of the classes and added it to my list of providers in app.module.ts
1

I had the same observations when migrating from AngularJS to Angular.

The problem

I'll use some sample code to describe the problem. Let's say we're building a CommentComponent to show comments, and the data is coming from a service named CommentService. We would then write something along the lines of:

this.comments: Array<Comment> = this.commentsService.commments;

This works perfectly fine during initialization. However, when data changes in the service (for instance when a new comment is pushed to the array), we won't see the changes reflect to our component. This is because the code above is just a direct (one-time) assignment inside the constructor, meaning there is no two-way binding.

Possible solutions

There are multiple solutions to tackle the abovementioned problem. I'll share the ones that I'm aware of, but feel free to expand the answer.


Using Observables

In this case I prefer using BehaviorSubject as it will immediately return the initial value or the current value on subscription.

private comments$: BehaviorSubject<Comment> = new BehaviorSubject([]);

... and subscribe to the observable in your component or whatever place you need the changes to be reflected:

this.commentsService.subscribe(comments => this.comments = comments);

Referencing from templates

Another solution (or workaround) is to reference to the service property directly from the template, which allows for two-way binding:

<div *ngFor="let comment of commentsService.comments">
    {{ comment | json }}
</div>

Change Detection Strategy

Another solution might be to adjust the Change Detection Strategy.

Comments

0

in my experience, the variable you said may be an object, and you change the value of its property but itself.

you should change the object itself instead of changing his property.

so, if you want to change a complex object of a service, you'd better code looks like this:

this.oldObject = anotherObject

duplicate the old object to a new one if necessary.

Hope it's helpful.

1 Comment

My service has many variables. In the snippet above, the variable that isn't changing is a boolean.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.