page change angular redux rxjs ngrx

change - router events angular 6



Angular 2 usando ngrx+RxJS suscribiéndose en múltiples componentes/rutas para poblar la tienda (1)

En general, su código se ve "bien". Sin embargo, hay algunas cosas que he notado:

  • Estás haciendo cosas como comprobar el ect de longitud del proyecto. probablemente guarde una llamada de descanso; desde mi experiencia, diría que este tipo de cosas solo valen la pena si se transfieren muchos datos o si hay cálculos pesados ​​del servidor o si su aplicación tiene un par de miles de usuarios y realmente está buscando optimizar hasta el último bit del rendimiento de su servidor, de otro modo: simplemente busque los datos O divide su tienda en allProjects: Projects[] que no se vuelven a recopilar y selectedProject: Project que solo se obtiene si no se encuentra dentro de todos los allProjects
  • en lo que se refiere a la nomenclatura de archivos, trate de evitar mayúsculas y nombre el componente de sus componentes y no la directiva .
  • Dado que está utilizando ngrx para su tienda, es posible que desee echar un vistazo a ngrx / effects como alternativa a las acciones de envío desde su servicio -> esta parte sería muy opcional, pero en la aplicación ngrx perfecta , la información el servicio ni siquiera sabría que hay una tienda.

Dicho esto, aquí hay algunas mejoras de código que lo acercan más a una aplicación orientada a ngrx; sin embargo, aún sugiero que eche un vistazo a la aplicación oficial ngrx-example, que es muy buena :

projects.component.ts

@Component({ selector: ''projects'', templateUrl: ''./projects.html'' }) export class Projects { private projects$: Observable<Project[]> = his.store .select<Project[]>(''projects'') .map(projects => projects.all) constructor(private store: Store<AppStore>) { store.dispatch({type: ProjectActions.LOAD_ALL}); } }

projects.component.html

<section> <article *ngFor="let project of projects$ | async"> <!-- you don''t need to use the questionmark here (project?.name) if you have something like "undefined" or "null" in your array, then the problem lies somewhere else --> <p>{{project._id}}</p> <p>{{project.name}}</p> <img src="{{project.img}}" /> <a routerLink="{{project._id}}">See more</a> </article> </section>

project.component.ts

