web-dev-qa-db-fra.com

Attendez que Angular 2 charge/résolve le modèle avant de rendre la vue/le modèle

Dans Angular 1.x, UI-Router était mon principal outil pour cela. En renvoyant une promesse pour les valeurs "résoudre", le routeur attendrait simplement que la promesse soit terminée avant de rendre les directives.

Sinon, dans Angular 1.x, un objet null ne plantera pas un modèle. Par conséquent, si un rendu temporairement incomplet ne me dérange pas, je peux simplement utiliser $digest pour restituer après que promise.then() ait rempli un objet modèle initialement vide.

Parmi les deux approches, si possible, je préférerais attendre pour charger la vue et annuler la navigation par route si la ressource ne peut pas être chargée. Cela me sauve le travail de "dé-navigation". EDIT: Notez que cela signifie spécifiquement que cette question demande une méthode compatible avec les futures ou angular 2 pour ce faire, et demande d’éviter si possible "l’opérateur Elvis"! Ainsi, je n'ai pas choisi cette réponse.

Cependant, aucune de ces deux méthodes ne fonctionne dans Angular 2.0. Il existe sûrement une solution standard prévue ou disponible pour cela. Est-ce que quelqu'un sait ce que c'est?

@Component() {
    template: '{{cats.captchans.funniest}}'
}
export class CatsComponent {

    public cats: CatsModel;

    ngOnInit () {
        this._http.get('/api/v1/cats').subscribe(response => cats = response.json());
    }
}

La question suivante peut refléter le même problème: Modèle de rendu angulaire 2 après le chargement de la PROMISE avec les données . Notez que cette question n’a pas de code ni de réponse acceptée.

68
shannon

Le package @angular/router a la propriété Resolve pour les itinéraires. Ainsi, vous pouvez facilement résoudre les données avant de rendre une vue d'itinéraire.

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

Exemple tiré de la documentation à compter d'aujourd'hui, le 28 août 2017:

class Backend {
  fetchTeam(id: string) {
    return 'someTeam';
  }
}

@Injectable()
class TeamResolver implements Resolve<Team> {
  constructor(private backend: Backend) {}

  resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<any>|Promise<any>|any {
    return this.backend.fetchTeam(route.params.id);
  }
}

@NgModule({
  imports: [
    RouterModule.forRoot([
      {
        path: 'team/:id',
        component: TeamCmp,
        resolve: {
          team: TeamResolver
        }
      }
    ])
  ],
  providers: [TeamResolver]
})
class AppModule {}

Désormais, votre itinéraire ne sera activé que lorsque les données auront été résolues et renvoyées.

Accès aux données résolues dans votre composant

Pour accéder aux données résolues depuis votre composant au moment de l'exécution, il existe deux méthodes. Donc, selon vos besoins, vous pouvez utiliser soit:

  1. route.snapshot.paramMap qui retourne une chaîne, ou le 
  2. route.paramMap qui renvoie un observable auquel vous pouvez .subscribe()

Exemple:

  // 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);

J'espère que ça aide.

32
TetraDev

Essayez {{model?.person.name}}, cela devrait attendre que le modèle ne soit pas undefined et ensuite le rendre.

Angular 2 désigne cette syntaxe ?. en tant qu'opérateur Elvis . Il est difficile de s'y référer dans la documentation. En voici une copie au cas où ils changeraient/le déplaceraient:

Les chemins de propriétés Elvis Operator (?.) Et null

L'opérateur angulaire «Elvis» (?.) Est un moyen efficace et simple de se prémunir contre les valeurs nulles et non définies dans les chemins de propriétés. Voilà, la protection contre une vue rend échec si le currentHero est null.

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

Expliquons le problème et cette solution particulière.

Que se passe-t-il lorsque la propriété title liée aux données suivante est null?

The title is {{ title }}

La vue est toujours restituée mais la valeur affichée est vide. nous voyons seulement "Le titre est" avec rien après. C'est un comportement raisonnable. Au moins, l'application ne plante pas.

Supposons que l’expression de modèle implique un chemin de propriété, comme dans l’exemple suivant, où nous affichons le prénom d’un héros nul.

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

JavaScript génère une erreur de référence nulle et Angular également:

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

Pire encore, la vue entière disparaît.

Nous pourrions affirmer qu'il s'agit d'un comportement raisonnable si nous pensions que la propriété du héros ne doit jamais être nulle. Si elle ne doit jamais être nulle et pourtant, elle est nulle, nous avons commis une erreur de programmation qui devrait être interceptée et corrigée. Lancer une exception est la bonne chose à faire.

D'autre part, les valeurs NULL dans le chemin de la propriété peuvent être correctes de temps en temps, en particulier lorsque nous savons que les données arriveront éventuellement.

Pendant que nous attendons des données, la vue doit être générée sans réclamation et le chemin de la propriété null doit être affiché aussi vierge que la propriété title.

Malheureusement, notre application se bloque lorsque le currentHero est null.

Nous pourrions coder ce problème avec NgIf

