0
votes

I built an application settings service for my angular app local storage stuff. I want to create, validate, get and set all top level local storage settings from here.

This works as expect so far, but now I want change detection in the service to listen for alterations to the setting config objects that exist in the service.

I basically want to use onChanges in a service.

For example:

SettingService:

constructor() {
   this.set_AppSettings();
}

public app_Settings: AppSettingsConfig = {
   appConfig: {
     showSideBar: null,
     collapsedSideBar: null,
     lastRoute: null,
     showAppIDBar: null,
     isDarkTheme: null
   }
};

// Methods that handle localstorage stuff.

AppComponent.ts:

constructor(
    public _settings: SettingsService
) {}

toggle() {
  _settings.app_Settings.appConfig.showSideBar = !_settings.app_Settings.appConfig.showSideBar
}

AppComponent.html:

<button mat-flat-button (click)="toggle()"
     Toggle Side Bar
</button>

I want to detect that this value has changed in my Settings Service and then fire the appropriate method to set the new value to local storage.

I don't want to call the method that sets the new local storage value from my service, every time I change a variable in a component. I feel like that would be unnecessary if I can just listen to the changes in the service.

Is this achievable or should I change my approach?

2

2 Answers

1
votes

there is no ngOnChanges on services, What I would do is:

service.ts

private defaultConfig : AppSettingsConfig = {
   appConfig: {
     showSideBar: null,
     collapsedSideBar: null,
     lastRoute: null,
     showAppIDBar: null,
     isDarkTheme: null
   }
};
private settings : BehaviorSubject<AppSettingsConfig> = new BehaviorSubject<AppSettingsConfig>(this.defaultConfig);
private app_Settings: AppSettingsConfig = defaultConfig;

settings$ : Observable<AppSettingsConfig>;

constructor() {
   this.settings$ = this.settings.asObservable();
}

updateConfig(val: Partial<AppSettingsConfig>) {
   this.app_Settings = {...this.app_Settings, ...val };
   this.settings.next(this.app_Settings);
}

app.component

private settings: AppSettingsConfig;

constructor(
    public settingService: SettingsService
) {}

ngOnInit() {
  this.settingService.subscribe((settings) => this.settings = settings);
}

toggle() {
  _settings.updateSettings({ appConfig : { showSideBar : !this.settings.appConfig.showSideBar } } ); 
}

in other.components



constructor(
    public settingService: SettingsService
) {}

ngOnInit() {
  this.settingService.subscribe((settings) => { ... do something with settings });
}
1
votes

You can use a BehaviorSubject() which will emit changes to the settings object, and also make it easy to patch modifications. The component can use selector to listen for specific values, and can call methods to patch values.

@Injectable()
export class SettingsService {
    private _settings = new BehaviorSubject<AppSettingsConfig>({
        showSideBar: false,
        collapsedSideBar: false,
        lastRoute: null,
        showAppIDBar: null,
        isDarkTheme: null
   });

   public get settings$(): Observable<AppSettingsConfig> {
      return this._settings.asObservable();
   }

   public get snapshot(): AppSettingsConfig {
      return this._settings.getValue();
   }

   public selectShowSideBar(): Observable<boolean> {
        return this._settings.pipe(
           map(state => state.showSideBar),
           distinctUntilChanged()
        );
   }

   public setShowSideBar(showSideBar: boolean) {
       this.patch({showSideBar});
   }

   private patch(value: Partial<AppSettingsConfig>) {
      this._settings.next({...this._settings.getValue(), ...value});
   }
}

The _settings uses a behavior subject to emit changes to the settings. You call selectShowSideBar() to create a selector that emits only the showSideBar property, and call setShowSideBar() to patch the state with new values. You can add additional methods as you need them.

The component would use the service like this:

public showSideBar$: Observable<boolean> = this._settings.selectShowSideBar();

constructor(public _settings: SettingsService) {}

toggle() {
  this._settings.setShowSideBar(!this._settings.snapshot.showSideBar);
}

onInit() {
   this.showSideBar$.subscribe(value => console.log('value changed', value);
}