Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get the value from an ngrx store into my template?

A total Redux noob here, I am having a little difficulty getting my data out of the store for use within my view. Here are my Actions, Reducers etc.

genre.model.ts

export interface Genre {
    id: string;
    title: string;
    description: string;
    slug: string;
    error: string;
}

export const initialState: Genre = {
    id: '',
    title: '',
    description: '',
    slug: '',
    error: null
};

genre.service.ts

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Http } from '@angular/http';
import {environment} from "../../../environments/environment";
import {Genre} from "./genre.model";

@Injectable()
export class GenreService {

    apiUrl = environment.apiUrl + environment.apiVersion + '/';

    constructor(private http: Http) { }

    /**
     * Gets Genres
     *
     * @returns {Observable<Genre[]>}
     */
    getGenres(): Observable<Genre[]> {
        return this.http.get(this.apiUrl + 'genres/')
            .map(res => res.json());
    }

    /**
     * Gets an individual genre
     *
     * @param {String} slug
     * @returns {Observable<Genre[]>}
     */
    getGenre(slug: String): Observable<Genre[]> {
        return this.http.get(this.apiUrl + 'genres/' + slug)
            .map(res => res.json().genre);
    }
}

genre.actions.ts

import { Action } from '@ngrx/store';
import { Injectable } from '@angular/core';

import { Genre } from '../_shared/genre.model';

@Injectable()
export class GenreActions {

    static LOAD_GENRES = '[Genre] Load Genres';
    loadGenres(): Action {
        return {
            type: GenreActions.LOAD_GENRES
        };
    }

    static LOAD_GENRES_SUCCESS = '[Genre] Load Genres Success';
    loadGenresSuccess(genres): Action {
        return {
            type: GenreActions.LOAD_GENRES_SUCCESS,
            payload: genres
        };
    }

    static GET_GENRE = '[Genre] Get Genre';
    getGenre(slug): Action {
        return {
            type: GenreActions.GET_GENRE,
            payload: slug
        };
    }

    static GET_GENRE_SUCCESS = '[Genre] Get Genre Success';
    getGenreSuccess(genre): Action {
        return {
            type: GenreActions.GET_GENRE_SUCCESS,
            payload: genre
        };
    }
}

genre.reducers.ts

import { Action } from '@ngrx/store';

import {Genre, initialState} from '../_shared/genre.model';

import { GenreActions } from './genre.actions';

export function genreReducer(state: Genre = initialState, action: Action) {
    switch (action.type) {
        case GenreActions.GET_GENRE_SUCCESS: {
            return action.payload;
        }
        case GenreActions.LOAD_GENRES_SUCCESS: {
            return action.payload;
        }
        default: {
            return state;
        }
    }
}

genre.effects.ts

export class GenreEffects {
    constructor (
        private update$: Actions,
        private genreActions: GenreActions,
        private svc: GenreService,
    ) {}

    @Effect() loadGenres$ = this.update$
        .ofType(GenreActions.LOAD_GENRES)
        .switchMap(() => this.svc.getGenres())
        .map(genres => this.genreActions.loadGenresSuccess(genres));

    @Effect() getGenre$ = this.update$
        .ofType(GenreActions.GET_GENRE)
        .map(action => action.payload)
        .switchMap(slug => this.svc.getGenre(slug))
        .map(genre => this.genreActions.getGenreSuccess(genre));
}

genre.detail.component.ts

import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import { ActivatedRoute, Router } from '@angular/router';
import { Component, OnInit } from '@angular/core';

import { Genre } from '../_shared/genre.model';
import { GenreActions } from '../_store/genre.actions';

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

    public idSub: Subscription;
    public genre: Observable<any>;

    constructor(
        private store: Store<Genre>,
        private route: ActivatedRoute,
        private genreActions: GenreActions,
        private router: Router
    ) {
        this.genre = store.select('genres');
    }

    ngOnInit() {
        this.idSub = this.route.params.subscribe(params => {
            this.store.dispatch(this.genreActions.getGenre(params['slug']));
            console.log(this.genre);
            });
    }
}

I can see that my API request is being fired, and it is return data, I can see in the Redux devtools that state is being populated but I just can't seem to get the data out and in to my views using the normal {{ genre.title }} I just get [Object object] being thrown back at me?

I am sure that this might be something really easy but like I said I am a total noob and have spent around 5 hours on this trying out different things following different tutorials etc.

Console

like image 750
ChrisBratherton Avatar asked Jan 04 '23 14:01

ChrisBratherton


2 Answers

I am guessing your genre is a list of array

It should be some thing like this take it as wireframe.

genre : any ;

ngOnInit(){
 this.idSub = this.route.params.subscribe(params => {
   this.store.dispatch(this.genreActions.getGenre(params['slug']));
   });
   this.store.select('genres').subscribe(data => this.genre = data)
}

If you want to look up to ngrx 4 look at this link

I just dug up my git you can check this snapshot of my repo of using ngrx v2 . I donot have a working example for the same but rest assured the code workes LINK

UPDATE

Make a different Object for Genre to use the Genre interface in state

export interface AppState {
    genre:Genre
}

Now Subscribe to this state object genre in the constructor or ngOnInit

private store: Store<AppState>, 
private route: ActivatedRoute, 
private genreActions: GenreActions, 
private router: Router 
) { 
this.store.select('genre').subscribe(data => this.genre = data); 
}
like image 104
Rahul Singh Avatar answered Feb 01 '23 20:02

Rahul Singh


Currently, you are getting just the Observable but expecting the list of genre. And, that is the problem. You need to subscribe it to get the value.

Make a different Object for Genre to use the Genre interface in state (as Rahul Singh told already)

export interface AppState {
    genre:Genre
}

constructor(private store: Store<AppState>,
    private route: ActivatedRoute,
    private genreActions: GenreActions, 
    private router: Router) {}

and in ngOnInit subscribe it to obeserve the change.

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

Note: ngOnInit() is used to make sure that the component's properties (for example: @Input() someId: number) you used are already initialized . So, If you use any component properties you should do it in ngOnInit(). otherwise constructor() is fine. but we should use constructor() to setup Dependency Injection and not much else. ngOnInit() is better place to "start" - it's where/when components' bindings are resolved.

like image 26
Sadid Khan Avatar answered Feb 01 '23 20:02

Sadid Khan