-1

component file:

   // Angular and 3rd party libs imports
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';

import { Store } from '@ngrx/store';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
// Utils
import { ApiLoadInfo, ApiStateEnum } from 'src/app/shared/utils/states';
// Services
import { TestPortalService } from '../../../testportal.service';
import { SharedClient } from 'src/app/shared/services/shared.service';
// Redux
import {
    CandidateInstructionsState,
    Quiz,
    Instruction,
    PageEnum,
    LandingPageData
} from '../redux/candidate-instructions.state';
import * as instructionActions from '../redux/candidate-instructions.action';
import * as instructionSelects from '../redux/candidate-instructions.selector';
import { ActivatedRoute } from '@angular/router';

@UntilDestroy()
@Component({
    selector: 'candidate-instructions-landing',
    templateUrl: './instructions-landing.component.html',
    styleUrls: ['./instructions-landing.component.scss', '../common.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class CandidateInstructionsLandingComponent implements OnInit {
    // Exposing constants to html template
    ApiStateEnum = ApiStateEnum;
    PageEnum = PageEnum;

    // Variables
    initDataLoadState: ApiLoadInfo;
    data: LandingPageData;

    constructor(private _store: Store<CandidateInstructionsState>,
        private _activatedRoute: ActivatedRoute,
        private _testPortalService: TestPortalService,
    ) {
        _store
            .select(instructionSelects.selectInitDataLoadState)
            .pipe(untilDestroyed(this))
            .subscribe((initDataLoadState) => {
        console.log('is same ref?:', this.initDataLoadState === initDataLoadState)
                this.initDataLoadState = initDataLoadState;
        console.log(initDataLoadState)
        console.log('----------')
            });

        _store
            .select(instructionSelects.selectLandingData)
            .pipe(untilDestroyed(this))
            .subscribe((data) => {
                this.data = data;
            });
    }

    ngOnInit() {
    this.loadInstructions();
  }

  loadInstructions() {
        this._store.dispatch(instructionActions.setInitData());  // sets state to 'loading'
        this._testPortalService.getTestInstructions(
            this._activatedRoute.snapshot.params.quizOrInviteId,
            (error, response) => {
                if (error) {
          // sets state to 'error'
                    this._store.dispatch(instructionActions.setInitDataFail({ errmsg: error.toString() }));
                } else {
          // sets state to 'loaded'
                    this._store.dispatch(instructionActions.setInitDataSuccess({ instructions: response }));
                    console.log(response);
                }
            }
        );
    }
}

html:

{{ initDataLoadState.state }}

console output: enter image description here

ui: enter image description here

I thought when onPush is set, the template will re-render if the variable ref is changed. And since redux store is immutable that is always supposed to happen (confirmed by logging in the console). But still the actual component data is not in sync with the UI ie. component value = "loaded" but value in ui = "loading". Why is it so?

  • Have a look at the [NGRX push pipe](https://ngrx.io/guide/component/push) – Tino Oct 28 '21 at 05:50
  • Ya looks like it will do what i want but is there any way to achieve the same behavior in the .ts file rather than html? Asking because sometimes i need to subscribe in the ts file and write some code to manage 3rd party libs. Ex: if card status changes from closed to open -> need to fire a function to open the card which is a jquery component. – Rahul R Badenkal Oct 28 '21 at 06:28

1 Answers1

1

If you don't want to or can't use the pushPipe you could do something like this to subscribe to the store data:

import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { Store } from '@ngrx/store';

import { getData } from 'path/to/store';
import { YourType } from 'path/to/type';

@Component({
  selector: 'subscribing-component',
  templateUrl: './subscribing.component.html'
})
export class SubscribingComponent implements OnInit, OnDestroy {
  data: YourType;
  dataSubscription: Subscription;

  constructor(store: Store) {}

  ngOnInit(): void {
    this.dataSubscription = this.store.select(getData).subscribe((data) => {
      this.data = data;
    });
  }

  // don't forget to unsubscribe
  ngOnDestroy(): void {
    if (this.dataSubscription) {
      this.dataSubscription.unsubscribe();
    }
  }
}
Tino
  • 646
  • 2
  • 9
  • 15
  • Sry, isn't this what I was also doing? But it was still not updating dom when onPush was set – Rahul R Badenkal Oct 28 '21 at 09:11
  • Did your move your subscription handling from the constructor method to `ngOnInit` as my answer suggests? If yes, does this work for you? – Tino Oct 28 '21 at 10:58
  • Didn't noticed that you moved the subscription to ngOnInt, tried that but still not working with onPush on – Rahul R Badenkal Oct 28 '21 at 14:26
  • Yes, that is correct. OnPush is not the right change detection for your type of component. When you check out the [documentation](https://angular.io/api/core/ChangeDetectionStrategy) you'll quickly see why: `OnPush` is the right choice for so-called [dumb components](https://dev.to/mquanit/concept-of-smart-dumb-components-in-angular-2fom) – Tino Oct 30 '21 at 09:02