【问题标题】:Using promise or async/await inside a lit-element web component在 lit-element web 组件中使用 promise 或 async/await
【发布时间】:2021-06-09 14:19:12
【问题描述】:

我有 2 个组件 - product-overviewproduct-overview-itemproduct-overview-item 是一个单独的项目,每当内置的 render() 方法在屏幕上加载 HTML 模板时,它就会在 product-overview 组件内部呈现。

我有一个带有 HATEOAS 链接数组的对象(例如,agreement),如下所示。

{
    "id": "1"
    "type": "INVEST"
    "group": "INVEST"
    "role": "HOLD"
    "accountId": "1",
    "commercialId": {
        "value": "DEFAULT",
        "type": "3000",
    },
    "productName": "Self Invest",
    "displayAlias": "Superman",
    "visible": true,
    "status": "ACTIVE"
    "balance": {
        "currency": "EUR",
        "value": "34.00",
    },
    "_links": [
        {
            "rel": "details",
            "href": "/nl/agree/192712812/investbalance",
        },
        {
            "rel": "portfolio",
            "href": "/dv/agreesecurities/981261892y9aksjakcnm",
        },
        {
            "rel": "edit",
            "href": "/mb/port/921nclaskofknsscsdwd",
        }
    ]
}

我需要对以 /nl/agreement 开头并以 /investmentbalance 结尾的 HATEOAS 链接数组中的 href 值进行 API 调用。投资余额的反应与上述反应非常相似。由此,我需要获取 balance 对象并将其传递给子组件(product-overview-item)。

balance 对象与上图类似。这是我迄今为止尝试过的。

1. product-overview.js(父组件)

_getUrl(agreement) {
    const { _links } = agreement;
    const nlAgreements = _links?.filter(link => link?.rel && link?.rel === 'details' && link?.href && link?.href.startsWith('/nl/agreements'));
    if (nlAgreements === undefined || nlAgreements.length === 0) return;
    return nlAgreements[0]?.href;
}
 
async _fetchMigratedInvestmentBalance(agreement) {
    await ajax.get(this._getUrl(agreement))
      .then(response => {
        if (!response) throw new Error('Failed to fetch balance');
        else return response;
      })
      .then(response => this.investmentBalance = response?.data?.balance)
      .catch(error => {
        throw new Error('Failed to fetch balance due to: ', error);
      });
}
 
_getProducts(status, role, tab) {
    ...
    ...
    if (this.agreementsList && this.agreementsList.length > 0) {
      let groupedAgreements = [];
      const filteredAgreements = this._filterAgreements(this.agreementsList, status, role);
      groupedAgreements = this._getGroupedAgreements(filteredAgreements);
      tab.count = filteredAgreements.length;
 
      if (groupedAgreements && groupedAgreements.length > 0) {
        const noBalanceAlert = this.isAPA ? html`
          <div class="alert"><uic-alert type="error">
          ${localize.msg('product-overview:NO_BALANCE')}
          </uic-alert></div>
        ` : '';
        return html`
        ${noBalanceAlert}
        ${groupedAgreements.map(item => html`
          <uic-expandable-item>
            ...
            <article slot="details" class="item-details productDetails">
              <ul class="list-unstyled">
                ${item.agreements.map((agreement, index) => html`
                <li class="productDetailsItem">
                  ${when(BeProductOverviewServices._isClickable((agreement.type).toUpperCase()), () => html` <a @click='${e => this._clickedToProductDetailsPage(e, item.agreements)}'
                    data-group="${item.agreementType}" data-index="${index}"
                    data-url="product-details/be/${item.agreementType}/${agreement.commercialId.value}">
                    <product-overview-item .agreement="${agreement}" @connected="${this._fetchMigratedInvestmentBalance(agreement)}" .balance="${this.investmentBalance}" .role="${role}">
                    </product-overview-item></a>`, () => html`<product-overview-item
                    .agreement="${agreement}" @connected="${this._fetchMigratedInvestmentBalance(agreement)}" .balance="${this.investmentBalance}" .role="${role}"
                    .disabled="${this.disabled}"></product-overview-item>
                  `)}
                <li>
                `)}
              </ul>
            </article>
          </uic-expandable-item>`)}`;
      `)}`;
      }
    }
    return html``;
  }

_getProducts() 函数在 render() 函数中被调用。

2。 product-overview-item.js(子组件) 在子组件中,每次在connectedCallback() 函数中加载项目时,我都会调度一个事件。

class beProductOverviewItem extends LitElement {
  static get properties() {
    return {
      agreement: { type: Object },
      balance: { type: Object, attribute: 'investment-balance', reflect: true },
      role: { type: String },
      disabled: { type: Boolean },
    };
  }
 
  static get styles() {
    return beProductOverviewStyle;
  }
 
  connectedCallback() {
    super.connectedCallback();
    this.dispatchEvent(new CustomEvent('connected'));
  }
 
