简体   繁体   English

Angular 5 Observable,完成后返回

[英]Angular 5 Observable, return when complete

Help with Angular 5 Observables. 帮助Angular 5 Observables。

I want to hide the complexity from the controller; 我想隐藏控制器的复杂性; that is to not make it have to subscribe to the observable. 那就是不要让它必须订阅observable。 Have all the complexity in the service and have it simply return the results/payload to the component. 拥有服务中的所有复杂性,并将结果/有效负载简单地返回到组件。 But obviously I'm getting stuck in a timing issue. 但显然我陷入了时间问题。 I feel as if this should be a popular thing/topic, but haven't found the answer. 我觉得这应该是一个受欢迎的事情/主题,但还没有找到答案。 So perhaps I'm going about things wrong? 所以也许我说错了什么?

//in the component.ts
const allColors = this.auth.getColors(); //service call
console.log(allColors); // returns undefined

//in the service.ts
getColors() {
  var myColors = "";

  var colors = this.http.get<any>('http://localhost/Account/GetColors', httpOptions)
    .pipe(catchError(this.handleError));

  //
  colors.subscribe(
    res => {
      myColors = res.body;
    },
    error => {}
  );

  return myColors;
}

I think you can't do what you wanna do, because until the subscribe in your service doesn't get resolved it won't send anything to the component, in the execution of the component it's undefined cuz the response haven't come yet. 我认为你不能做你想做的事情,因为直到你的服务中的订阅没有得到解决它不会向组件发送任何东西,在组件的执行中它是未定义的,因为响应尚未到来。

That's why callback functions are invented. 这就是发明回调函数的原因。 The correct way to do it is having the subscribe in your component cuz you want to wait for the response of the service. 正确的方法是在组件中进行订阅,因为您希望等待服务的响应。

Sorry but I cannot use comments yet so I had to make this reply. 抱歉,我还不能使用评论,所以我不得不做出这个回复。 Hope it helps 希望能帮助到你

So perhaps I'm going about things wrong? 所以也许我说错了什么?

Indeed. 确实。 Embrace asynchronous programming with Observables and Promises. 使用Observables和Promises包含异步编程。 I believe that is the only reason you are asking this question, you do not fully understand how they work so you want to abstract it away which is something you can't do. 我相信这是你提出这个问题的唯一原因,你并不完全明白它们是如何工作的,所以你想把它抽象出来,这是你不能做的事情。 See also How do I return the response from an asynchronous call? 另请参见如何从异步调用返回响应?


That said you could use async/await which can give the appearance of synchronous code. 那说你可以使用async / await ,它可以提供同步代码的外观

component.ts component.ts

async ngOnInit() {
  const allColors = await this.auth.getColorsAsync(); //service call
  console.log(allColors); // returns color array
}

service.ts service.ts

getColorsAsync(): Promise<color[]> {
  return this.http.get<color[]>('http://localhost/Account/GetColors', httpOptions)
    .pipe(catchError(this.handleError))
    .toPromise();
}

I guessed as to the actual type. 我猜到了实际的类型。 Ideally you should use a strong type over any when possible 理想情况下,您应尽可能使用强类型

When will your method return? 你的方法什么时候回来? That's the question you need to answer, and to spare you the suspense it will return prior to myColors being filled with anything, and that's the nature of asynchronous programming. 这是你需要回答的问题,并且为了避免 myColors充满任何东西之前它会返回的悬念,这就是异步编程的本质。

You need to return the observable and let it notify you when the results are available. 您需要返回observable并在结果可用时通知您。 This complexity cannot be hidden in the way you are trying, the closest you can get is to pass a callback function into your service method that will then be called with the results when they are available, something like this: 这种复杂性无法以您尝试的方式隐藏,您可以获得的最接近的是将回调函数传递给您的服务方法,然后在结果可用时调用它们,如下所示:

getColors(callback) {
    //...

    colors.subscribe(
       res => {
           callback(res.body);
       }
       //...
    );
}

But then you'll miss out on a lot of cool functionality, such as being able to have multiple subscribers to the same observable. 但是你会错过许多很酷的功能,例如能够拥有同一个observable的多个订阅者

You want to use a Resolver and routing to pre-load the data before the service is used. 您希望在使用服务之前使用Resolver和路由来预加载数据。

A resolver is an asynchronized action that is finished before the rest of the routes are activated. 解析器是一个异步操作,在激活其余路由之前完成。 So you would define a top-level route that doesn't do anything, but contains the resolver to fetch the color data. 因此,您将定义一个不执行任何操作的顶级路由,但包含用于获取颜色数据的解析程序。 I say top-level because I assume this is global application data. 我说顶级因为我认为这是全局应用程序数据。 If this data is specific to a feature then place the resolver in the correct routing path. 如果此数据特定于某个功能,则将解析程序放在正确的路由路径中。

const routes: Routes = [
    { 
       path: '',
       resolve: {
           Colors: ColorsResolver
       },
       children: [
             // your application routes here
       ]
    }
] 

The class ColorsResolver would fetch the color data from the server, and then assign it to the service. ColorsResolver类将从服务器获取颜色数据,然后将其分配给服务。

@Injectable()
export class ColorsResolver implements Resolve<any> {
     public constructor(private colorService: ColorService) {}

     public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
          return this.http.get<any>('http://localhost/Account/GetColors', httpOptions)
                  .do((resp)=> this.colorService.setColor(resp));;
     }
}

In your service you just assign the value to a property 在您的服务中,您只需将值分配给属性即可

 @Injectable()
 public ColorService {
     public setColor(resp: any) { this._color = resp; }
     public getColor():any { return this._color; }
 }

You can now call getColor() many times after the resolver has finished without it blocking. 现在,您可以在解析器完成后多次调用getColor()而不会阻塞它。

Going off of what Osakr said. 关闭Osakr所说的话。 You will have to subscribe in your component. 您必须在您的组件中订阅。

In your service.ts 在你的service.ts

getColors(): Observable<any> {

    let urlString = 'http://localhost/Account/GetColors'

    return this.http.get(urlString);
  }

In your component: 在您的组件中:

getColors() {

    this.service.getColors()
      .subscribe((colors) => {

      this.myColors = colors;

  })
}

I think you can do it in a different way 我想你可以用不同的方式做到这一点

//in the component.ts
this.service.getColors(); //service call
console.log(this.service.allColors); // print the service property.  which will resolve after the oberservable resolve

//in the service.ts
@injectable()
export class Service {
  public allColors: any; //Public property to print.
  public getColors() {
    var myColors = "";

    var colors = this.http.get<any>('http://localhost/Account/GetColors', httpOptions)
    .pipe(catchError(this.handleError));

    colors.subscribe(
      res => {
         this.allColors= res.body;
      },
      error => {}
     );

    return;
  }
}

Here's how I would do it: 我是这样做的:

this.http.get<any>('http://localhost/Account/GetColors', httpOptions)
  .pipe(catchError(this.handleError))
  .subscribe(res => {
    console.log(res.body);
  });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM