Search code examples
angulartypescriptobservable

Setting a variable in the component from an observable


I am trying to bind a variable in a component to the content from an observable in a subscribe() function with an anonymous function:

ngOnInit() {
    this.strSub = this._store.select(selectStr).subscribe((data) => console.log(data));
    //this.strSub = this._store.select(selectStr).subscribe((data) => {this.str = data});
}

The uncommented line runs fine while the commented line throws an error on compilation Type 'void' is not assignable to type 'string'..

In other words, this assignment this.str = data somehow breaks the program. I suspect this may be due to data being null at some point during run-time, and I added a check with if (data) {...} but it still failed to compile.

I have seen people bind variables in components this way (so it should be a valid way to approach this problem), but don't understand why this isn't allowed here.

Below is the full code for this example which consists of a single component.ts with a selectors.ts, reducer.ts and actions.ts:

Component.ts

export class AppComponent implements OnInit{
    constructor(private _store: Store) {}
    strSub: Subscription;
    str: string;
    numChanged: number;
    observable: Observable<string>;

ngOnInit() {
    this.strSub = this._store.select(selectStr).subscribe((data) => console.log(data));
    //this.strSub = this._store.select(selectStr).subscribe((data) => {this.str = data});
    // this.observable = this._store.select(selectStr)
}
}

Selectors.ts

export const myTopLevelSelector = createFeatureSelector<fromReducer.State>(fromReducer.featureKey);

export const selectStr = createSelector(myTopLevelSelector, (state) => {state.str});

Reducer.ts

export const featureKey = 'myFeatureKey';

export interface State {
    str: string,
    numChanged: number
}

export const initialState: State = {
    str: "initialState",
    numChanged: 0
}

const myReducer = createReducer(
    initialState,
    on(myAction1, (state, action) => ({
        ...state,
        str: action.str,
        numChanged: state.numChanged + 1
    }))
)    

export function reducer(state: State | undefined, action: Action) {
    return myReducer(state, action);
}

Actions.ts

export const myAction1 = createAction('ACTION1', props<{str: string, numChanged: number}>());

export const myActionSuccess = createAction('ACTIONSUCCESS');

The HTML file is an unremarkable one-liner: <p>{{this.str}}</p>

I looked at this thread (and the links it contains): cannot get 'this' of component inside subscribe function, and the problem is somewhat similar in format, but a fundamental difference is that I'm not trying to log; I'm trying to assign, and the assignment doesn't work while the logging works.


Solution

  • I think that the problem that you are having is in your selector.

    Remove the brackets from the arrow funtion if you have them you will need to add the return into it.

    Without brackets

    export const selectStr = createSelector(myTopLevelSelector, (state) => state.str);
    

    With brackets

    export const selectStr = createSelector(myTopLevelSelector, (state) => { return state.str })
    

    Hope it helps you!