异步管道不会将对象数据填充到模板中

问题描述 投票:0回答:7

谁能帮我看看我的模板中是否存在语法错误?它不会给出错误,但也不会将数据填充到模板中:

<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,姓名:“尼斯先生”}

这意味着数据已正确检索。

<div>
块也出现,这意味着
*ngIf
将对象视为非空。

<h2>{{hero}}</h2>
显示
[object Object]

但是为什么

{{hero.name}}
没有显示?

angular angular2-template
7个回答
66
投票

对象对于异步管道来说有点棘手。 对于包含数组的 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。)

使用组件逻辑将对象(位于 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>

25
投票

现在可以使用 v4.0.0 中提供的“as”语法:

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

更多详细信息请参阅 github 上的 RFC 线程


22
投票

另一种选择是使用 @Input 并利用智能/哑组件方法。在您的智能组件中,您可以将异步对象传递给哑组件,然后在哑组件中您可以像普通对象一样使用它。

这个想法是你的智能组件处理逻辑和数据,而哑组件处理表示。

智能组件:

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

哑组件类:

@Input() myHero: Hero;

哑组件模板:

<div>{{ myHero.name }}</div>

1
投票

我只是在您需要使用异步管道和括号语法的情况下添加如何使用智能/哑组件方法的精确性。

结合了here找到的技巧。

< ui-gallery-image([image]="(imagesFB | async) ? (imagesFB | async)[0] : null") ></ui-gallery-image>

我花了几个小时才找到。 希望这有帮助。 有关此博客文章的更多信息。


1
投票

在 Angular 2.3.x 或 Angular 4.x 模板中处理单个可观察对象的最佳方法是使用带有模板变量的异步管道。

这是角度开发人员的共同目标。从 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));
  }
}

查看

<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>

0
投票
  1. 异步管道订阅
  2. 取消订阅
  3. 为 OnPush 调用 markForCheck()
@Component({
    selector: 'product-alt',
    templateUrl: './products-alt.component.html',
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProductAltComponent implements OnDestroy {
    pageTitle: string = '';
    pageTitle$ =  (new BehaviorSubject<string>('InitialTitle'));
   
    constructor(private cdr: ChangeDetectorRef) {
        // Asyc pipe subscribes
        this.pageTitle$.subscribe((title: string) => {
            this.pageTitle = title;
            // call markForCheck
            this.cdr.markForCheck()
        });
    }
    
    ngOnDestroy() {
        // Unsubscribe
        this.pageTitle$.unsubscribe();
    }
}

0
投票

值得注意的是,在 Angular 18+ 中,您现在可以使用 @let 语法来获取对数据的引用。

<div>
  @let myItem = myItem$ | async;
  <span>{{ myItem.title }}
</div>
  
© www.soinside.com 2019 - 2024. All rights reserved.