Angular2 асинхронный канал не заполняет данные объекта в шаблон
Может ли кто-нибудь помочь увидеть, есть ли синтаксическая ошибка здесь в моем шаблоне? Он не дает ошибок, но не заполняет данные в шаблоне.
<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>
Компонентный код
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)
)
}
Отпечатки console.log(data)
Объект {_id: 11, имя: "Mr. Nice" }
что я правильно понял.
Также отображается блок <div>
, что означает * ngIf видит объект как не пустой.
<h2>{{hero}}</h2>
показывает [объект Object].
Но почему {{hero.name}} не отображается?
Ответы
Ответ 1
Объекты немного сложны с асинхронным каналом. С Observable, который содержит массив, мы можем использовать NgFor и создать локальную переменную шаблона (ниже hero
), которая получает каждый элемент массива после того, как асинхронный канал извлекает массив из Observable. Затем мы можем использовать эту переменную в другом месте шаблона:
<div *ngFor="let hero of heroes | async">
{{hero.name}}
</div>
<!-- we can't use hero here, outside the NgFor div -->
Но с Observable, который содержит один объект, я не знаю, как создать локальную переменную шаблона, которая будет ссылаться на этот объект. Вместо этого нам нужно сделать что-то более сложное:
<div>{{(hero | async)?.name}}</div>
И нам нужно будет повторить это для каждого свойства объекта, который мы хотим отобразить. (В приведенной выше строке предполагается, что свойство компонента hero
является наблюдаемым.)
Возможно, проще присвоить объект (находящийся внутри Observable, hero$
ниже) к свойству компонента, используя логику компонента:
this._heroService.hero$.subscribe(data => this.hero = data.json());
а затем используйте NgIf или Elvis/safe navigation operator, чтобы отобразить данные в представлении:
<div *ngIf="hero">{{hero.name}}</div>
<!-- or -->
<div>{{hero?.name}}</div>
Ответ 2
Другой вариант - использовать метод @Input и использовать подход "умный/немой". В вашем интеллектуальном компоненте вы можете передать объект async немому компоненту, а затем в немой компоненте вы можете использовать его как обычный объект.
Идея заключается в том, что ваш интеллектуальный компонент имеет дело с логикой и данными, а также с представлением о немых компонентах.
Интеллектуальный компонент:
<dumb-component [myHero]="hero$ | async"></dumb-component>
Немой компонентный класс:
@Input() myHero: Hero;
Немой шаблон компонента:
<div>{{ myHero.name }}</div>
Ответ 3
Теперь это возможно с использованием синтаксиса "как", доступного в версии 4.0.0:
<span *ngIf="user$ | async as user; else loadingUserInfo">
{{user.firstName}} {{user.lastName}}
</span>
<ng-template #loadingUserInfo>
Loading user information...
</ng-template>
Более подробная информация доступна в RFC-потоке в github.
Ответ 4
Я просто добавляю точность о том, как использовать подход "умный/немой" в случае, если вам нужно использовать асинхронный канал и синтаксис скобки.
Это объединяет трюк, найденный здесь.
< ui-gallery-image([image]="(imagesFB | async) ? (imagesFB | async)[0] : null") ></ui-gallery-image>
Ответ 5
Лучший способ обработки одиночного наблюдаемого объекта внутри шаблона Angular 2.3.x или Angular 4.x - использовать асинхронный канал с переменной шаблона.
Здесь общая цель для разработчиков Angular. Возьмите массив элементов из redux и вырвите из коллекции один элемент соответствия. Затем визуализируйте этот особый объект в шаблоне.
КОМПОНЕНТ
@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));
}
}
VIEW
<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>