使用事件发射器和服务进行跨组件通信的角度异步调用

无法将从订阅方法接收的值存储在模板变量中。

照片细节组件

import { Component,OnInit,Input } from "@angular/core";
import { PhotoSevice } from "../photo.service";
import { Photo } from "src/app/model/photo.model";

@Component({
  selector: "app-photo-detail",templateUrl: "./photo-detail.component.html",styleUrls: ["./photo-detail.component.css"]
})
export class PhotoDetailComponent implements OnInit {

  url: string;

  constructor(private photoService: PhotoSevice) {
    this.photoService.photoSelected.subscribe(data => {
      this.url = data;
      console.log(this.url);
    });
    console.log(this.url);
  }

  ngOnInit() {

  }
}

外部console.log给出了未定义的内容,视图中未呈现任何内容,但是在subscibe方法内部,我可以看到该值。那么,如何在视图中显示它?

照片组件

import { Component,OnInit } from "@angular/core";
import { activatedRoute,Params,Router } from "@angular/router";
import { FnParam } from "@angular/compiler/src/output/output_ast";
import { AlbumService } from "../service/album.service";
import { Photo } from "../model/photo.model";
import { PhotoSevice } from "./photo.service";

@Component({
  selector: "app-photos",templateUrl: "./photos.component.html",styleUrls: ["./photos.component.css"]
})
export class PhotosComponent implements OnInit {
  selectedAlbumId: string;
  photoList: Photo[] = [];
  photoSelected: Photo;
  isLoading: Boolean;
  constructor(
    private rout: activatedRoute,private albumService: AlbumService,private router: Router,private photoService: PhotoSevice
  ) { }

  ngOnInit() {
    this.isLoading = true;
    this.rout.params.subscribe((params: Params) => {
      this.selectedAlbumId = params["id"];
      this.getPhotos(this.selectedAlbumId);
    });
  }

  getPhotos(id: string) {
    this.albumService.fetchPhotos(this.selectedAlbumId).subscribe(photo => {
      this.photoList = photo;
      this.isLoading = false;
    });
  }

  displayPhoto(url: string,title: string) {

    console.log(url);
    this.photoService.photoSelected.emit(url);
    this.router.navigate(["/photo-detail"]);
  }
}

请向我解释这是如何工作的以及如何解决它,以便我可以在模板视图中存储和显示从订阅和异步调用接收的值。

这是两个组件的视图-

photo.component.html

<div *ngIf="isLoading">
  <h3>Loading...</h3>
</div>

<div class="container" *ngIf="!isLoading">
  <div class="card-columns">
    <div *ngFor="let photo of photoList" class="card">
      <img
        class="card-img-top"
        src="{{ photo.thumbnailUrl }}"
        alt="https://source.unsplash.com/random/300x200"
      />
      <div class="card-body">
        <a
          class="btn btn-primary btn-block"
          (click)="displayPhoto(photo.url,photo.title)"
          >Enlarge Image</a
        >
      </div>
    </div>
  </div>
</div>

photo-detail.component.ts

<div class="container">
  <div class="card-columns">
    <div class="card">
      <img class="card-img-top" src="{{ url }}" />
    </div>
  </div>
</div>

photo.service.ts

import { Injectable } from "@angular/core";
import { EventEmitter } from "@angular/core";

@Injectable({ providedIn: "root" })
export class PhotoSevice {
  photoSelected = new EventEmitter();
 // urlService: string;
}

这是我的github存储库的链接,我将代码保留在注释中,并在那里使用了另一种方法。 如果您检查相册组件,那么我也已经订阅了http请求,并在相册组件的模板变量中分配了值。 还有该值作为未定义的subscibe方法,但我能够在模板中访问它。

https://github.com/Arpan619Banerjee/angular-accelerate

以下是相册组件和服务的详细信息 请将此与事件发射器的情况进行比较,并向我解释有什么区别- albums.component.ts

import { Component,OnInit } from "@angular/core";
import { AlbumService } from "../service/album.service";
import { Album } from "../model/album.model";

@Component({
  selector: "app-albums",templateUrl: "./albums.component.html",styleUrls: ["./albums.component.css"]
})
export class AlbumsComponent implements OnInit {
  constructor(private albumService: AlbumService) {}
  listAlbums: Album[] = [];
  isLoading: Boolean;

  ngOnInit() {
    this.isLoading = true;
    this.getalbums();
  }

  getalbums() {
    this.albumService.fetchAlbums().subscribe(data => {
      this.listAlbums = data;
      console.log("inside subscibe method-->" + this.listAlbums); // we have data here
      this.isLoading = false;
    });
    console.log("outside subscribe method----->" + this.listAlbums); //empty list==== but somehow we have the value in the view,this doesn t work
    //for my photo and photo-detail component.
  }
}

albums.component.html

<div *ngIf="isLoading">
  <h3>Loading...</h3>
</div>
<div class="container" *ngIf="!isLoading">
  <h3>Albums</h3>
  <app-album-details
    [albumDetail]="album"
    *ngFor="let album of listAlbums"
  ></app-album-details>
</div>

album.service.ts

import { Injectable } from "@angular/core";
import { HttpClient,HttpParams } from "@angular/common/http";
import { map,tap } from "rxjs/operators";
import { Album } from "../model/album.model";
import { Observable } from "rxjs";
import { username } from "../model/user.model";

@Injectable({ providedIn: "root" })
export class AlbumService {
  constructor(private http: HttpClient) {}
  albumUrl = "http://jsonplaceholder.typicode.com/albums";
  userUrl = "http://jsonplaceholder.typicode.com/users?id=";
  photoUrl = "http://jsonplaceholder.typicode.com/photos";