<!--No hero, div not displayed, no error --><div *ngIf="nullHero">The null hero's name is {{nullHero.firstName}}</div>

Nous pouvons également essayer d’enchaîner des parties du chemin de la propriété avec &&, sachant que l’expression s’arrache quand elle rencontre le premier null.

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

Ces approches ont du mérite, mais elles peuvent être lourdes, surtout si le chemin de la propriété est long. Imaginez que vous vous protégiez contre un null quelque part dans un chemin d'accès de propriété long tel que a.b.c.d.

L'opérateur angulaire «Elvis» (?.) Est un moyen plus fluide et plus pratique de se prémunir des valeurs nulles dans les chemins de propriété. L'expression échoue lorsqu'elle atteint la première valeur NULL. L'affichage est vide, mais l'application continue de rouler et il n'y a pas d'erreur.

<!-- No hero, no problem! -->The null hero's name is {{nullHero?.firstName}}

Cela fonctionne aussi parfaitement avec les longs chemins de propriété:

a?.b?.c?.d

84
tehaaron

EDIT: L'équipe angulaire a sorti le décorateur @Resolve. Il faut encore des éclaircissements sur son fonctionnement, mais en attendant, je vais prendre la réponse connexe de quelqu'un d'autre ici et fournir des liens vers d'autres sources:


EDIT: Cette réponse ne fonctionne que pour Angular 2 BETA. Le routeur n'est pas disponible pour Angular 2 RC à la date de cette modification. Lorsque vous utilisez Angular 2 RC, remplacez plutôt les références à router par router-deprecated pour continuer à utiliser le routeur bêta.

Angular2-future sera implémenté via le décorateur @Resolve. Jusque-là, le fac-similé le plus proche est CanActivate Décorateur de composants, selon Brandon Roberts. voir https://github.com/angular/angular/issues/6611

Bien que la version bêta 0 ne prenne pas en charge la fourniture de valeurs résolues au composant, cela est prévu et une solution de contournement est décrite ici: Utilisation de la résolution dans les itinéraires angulaires2

Un exemple de beta 1 peut être trouvé ici: http://run.plnkr.co/BAqA98lphi4rQZAd/#/resolved . Il utilise une solution de contournement très similaire, mais utilise légèrement plus précisément l'objet RouteData plutôt que RouteParams.

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

Notez également qu’il existe également un exemple de solution de contournement pour accéder aux valeurs "résolues" de la route parent/imbriquée, ainsi que d’autres fonctionnalités que vous attendez si vous avez utilisé 1.x UI-Router.

Notez que vous devrez également injecter manuellement les services nécessaires, car la hiérarchie de l'injecteur angulaire n'est actuellement pas disponible dans le décorateur CanActivate. Le simple fait d'importer un injecteur crée une nouvelle instance d'injecteur, sans accès aux fournisseurs de bootstrap(); vous souhaiterez donc probablement stocker une copie de l'injecteur démarré à l'échelle de l'application. Le second lien Plunk de Brandon sur cette page est un bon point de départ: https://github.com/angular/angular/issues/4112

6
shannon

Définir une valeur locale avec l'observateur

... n'oubliez pas d'initialiser la valeur avec des données factices pour éviter les erreurs 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);
      );
    }
}

Ceci suppose que Modèle est un modèle de données représentant la structure de vos données.

Un modèle sans paramètre doit créer une nouvelle instance avec toutes les valeurs initialisées (mais vides). Ainsi, si le modèle est rendu avant la réception des données, il ne générera pas d'erreur.

Idéalement, si vous souhaitez conserver les données pour éviter les requêtes HTTP inutiles, vous devez les placer dans un objet disposant de son propre observateur auquel vous pouvez vous abonner.

4
Evan Plaice

Implémentez la routerOnActivate dans votre @Component et retournez votre promesse:

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

EDIT: Cela ne fonctionne explicitement PAS, bien que la documentation actuelle puisse être un peu difficile à interpréter sur ce sujet. Voir le premier commentaire de Brandon ici pour plus d'informations: https://github.com/angular/angular/issues/6611

EDIT: les informations associées sur le site Auth0, qui est sinon généralement exact, ne sont pas correctes: https://auth0.com/blog/2016/01/25/angular-2-series-part-4-component-router- en profondeur/

EDIT: L’équipe angulaire prépare un décorateur @Resolve à cet effet.

2
Langley

Une solution intéressante que j'ai trouvée consiste à faire sur l'interface utilisateur quelque chose comme:

<div *ngIf="vendorServicePricing && quantityPricing && service">
 ...Your page...
</div

Seulement lorsque: vendorServicePricing, quantityPricing et service sont chargés, la page est rendue.

2
Linu Radu

j'utilise Angular 6 avec Universal Add-on J'ai lu les réponses spécialement la deuxième. Mais je n'ai pas encore compris le point. J'ai un résolveur qui appelle et demande HttpClient XHR et envoie au composant et le composant sur ngOnInit liera les données à la vue. Mais le problème réside dans le mode d'affichage de la source. Il n'y a pas de données provenant de la réponse XHR affichée dans la vue.

0
Sami Al Morshedi