Espere a que Angular 2 cargue / resuelva el modelo antes de mostrar la vista / plantilla

En Angular 1.x, UI-Router fue mi principal herramienta para esto. Al devolver una promesa de valores de “resolución”, el enrutador simplemente esperaría a que se cumpliera la promesa antes de entregar las directivas.

Alternativamente, en Angular 1.x, un objeto nulo no bloqueará una plantilla, por lo que si no me importa un procesamiento temporalmente incompleto, solo puedo usar $digest para renderizar después de la promise.then() rellena un objeto de modelo inicialmente vacío .

De los dos enfoques, si es posible, preferiría esperar para cargar la vista y cancelar la navegación de ruta si el recurso no se puede cargar. Esto me ahorra el trabajo de “no navegar”. EDITAR: Tenga en cuenta que esto significa específicamente que esta pregunta requiere un método compatible con Futuros 2 futuros o las mejores prácticas para hacer esto, y pide evitar el “operador de Elvis” si es posible. Por lo tanto, no seleccioné esa respuesta.

Sin embargo, ninguno de estos dos métodos funciona en Angular 2.0. Seguramente hay una solución estándar planificada o disponible para esto. ¿Alguien sabe qué es esto?

 @Component() { template: '{{cats.captchans.funniest}}' } export class CatsComponent { public cats: CatsModel; ngOnInit () { this._http.get('/api/v1/cats').subscribe(response => cats = response.json()); } } 

La siguiente pregunta puede reflejar el mismo problema: Plantilla de renderizado angular 2 después de la PROMESA con los datos cargados . Tenga en cuenta que la pregunta no tiene código ni respuesta aceptada.

El paquete @angular/router tiene la propiedad Resolve para las rutas. Para que pueda resolver fácilmente los datos antes de representar una ruta.

Ver: https://angular.io/docs/ts/latest/api/router/index/Resolve-interface.html

