web-dev-qa-db-fra.com

Le tuyau async ne remplace pas les données d'objet dans le modèle

Quelqu'un peut-il m'aider à voir s'il y a une erreur de syntaxe ici dans mon modèle? Cela ne donne pas d'erreur, mais ne remplit pas non plus les données dans le modèle:

<div *ngIf="(hero | async)">
  <h2>{{hero}}</h2>
  <h2>{{hero.name}} details!</h2>
  <div>
    <label>_id: </label>{{hero._id}}</div>
  <div>
    <label>name: </label>
    <input [(ngModel)]="hero.name" placeholder="name" />
  </div>
  <button (click)="goBack()">Back</button>
</div>

Code composant

export class HeroDetailComponent implements OnInit {
    errorMessage: string;

    //@Input() 
    hero: Observable<Hero>;

    constructor(
        private _heroService: HeroService,
        private _routeParams: RouteParams) {
    }

    ngOnInit() {
        let _id = +this._routeParams.get('_id');
        this._heroService.loadHero(_id);
        this.hero = this._heroService.hero$;
        this.hero.subscribe(data => 
           console.log(data)
        )
    }

La console.log(data) imprime:

Objet {_id: 11, name: "Mr. Nice"}

ce qui signifie que les données sont correctement récupérées. 

Le bloc <div> apparaît également, ce qui signifie que *ngIf voit l'objet comme non vide.

<h2>{{hero}}</h2> affiche [object Object].

Mais pourquoi le {{hero.name}} n'est pas affiché? 

20
Shawn

Les objets sont un peu délicats avec le pipe async. Avec un Observable contenant un tableau, nous pouvons utiliser NgFor et créer une variable de modèle local (hero ci-dessous) à laquelle est affecté chaque élément du tableau après que le canal asynchrone ait extrait le tableau de Observable. Nous pouvons ensuite utiliser cette variable ailleurs dans le modèle:

<div *ngFor="let hero of heroes | async">
  {{hero.name}}
</div>
<!-- we can't use hero here, outside the NgFor div -->

Mais avec un observable contenant un seul objet, je ne connais aucun moyen de créer une variable de modèle local qui référencera cet objet. Au lieu de cela, nous devons faire quelque chose de plus compliqué:

<div>{{(hero | async)?.name}}</div>

Et nous aurions besoin de répéter cela pour chaque propriété de l'objet que nous voulons afficher. (La ligne ci-dessus suppose que la propriété de composant hero est un observable.)

Il est probablement plus facile d'attribuer l'objet (situé dans l'observable, hero$ ci-dessous) à une propriété du composant, à l'aide de la logique du composant:

this._heroService.hero$.subscribe(data => this.hero = data.json());

puis utilisez NgIf ou l'opérateur Elvis/safe navigation pour afficher les données dans la vue:

<div *ngIf="hero">{{hero.name}}</div>
<!-- or -->
<div>{{hero?.name}}</div>
54
Mark Rajcok

Une autre option consisterait à utiliser @Input et à tirer parti de l'approche des composants intelligents/muets. Dans votre composant intelligent, vous pouvez transmettre l'objet asynchrone au composant muet, puis dans le composant muet, vous pouvez l'utiliser comme un objet normal.

L'idée est que votre composant intelligent traite la logique et les données et le composant muet gère la présentation.

Composant intelligent:

<dumb-component [myHero]="hero$ | async"></dumb-component>

Classe de composant muet:

@Input() myHero: Hero;

Modèle de composant muet:

<div>{{ myHero.name }}</div>
21
chris cooley

Ceci est maintenant possible en utilisant la syntaxe 'as', disponible dans la v4.0.0:

<span *ngIf="user$ | async as user; else loadingUserInfo">
 {{user.firstName}} {{user.lastName}}
</span>
<ng-template #loadingUserInfo>
  Loading user information...
</ng-template>

Plus de détails disponibles dans le fil de discussion RFC sur github .

17
jmcmichael

La meilleure façon de gérer un objet unique observable dans un modèle angulaire 2.3.x ou 4.x angulaire consiste à utiliser un canal asynchrone avec une variable de modèle.

Voici un objectif commun pour les développeurs angulaires. Prenez un tableau d'éléments de redux et cueillez un seul élément correspondant de la collection. Rendez ensuite cet objet singulier dans un modèle.

COMPOSANT

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

  @select(['documents', 'items']) readonly documenList$: Observable<DocumentDTO[]>;
  public documentVO$: Observable<DocumentDTO>;

  constructor(private _state: NgRedux<IAppState>,
              private _route: ActivatedRoute,
              private _documentActions: DocumentActions) {

    _route.params.subscribe(params => {
      let modelId: number = parseInt(params['modelId']); //1          
      let documentId: number = parseInt(params['documentId']); //50
      this._documentActions.getDocument(modelId, documentId);
    });
  }

  ngOnInit() {

    //documenList holds all of the documents in our application state
    //but this view only wants a single element

    this.documentVO$ = this.documenList$.map(documents => documents.find(doc => doc.documentId === 50));
  }
}

VUE

<div class="row" *ngIf="documentVO$ | async as dto">
    <div id="about" class="col-12">
        <div id="title" class="paper meta">
            <p>{{ dto.title }}</p>
        </div>
    </div>
</div>
1
Jack Murphy