【问题标题】:Angular template binding with Observable async pipe issue [duplicate]带有可观察异步管道问题的角度模板绑定[重复]
【发布时间】:2020-08-29 11:10:56
【问题描述】:

注意我在Template binding with function return Observable and async pipe创建了这个问题的简化版

模板:

<div *ngIf="entity?.ext.insuredDetails.insuredType$() | async as insuredType">
 {{insuredType}}
</div>

insuredType$ 定义:

@NeedsElement(sp(115621),ap(116215))
insuredType$(): Observable<string> {
  return empty();
}

NeedsElement装饰者:

export function NeedsElement(...mappings: NeedsElementMapping[]) {
  if (mappings.length === 0) {
    throw new Error('needs mapping expected');
  }

  let lookup = new Map<ProductId, number>();
  mappings.forEach((mapping) => {
    lookup.set(mapping.productId, mapping.elementId);
  });

  return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
    descriptor.value = function (...args: any[]) {
      Logger.info("bbbbb");
      let entity = UcEntityStoreContext.currentEntity;
      let productId = entity['productId'];
      if (!productId) {
        throw new Error(`Cannot get product Id from host entity: ${entity.ucId}`);
      }
      let elementId: number = lookup.get(entity['productId']);
      if (!elementId) {
        throw new Error(`Cannot locate needs element ID by productId ${productId}`);
      };
      let enitityStore = UcEntityStoreContext.current;
      let entityApi = enitityStore.apiService as QuotePolicyApiBase<any>;
      let needsDefApi = NeedsDefinitionApi.instance;

      return needsDefApi.fetchOne(productId, elementId).pipe(
        concatMap(
          nd => {
            return entityApi.fetchNeedsElementValue(entity.ucId, elementId).pipe(
              concatMap(needsVal => {
                if (!needsVal) {
                  return of("");
                }
                if (nd.lookupId) {
                  return LookupApi.instance.getByPrimaryValueId(nd.lookupId, needsVal).pipe(
                    map(res => res.primaryValue)
                  );
                } else {
                  return of(needsVal);
                }
              })
            )
          }
        )
      );
    };
  };
}

问题是装饰器被多次调用:

如果它去这个分支:

然后它继续向后端服务发送请求,并且绑定永远不会输出任何内容:

如果它是异步可观察对象,它似乎会一直尝试评估可观察对象而不会结束,比如这个:


2020 年 5 月 14 日更新

我从Template binding with function return Observable and async pipe得到了答案

最后我将方法装饰器更改为属性装饰器并修复了问题。

【问题讨论】:

    标签: angular rxjs rxjs6 rxjs-observables


    【解决方案1】:

    当您使用 insuredType$() | async 之类的东西时,这意味着每次发生更改检测时 Angular 都会调用此函数。因此它每次都会调用needsDefApi.fetchOne(productId, elementId)

    为避免这种情况,您需要将您的组件标记为OnPush。什么实际上是减少调用量的救命稻草,因为只有在组件的输入或触发输出发生更改的情况下才会调用它。如果它经常发生 - 这将无济于事。

    或者您需要重组装饰器以在任何调用相同的entity 时返回相同的Observable,因此entity?.ext.insuredDetails.insuredType$() === entity?.ext.insuredDetails.insuredType$() 将是正确的。

    不确定它是否有效,但应该与它相似:

    export function NeedsElement(...mappings: NeedsElementMapping[]) {
        if (mappings.length === 0) {
            throw new Error('needs mapping expected');
        }
    
        let lookup = new Map<ProductId, number>();
        mappings.forEach((mapping) => {
            lookup.set(mapping.productId, mapping.elementId);
        });
    
        Logger.info("bbbbb");
        let entity = UcEntityStoreContext.currentEntity;
        let productId = entity['productId'];
        if (!productId) {
            throw new Error(`Cannot get product Id from host entity: ${entity.ucId}`);
        }
        let elementId: number = lookup.get(entity['productId']);
        if (!elementId) {
            throw new Error(`Cannot locate needs element ID by productId ${productId}`);
        };
        let enitityStore = UcEntityStoreContext.current;
        let entityApi = enitityStore.apiService as QuotePolicyApiBase<any>;
        let needsDefApi = NeedsDefinitionApi.instance;
    
        const stream$ = needsDefApi.fetchOne(productId, elementId).pipe(
            concatMap(
                nd => {
                    return entityApi.fetchNeedsElementValue(entity.ucId, elementId).pipe(
                        concatMap(needsVal => {
                            if (!needsVal) {
                                return of("");
                            }
                            if (nd.lookupId) {
                                return LookupApi.instance.getByPrimaryValueId(nd.lookupId, needsVal).pipe(
                                    map(res => res.primaryValue)
                                );
                            } else {
                                return of(needsVal);
                            }
                        })
                    )
                }
            )
        );
    
        return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
            descriptor.value = function (...args: any[]) {
                return stream$; // <- returns the same stream every time.
            };
        };
    }
    

    【讨论】:

    【解决方案2】:

    Template binding with function return Observable and async pipe得到答案

    解决方法是使用PropertyDecorator而不是MethodDecorator,所以现在被保险的Type$是:

    @NeedsElement(sp(115623),ap(116215))
    readonly insuredType$: Observable<any>;
    

    装饰器现在是

    export function NeedsElement(...mappings: NeedsElementMapping[]) {
      ...
      const observable = of('').pipe(switchMap(() => {
        ...
      })
      return (target: any, propertyKey: string) => {
        const getter = () => {
          return observable;
        };
        Object.defineProperty(target, propertyKey, {
          get: getter,
          enumerable: true,
          configurable: true,
        });
      };
    }
    

    注意必须在返回函数之外定义observable,否则它仍然会陷入死循环,说下面的代码不起作用:

    export function NeedsElement(...mappings: NeedsElementMapping[]) {
      ...
      return (target: any, propertyKey: string) => {
        const getter = () => {
          return of('').pipe(switchMap(() => {
            ...
          });
        };
        Object.defineProperty(target, propertyKey, {
          get: getter,
          enumerable: true,
          configurable: true,
        });
      };
    }
    

    【讨论】:

      猜你喜欢
      • 2018-11-19
      • 1970-01-01
      • 1970-01-01
      • 2019-06-27
      • 1970-01-01
      • 1970-01-01
      • 2021-11-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多