Ejemplo de documentos a partir de hoy, 28 de agosto de 2017:

 class Backend { fetchTeam(id: string) { return 'someTeam'; } } @Injectable() class TeamResolver implements Resolve { constructor(private backend: Backend) {} resolve( route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable|Promise|any { return this.backend.fetchTeam(route.params.id); } } @NgModule({ imports: [ RouterModule.forRoot([ { path: 'team/:id', component: TeamCmp, resolve: { team: TeamResolver } } ]) ], providers: [TeamResolver] }) class AppModule {} 

Ahora su ruta no se activará hasta que los datos se hayan resuelto y devuelto.

Accediendo a los datos resueltos en su componente

Para acceder a los datos resueltos desde dentro de su componente en tiempo de ejecución, existen dos métodos. Dependiendo de sus necesidades, puede usar:

  1. route.snapshot.paramMap que devuelve una cadena, o el
  2. route.paramMap que devuelve un Observable que puede .subscribe() a.

Ejemplo:

  // the no-observable method this.dataYouResolved= this.route.snapshot.paramMap.get('id'); // console.debug(this.licenseNumber); // or the observable method this.route.paramMap .subscribe((params: ParamMap) => { // console.log(params); this.dataYouResolved= params.get('id'); return params.get('dataYouResolved'); // return null }); console.debug(this.dataYouResolved); 

Espero que eso ayude.

Pruebe con {{model?.person.name}} esto debería esperar a que el modelo no esté undefined y luego renderizar.

Angular 2 se refiere a esto ?. syntax como el operador de Elvis . La referencia en la documentación es difícil de encontrar, así que aquí hay una copia en caso de que la cambien / muevan:

El operador de Elvis (?.) Y las rutas de propiedad nula

El operador “Elvis” angular (?) Es una forma fluida y conveniente de protegerse contra valores nulos e indefinidos en las rutas de propiedad. Aquí está, protegiendo contra una vista, render failure si el actualHero es nulo.

The current hero's name is {{currentHero?.firstName}}

Vamos a elaborar sobre el problema y esta solución particular.

¿Qué sucede cuando la siguiente propiedad de título enlazado a datos es nula?

The title is {{ title }}

La vista aún se muestra, pero el valor mostrado está en blanco; solo vemos “El título es” sin nada después. Ese es un comportamiento razonable. Al menos la aplicación no se cuelga.

Supongamos que la expresión de la plantilla implica una ruta de propiedad como en el siguiente ejemplo donde estamos mostrando el primer Nombre de un héroe nulo.

The null hero's name is {{nullHero.firstName}}

JavaScript arroja un error de referencia nulo, al igual que Angular:

TypeError: Cannot read property 'firstName' of null in [null]

Peor aún, toda la vista desaparece.

Podríamos afirmar que este es un comportamiento razonable si creyéramos que la propiedad del héroe nunca debe ser nula. Si nunca debe ser nulo y, sin embargo, es nulo, hemos cometido un error de progtwigción que debe detectarse y corregirse. Lanzar una excepción es lo correcto.

Por otro lado, los valores nulos en la ruta de la propiedad pueden ser correctos de vez en cuando, especialmente cuando sabemos que los datos llegarán eventualmente.

Mientras esperamos los datos, la vista debe mostrarse sin quejarse y la ruta de acceso de la propiedad nula debe aparecer tan vacía como la propiedad del título.

Desafortunadamente, nuestra aplicación falla cuando el actualHero es nulo.

Podríamos codificar ese problema con NgIf

The null hero's name is {{nullHero.firstName}}

O podríamos tratar de encadenar partes de la ruta de propiedad con &&, sabiendo que la expresión se desactiva cuando encuentra el primer nulo.

The null hero's name is {{nullHero && nullHero.firstName}}

Estos enfoques tienen sus méritos pero pueden ser engorrosos, especialmente si el camino de la propiedad es largo. Imagine protegerse contra un nulo en algún lugar de un largo camino de propiedad como abcd

El operador angular “Elvis” (?) Es una forma más fluida y conveniente de protegerse contra nulos en las rutas de las propiedades. La expresión se desactiva cuando alcanza el primer valor nulo. La pantalla está en blanco, pero la aplicación continúa y no hay errores.

The null hero's name is {{nullHero?.firstName}}

Funciona perfectamente con rutas de propiedad largas también:

a?.b?.c?.d

EDITAR: El equipo angular ha lanzado el decorador @Resolve. Todavía necesita alguna aclaración sobre cómo funciona, pero hasta entonces tomaré la respuesta relacionada de otra persona aquí y proporcionaré enlaces a otras fonts:

  • StackOverflow: Uso de Resolve In Angular2 Routes
  • StackOverflow: muestra de uso de Günter Zöchbauer
  • Resolver documentación
  • El parche del enrutador en GitHub
  • RC4 ChangeLog
  • RC4 lanzamiento Tweet

EDITAR: Esta respuesta funciona solo para Angular 2 BETA. El enrutador no se lanza para Angular 2 RC a partir de esta edición. En su lugar, cuando utilice Angular 2 RC, reemplace las referencias al router con un router-deprecated para continuar usando el enrutador beta.

La forma futura Angular2 de implementar esto será a través del decorador @Resolve. Hasta entonces, el facsímil más cercano es el decorador CanActivate Component, por Brandon Roberts. ver https://github.com/angular/angular/issues/6611

Aunque beta 0 no admite proporcionar valores resueltos al Componente, está planificado, y también hay una solución alternativa descrita aquí: Uso de Resolve In Angular2 Routes

Un ejemplo de beta 1 se puede encontrar aquí: http://run.plnkr.co/BAqA98lphi4rQZAd/#/resolved . Utiliza una solución muy similar, pero con un poco más de precisión utiliza el objeto RouteData lugar de RouteParams .

 @CanActivate((to) => { return new Promise((resolve) => { to.routeData.data.user = { name: 'John' } 

Además, tenga en cuenta que también existe una solución de ejemplo para acceder a los valores “resueltos” de la ruta anidada / principal, y otras características que espera si ha utilizado 1.x UI-Router.

Tenga en cuenta que también necesitará inyectar manualmente los servicios que necesite para lograr esto, ya que la jerarquía de inyectores angulares no está disponible actualmente en el decorador CanActivate. La simple importación de un inyector creará una nueva instancia de inyector, sin acceso a los proveedores de bootstrap() , por lo que probablemente desee almacenar una copia de la aplicación del inyector bootstrap. El segundo enlace Plunk de Brandon en esta página es un buen punto de partida: https://github.com/angular/angular/issues/4112

Establecer un valor local con el observador

… también, no te olvides de inicializar el valor con datos ficticios para evitar errores uninitialized .

 export class ModelService { constructor() { this.mode = new Model(); this._http.get('/api/v1/cats') .map(res => res.json()) .subscribe( json => { this.model = new Model(json); }, error => console.log(error); ); } } 

Esto supone que el Modelo es un modelo de datos que representa la estructura de sus datos.

El modelo sin parámetros debe crear una nueva instancia con todos los valores inicializados (pero vacíos). De esta forma, si la plantilla renderiza antes de que se reciban los datos, no arrojará un error.

Idealmente, si desea conservar los datos para evitar solicitudes http innecesarias, debe poner esto en un objeto que tenga su propio observador al que pueda suscribirse.

Implemente el routerOnActivate en su @Component y devuelva su promesa:

https://angular.io/docs/ts/latest/api/router/OnActivate-interface.html

EDITAR: Esto NO funciona explícitamente, aunque la documentación actual puede ser un poco difícil de interpretar sobre este tema. Vea el primer comentario de Brandon aquí para más información: https://github.com/angular/angular/issues/6611

EDITAR: la información relacionada en el sitio Auth0, que por lo demás suele ser preciso, no es correcta: https://auth0.com/blog/2016/01/25/angular-2-series-part-4-component-router-in- profundidad/

EDITAR: El equipo angular está planeando un decorador @Resolve para este propósito.

Una buena solución que he encontrado es hacer en UI algo así como:

 
...Your page...

Solo cuando se vendorServicePricing , vendorServicePricing , quantityPricing y service , se procesará la página.

Estoy usando Angular 6 con Universal add-on He leído las respuestas especialmente la segunda. Pero todavía no entiendo el punto. Tengo una resolución que llama y HttpClient XHR solicita y envía a componente y el componente en ngOnInit vinculará los datos a la vista. Pero el problema está en el modo fuente de vista, no hay datos de la respuesta XHR que se muestra a la vista.

Una solución fácil es inicializar las propiedades del objeto en init / constructor

cats.captchans.funniest

Una vez que se resuelva la promesa, se vincularán los valores reales.