  render() {
    return html`<uic-item ?disabled="${this.disabled}">
      <uic-item-header>
      <span class="bold">${this.agreement?.productName}</span>
      <span class="bold" slot="header-addon">
      ${(this.agreement?.status === 'CLOSED') ? html`
      ${localize.msg('product-overview:CLOSED')}` : (this.agreement?.type !== 'INVESTMENT' && this.agreement?.balance) ? html`
    ${formatAmountHtml(this.agreement?.balance?.value, {
    locale: localize.locale,
    style: 'currency',
    currencyDisplay: 'symbol',
    currency: this.agreement?.balance?.currency,
  })}
` : (this.agreement?.type === 'INVESTMENT' && this.balance) ? html`
  ${formatAmountHtml(this.balance?.value, {
    locale: localize.locale,
    style: 'currency',
    currencyDisplay: 'symbol',
    currency: this.balance?.currency,
  })}
  ` : ''}
 
            </uic-item>`;
  }
}

promise 在没有 undefined/pending 的情况下解析,但这次它进入了一个无限循环,不断地加载余额。有什么我们遗漏的吗?

注意:现在,我将首先声明我没有足够的经验来理解这些异步调用发生了什么。如何将 async/await 逻辑合并到我的组件中?

【问题讨论】:

  • 大多数 Lit-heads 在 Slack 频道中闲逛 Lit and Friends
  • @Danny'365CSI'Engelman - 你能在这里提出答案吗?
  • 对不起,我不是 Lit 用户...我是 vanilla JS Web Components 的朋友

标签: javascript web-component lit-element lit-html native-web-component


【解决方案1】:

试试这样的:

class ProductOverviewItem extends LitElement {
    static get properties() {
        return {
          balance: { state: true },
          loading: { type: Boolean, reflect: true },
        }
    }

    _getUrl() {
        const { _links } = this.agreement;
        const nlAgree = _links?.filter(link => link?.rel && link?.rel === 'details' && link?.href && link?.href.startsWith('/nl/agree'));
        if (nlAgree === undefined && nlAgree.length === 0) return;
        return nlAgree[0].href;
    }

    async _fetchInvestBalance() {
        await fetch(this._getUrl())
          .then(x => {
            if (!x.ok)
              throw new Error(x.status)
            else 
              return x;
          })
          .then(x => x.json())
          .then(x => this.balance = x.data?.data?.data?.balance)
          .catch(error => {
              this.error = error;
          });
    }

    render() {
      return html`
        <product-overview-item
            ?hidden="${this.loading}"
            @connected="${this._fetchInvestBalance()}"
            .agree="${agree}"
            .balance="${this.balance}"></product-overview-item>
      `;
    }
}

class ProduceOverviewItem extends LitElement {
  // ...
  connectedCallback() {
    super.connectedCallback();
    this.dispatchEvent(new CustomEvent('connected'));
  }
}

您使用内置获取 API 获取数据的位置,并将其设置为父组件上的状态,以将其传递给子组件。

【讨论】:

  • _fetchInvestBalance() 函数的调用应该在product-overview-item(子)组件加载时发生。这发生在product-overview 组件(父级)内部。因此,当列表中一个接一个地存在多个项目时,子组件会多次加载。在这里,就我而言,我将agreement 对象传递给_fetchInnvestBalance(),它在孩子加载时已经存在。像这样&lt;product-overview-item .agree="${agree}" .balance="${this._fetchInvestBalance(agreement)}"&gt;&lt;/product-overview-item&gt;
  • 根据 cmets 编辑。只需在子节点连接之前在父节点上设置“this.agreement”。
  • 一小部分开发人员也认为 Web 组件仅在可视化时效果最佳 - 将其数据处理委托给状态存储,例如但不限于 redux。这只是一个偏好,而不是作为判断。
  • @BennyPowers - 您好,我正在尝试合并您提到的更改。我正在尝试使用 ajax.get() 而不是 fetch() API。您是否也可以对ajax.get() 方法有所了解,因为这将帮助我更好地理解。
  • @BennyPowers - 我将使用代码编辑我的答案,以便您了解调用堆栈的工作方式。做出您建议的更改后,我陷入了无限循环。我没有使用fetch() Web API,而是使用ajax.get(),这是我们项目中的标准。
【解决方案2】:

我正在回答我自己的问题,因为这个问题的解决方案与上面建议的有点不同。

我的想法是在子组件中调用 API 并在 connectedCallback() 中保持余额对象就绪!

class ProductOverviewItem extends LitElement {
    static get properties() {
        return {
          balance: { state: true },
          loading: { type: Boolean, reflect: true },
        }
    }

    _getUrl() {
        const { _links } = this.agreement;
        const nlAgree = _links?.filter(link => link?.rel && link?.rel === 'details' && link?.href && link?.href.startsWith('/nl/agree'));
        if (nlAgree === undefined && nlAgree.length === 0) return;
        return nlAgree[0].href;
    }

    async _fetchInvestBalance() {
        const url = this._getUrl();
        const investmentBalance = await ajax.get(this._getUrl());
        this.balance = investmentBalance.data.balance;
    }
    
    connectedCallback() {
       super.connectedCallback();
       until(this._fetchInvestBalance(), 'Loading...');
    }

    render() {
      // displaying the balance from the property value set here.
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-01
    • 1970-01-01
    • 2020-02-02
    • 2021-03-08
    • 1970-01-01
    • 2022-07-08
    • 2019-04-17
    • 1970-01-01
    相关资源
    最近更新 更多