@Component({ selector: ''projectOne'', templateUrl: ''./projectOne.html'' }) export class ProjectOneComponent implements OnInit { // project$ is only used with the async-pipe private project$: Observable<Project[]> = this.route.params .map(params => params[''id'']) .switchMap(id => this.store .select<Project[]>(''projects'') .map(projects => projects.byId[id]) .filter(project => !!project) // filter out undefined & null ) .share(); // sharing because it is probably used multiple times in the template constructor(private route: ActivatedRoute, private store: Store<AppStore>) {} ngOnInit() { this.route.params .take(1) .map(params => params[''id'']) .do(id => this.store.dispatch({type: ProjectActions.LOAD_PROJECT, payload: id}) .subscribe(); } }

project.service.ts => no sabe sobre la tienda

@Injectable() export class ProjectsService { constructor(private _http: Http){} fetchAll() { return this._http.get(`/api/projects`) .map(res => res.json()); } fetchBy(id) { return this._http.get(`/api/projects?id=${id}`) .map(res => res.json()); } }

project.effects.ts

@Injectable() export class ProjectEffects { private projects$: Observable<Project[]> = his.store .select<Project[]>(''projects'') .map(projects => projects.all); constructor(private actions$: Actions, private store: Store<AppStore>, private projectsService: ProjectsService){} @Effect() public loadAllProjects$: Observable<Action> = this.actions$ .ofType(ProjectActions.LOAD_ALL) .switchMap(() => this.projectsService.fetchAll() .map(payload => {type: ProjectActions.ADD_PROJECTS, payload}) ); @Effect() public loadSingleProject$: Observable<Action> = this.actions$ .ofType(ProjectActions.LOAD_PROJECT) .map((action: Action) => action.payload) .withLatestFrom( this.projects$, (id, projects) => ({id, projects}) ) .flatMap({id, projects} => { let project = projects.find(project => project._id === id); if (project) { // project is already available, we don''t need to fetch it again return Observable.empty(); } return this.projectsService.fetchBy(id); }) .map(payload => {type: ProjectActions.ADD_PROJECT, payload}); }

projects.reducer.ts

export interface ProjectsState { all: Project[]; byId: {[key: string]: Project}; } const initialState = { all: [], byId: {} }; export const projects: ActionReducer<ProjectsState> = (state: ProjectsState = initialState, action: Action) => { switch (action.type) { case ADD_PROJECTS: const all: Project[] = action.payload.slice(); const byId: {[key: string]: Project} = {}; all.forEach(project => byId[project._id] = project); return {all, byId}; case ADD_PROJECT: const newState: ProjectState = { all: state.slice(), byId: Object.assing({}, state.byId) }; const project: Project = action.payload; const idx: number = newState.all.findIndex(p => p._id === project._id); if (idx >= 0) { newState.all.splice(idx, 1, project); } else { newState.all.push(project); } newState.byId[project._id] = project; return newState; default: return state; } };

Como puede ver, este podría ser un poco más de código, pero solo en lugares centrales, donde el código se puede reutilizar fácilmente: los componentes se volvieron mucho más sencillos.

En una aplicación ideal, también tendría una capa adicional para ProjectsComponent y ProjectOneComponent , algo así como ProjectsRouteComponent y SingleProjectRoute , que contendría solo una plantilla como esta: <projectOne project="project$ | async"></projectOne> esto liberaría la ProjectOneComponent de cualquier conocimiento de la tienda o cualquier otra cosa, y solo contendría una entrada simple:

@Component({ selector: ''projectOne'', templateUrl: ''./projectOne.html'' }) export class ProjectOneComponent implements OnInit { @Input("project") project: Project; }

Actualmente estoy compilando una aplicación Angular 2 usando Redux (ngrx) y RxJS (principalmente para fines de aprendizaje), sin embargo, sigue siendo un poco (por decir lo menos) confusa para mí.

Estoy intentando implementar una ruta "/ projects", así como una ruta "/ projects /: id". En ambos casos, el comportamiento es que realizo una solicitud HTTP para obtener los datos requeridos.

Actualmente, si navego a "proyectos" (ya sea por URL o llamada ajax a través de navegación) obtendrá los 15 proyectos más o menos del servidor y los agregará a la tienda de "proyectos" en Redux. Ahora, si actualmente intento ingresar desde un proyecto específico (desde la barra de búsqueda del navegador -> "localhost: 3000 / projects / 2", por ejemplo), solo obtendrá el que sea, que es lo que quiero y lo coloque en el almacenar, sin embargo, si navego a la sección "proyectos" desde allí, solo se imprimirá el proyecto que se encuentra en la tienda.

Lo que quiero lograr es lo siguiente:

  • Si entro en "/ projects" primero, entonces busque y coloque todos los resultados en la tienda.
  • Si se cumple el caso anterior y navego a un proyecto específico desde allí, utilizando una etiqueta de enlace, quiero verificar en la tienda el artículo que tiene ese ID específico y devolverlo.
  • Si ingreso directamente desde "/ projects /: id", quiero buscar ese proyecto específico solo y colocarlo en la tienda.
  • Si ocurre el punto inmediatamente anterior, quiero poder navegar a "/ projects", a través de mi menú o cualquier otro enlace, buscar todos los artículos y actualizar mi tienda de "proyectos" con todos los artículos (no solo el que ya existía) del punto anterior)
  • Cualquier otro escenario lógico que pueda estar perdiendo respetando lo anterior

Quiero lograr esto de una manera eficiente, performante y elegante.

Actualmente estoy, creo, suscrito al mismo observable desde al menos dos lugares y no creo que ese sea el enfoque correcto. Además de eso, todavía no puedo obtener los resultados que quiero si entro primero de la ruta "/ projects: / id" y luego navego hacia la ruta "/ projects".

Aquí está el código que considero relevante:

projects.directive.ts

import { Component, OnInit } from ''@angular/core''; import { ProjectsService } from ''../shared/services/projects.service''; import { Observable } from ''rxjs/Observable''; import { Project } from ''../../models/project.model''; @Component({ selector: ''projects'', templateUrl: ''./projects.html'' }) export class Projects implements OnInit { private projects$: Observable<Project[]> constructor(private projectsService: ProjectsService) {} ngOnInit() { this.projectsService.findProjects(); } }

projectOne.directive.ts

import { Component, OnInit } from ''@angular/core''; import { Params, ActivatedRoute } from ''@angular/router''; import { Observable } from ''rxjs/Observable''; import { ProjectsService } from ''../../shared/services/projects.service''; import { Project } from ''../../../models/project.model''; @Component({ selector: ''projectOne'', templateUrl: ''./projectOne.html'' }) export class ProjectOneComponent implements OnInit { private projects$: Observable<Project[]> constructor(private route: ActivatedRoute, private projectsService: ProjectsService) {} ngOnInit() { this.route.params.subscribe((params: Params) => { this.projectsService.findProjects(params[''id'']) }); } }

* Algunas cosas para tener en cuenta aquí: me suscribo a this.route.params , que se suscribe a otro Observable, ¿necesito aplanar eso en absoluto o no? El concepto todavía me gana

projects.html

<section> <article *ngFor="let project of projectsService.projects$ | async"> <p>{{project?._id}}</p> <p>{{project?.name}}</p> <img src="{{project?.img}}" /> <a routerLink="{{project?._id}}">See more</a> </article> </section>

* Aquí me gustaría señalar que también estoy usando projectsService.projects $ | asincrónico para imprimir los resultados en la iteración que estoy bastante seguro también afecta ...

projects.service.ts

import { Http } from ''@angular/http''; import { Injectable } from ''@angular/core''; import { Observable } from ''rxjs/Observable''; import ''rxjs/add/operator/map''; import { Store } from ''@ngrx/store''; import { Project } from ''../../../models/project.model''; import { AppStore } from ''../../app.store''; import { ADD_PROJECTS } from ''../../../reducers/projects.reducer''; @Injectable() export class ProjectsService { public projects$: Observable<Project[]>; constructor(private _http: Http, private store: Store<AppStore>){ this.projects$ = store.select<Project[]>(''projects''); } fetchProjects(id) { return this._http.get(`/api/projects?id=${id}`) .map(res => res.json()) .map(({projectsList}) => ({ type: ADD_PROJECTS, payload: projectsList })) .subscribe(action => this.store.dispatch(action)); } findProjects(id: Number = 0) { this.projects$.subscribe(projects => { if (projects.length) { if (projects.length === 1) { return this.fetchProjects(); } } else { return this.fetchProjects(id ? id : '''') } }) } }

* Supongo que cada vez que llamo a esa función "findProjects" me suscribo al Observable. No está bien, ¿eh?

* Además, con esta configuración actual cada vez que voy directamente a "/ projects /: id", parece que está ejecutando la función fetchProjects dos veces (eso me pareció mucho por el registro de la consola). Básicamente, la suscripción de this.projects $ dentro de findProjects salta y obtiene el proyecto con la identificación correspondiente, pero luego vuelve a entrar y recupera todos los demás proyectos y finalmente "desaparece". ¿Por qué se está llamando a sí mismo o de dónde viene la segunda llamada?

projects.reducer.ts

import { Project } from ''../models/project.model''; import { ActionReducer, Action } from ''@ngrx/store''; export const ADD_PROJECTS = ''ADD_PROJECTS''; export const projects: ActionReducer<Project[]> = (state: Project[] = [], action: Action) => { switch (action.type) { case ADD_PROJECTS: return action.payload; default: return state; } };

* Esto es todo lo que el reductor tiene por el momento porque todavía estoy súper estancado en el resto.

De todos modos, me gustaría agradecerles a todos por adelantado. Si algo no está claro en absoluto o si necesita más información, hágamelo saber. Sé que esto cubre más de una cosa y podría ser súper fácil o nada, pero estoy realmente ansioso por obtener tanta ayuda como sea posible porque estoy realmente atrapado aquí ... ¡Gracias de nuevo!