  //get the album title along with the user name
  fetchAlbums(): Observable<any> {
    return this.http.get<Album[]>(this.albumUrl).pipe(
      tap(albums => {
        albums.map((album: { userId: String; username: String }) => {
          this.fetchUsers(album.userId).subscribe((user: any) => {
            album.username = user[0].username;
          });
        });
        // console.log(albums);
      })
    );
  }

  //get the user name of the particular album with the help of userId property in albums
  fetchUsers(id: String): Observable<any> {
    //let userId = new HttpParams().set("userId",id);
    return this.http.get(this.userUrl + id);
  }

  //get the photos of a particular album using the albumId
  fetchPhotos(id: string): Observable<any> {
    let selectedId = new HttpParams().set("albumId",id);
    return this.http.get(this.photoUrl,{
      params: selectedId
    });
  }
}

使用事件发射器和服务进行跨组件通信的角度异步调用

我已经按照注释中的说明在偶数发射器中添加了控制台日志,这就是我所期望的行为。

SCORPIOYUKI 回答:使用事件发射器和服务进行跨组件通信的角度异步调用

我认为您正在尝试在照片详细信息组件中显示选定的图像,该组件将从服务中显示照片。

这个问题没有提到您如何创建照片细节组件。

  1. 是在用户选择要展示的照片之后创建的组件吗?
  2. 是否在用户选择要显示的照片之前就创建了组件?

我认为第一个是您要尝试做的。 如果是这样,那么有两件事……

  1. 在构造函数内部进行订阅时,订阅中的代码会在观察到的消息发出一段时间后运行。同时,订阅后的代码(即console.log(url)(外部代码))将运行,因此未定义。

  2. 如果订阅在发出事件之后发生,即您已经发出带有url的事件,但是到那时组件尚未订阅服务事件。因此事件丢失了,您一无所获。为此,您可以做几件事

    a。将要显示其详细信息的照片添加到url中,并在“照片详细信息”组件中获取它。

    b。将服务中的主题/事件发射器转换为行为主题。这样可以确保,即使您在以后的某个时间订阅,您仍然可以得到上一次发出的事件。

    c。如果照片详细信息组件在照片组件的模板内,则将URL作为输入参数发送(@Input()绑定)。

希望这会有所帮助

,

问题是两部分的。

第1部分-照片和照片细节部分

  1. EventEmitter用于将由@Output装饰器修饰的变量从子组件 (不是 service )发射到父组件。然后,它可以由 template 中的父组件绑定。可以找到一个简单而良好的示例here。请注意应用组件模板中的(notify)="receiveNotification($event)"

  2. 对于您的情况,使用SubjectBehaviorSubject是更好的主意。它们之间的差异可以在我的其他答案here中找到。尝试以下代码

photo.service.ts

import { Injectable } from "@angular/core";
import { BehaviorSubject } from 'rxjs';

@Injectable({ providedIn: "root" })
export class PhotoSevice {
  private photoSelectedSource = new BehaviorSubject<string>(undefined);

  public setPhotoSelected(url: string) {
    this.photoSelectedSource.next(url);
  }

  public getPhotoSelected() {
    return this.photoSelectedSource.asObservable();
  }
}

photos.component.ts

export class PhotosComponent implements OnInit {
  .
  .
  .
  displayPhoto(url: string,title: string) {
    this.photoService.setPhotoSelected(url);
    this.router.navigate(["/photo-detail"]);
  }
}

photo-detail.component.ts

  constructor(private photoService: PhotoSevice) {
    this.photoService.getPhotoSelected().subscribe(data => {
      this.url = data;
      console.log(this.url);
    });
    console.log(this.url);
  }

photo-detail.component.html

<ng-container *ngIf="url">
  <div class="container">
    <div class="card-columns">
      <div class="card">
        <img class="card-img-top" [src]="url"/>
      </div>
    </div>
  </div>
</ng-container>

第2部分-相册组件和服务

调用this.albumService.fetchAlbums()返回可观察到的HTTP GET响应。您正在订阅它,并更新成员变量值并在模板中使用它。

根据您对其他答案的评论:

我了解行为以及为何外部console.log是 不够细致,因为执行上下文是异步调用的差异 然后先执行同步代码,然后是异步代码

恐怕同步和异步调用之间的区别并不那么简单。请参阅here,了解它们之间的区别。

albums.components.ts

  getAlbums() {
    this.albumService.fetchAlbums().subscribe(data => {
      this.listAlbums = data;
      console.log("inside subscibe method-->" + this.listAlbums); // we have data here
      this.isLoading = false;
    });
    console.log("outside subscribe method----->" + this.listAlbums); //empty list==== but somehow we have the value in the view,this doesn t work
    //for my photo and photo-detail component.
  }

albums.component.html

<div *ngIf="isLoading">
  <h3>Loading...</h3>
</div>
<div class="container" *ngIf="!isLoading">
  <h3>Albums</h3>
  <app-album-details
    [albumDetail]="album"
    *ngFor="let album of listAlbums"
  ></app-album-details>
</div>

问题是要解释为什么console.log("outside subscribe method----->" + this.listAlbums);打印undefined时模板仍显示相册。简而言之,当您在外部控制台日志中进行操作时,this.listAlbums实际上是undefined,因为它尚未初始化。但是在模板中,有一个加载检查*ngIf="!isLoading"。并且从控制器代码中,仅在为isLoading分配值时,false才被设置为listAlbums。因此,当您将isLoading设置为false时,可以确保listAlbums包含要显示的数据。

本文链接:https://www.f2er.com/2604124.html

大家都在问