【问题标题】:Async pipe does not fill object data into template异步管道不会将对象数据填充到模板中
【发布时间】:2016-08-16 15:34:52
【问题描述】:

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

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

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

&lt;div&gt; 块也出现了,这意味着 *ngIf 将对象视为非空。

&lt;h2&gt;{{hero}}&lt;/h2&gt; 显示[object Object]

但是为什么{{hero.name}} 没有显示?

【问题讨论】:

  • 你搞混了一些事情。 hero 是一个输入属性,但是你在 ngOnInit() 中为其分配了一个值——这很奇怪。分配的值是一个 Observable,它没有 name 属性,这就解释了为什么 {{hero.name}} 不起作用。这个答案应该可以帮助你:stackoverflow.com/a/34561532/215945
  • 我更新了帖子并删除了@input。但还是一样。异步管道应该将可观察对象变成英雄对象?

标签: angular angular2-template


【解决方案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。)

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

【讨论】:

  • {{(hero | async)?.name}} 不起作用。我做了第二种方法:订阅将数据放入组件属性并在模板中引用它。感谢您抽出宝贵的时间。我很感激。
  • {{(hero | async)?.name}}
    - 这简直太完美了。节省了我很多时间...
【解决方案2】:

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

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

智能组件:

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

哑组件类:

@Input() myHero: Hero;

哑组件模板:

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

【讨论】:

  • myHero 是一个字符串,对吧?不应该是 '
    {{ myHero }}
    ' 而不是 '
    {{ myHero.name }}
    ' 吗?
  • @guilhebl no.. myHero 属于“英雄”类型。
【解决方案3】:

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

这结合了here发现的一个技巧。

&lt; ui-gallery-image([image]="(imagesFB | async) ? (imagesFB | async)[0] : null") &gt;&lt;/ui-gallery-image&gt;

我花了好几个小时才找到。希望这有帮助。 有关此blog post 的更多信息。

【讨论】:

    【解决方案4】:

    现在可以使用 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>
    

    更多详情请见RFC thread on github

    【讨论】:

      【解决方案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));
        }
      }
      

      查看

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

      【讨论】:

        【解决方案6】:
        1. 异步管道订阅

        2. 取消下标

        3. 为 OnPush 调用 markForCheck()

          @组件({ 选择器:“产品替代”, templateUrl: './products-alt.component.html', changeDetection:ChangeDetectionStrategy.OnPush }) 导出类 ProductAltComponent 实现 OnDestroy { 页面标题:字符串=''; pageTitle$ = (new BehaviorSubject('InitialTitle'));

          constructor(private cdr: ChangeDetectorRef) {
              //Asyc pipe subscribs
              this.pageTitle$.subscribe((title: string) => {
                  this.pageTitle = title;
                  // call markForCheck
                  this.cdr.markForCheck()
              });
          }
          
          ngOnDestroy() {
              //Unsubscrib
              this.pageTitle$.unsubscribe();
          }
          

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-06-26
          • 2020-08-30
          • 2019-09-26
          • 2019-08-02
          • 2018-10-16
          • 1970-01-01
          • 1970-01-01
          • 2017-07-08
          相关资源
          最近更新 更多