Дочерний элемент LWC кэширует устаревшие данные с помощью запроса @wired Apex, родительский элемент не может обновитьApex () для дочернего элемента с помощью querySelector (), поскольку дочерний элемент скрыт с помощью if: true. Что делать?

Aug 17 2020

Краткая структура приложения (см. изображение): справа список дочерних компонентов «Линия» (на фото всего один 1601xCC5Bulk), слева контейнер с поддонами. << Allocate btn открывает другой дочерний модальный файл с формой (if: true ..). В раскрывающемся списке этой формы используется @wire apex для отображения только пустых поддонов, поэтому он обновляется самим контроллером формы после DML.

Проблема возникает при изменении количества поддонов родителем, например, с помощью красной кнопки справа. этот DML удаляет контейнер вместе с поддонами.

Но если скрытая форма снова открывается, старые значения поддонов все еще присутствуют в качестве значений раскрывающегося списка, мне нужно обновить эти данные. но этот дочерний элемент скрыт, поэтому по стандарту недоступен: parent -> querySelector (child2) .method ()

  • Извините за новый вопрос: есть ли способ получить доступ к скрытым методам компонента?
  • Я также думал о том, чтобы переместить вызов Apex-запроса в родительский компонент (легко запустить), а затем передать его дочернему модальному модулю в качестве аргумента api, поэтому при появлении модального окна будут обновляться данные. это кажется немного неправильным, поскольку родители снимают бремя с ребенка (возможно, я ошибаюсь)
  • Я также думал об использовании дочернего renderedCallback для этого запроса вершины, но боюсь бесконечных циклов рендеринга?
  • Отключение кеширования auraEnabled apex было бы неплохо, но кажется невозможным.

Вот простая разметка, показывающая только все задействованные части

=== РОДИТЕЛЬ (с 2 детьми) ===

HTML

<template for:each={lineItems.data} for:item="lineItem">
   <lightning-layout-item key={lineItem.Id} size="12">
     <c-packing-list-line-item-card lineitem={lineItem} onallocatebuttonpress={openAllocationModal}></c-packing-list-line-item-card>
   </lightning-layout-item>
</template>

<template if:true={isLineItemModalOpen}>
     <c-packing-list-allocation-modal recordid={recordId} oncloseallocationmodal={closeAllocModal} onproductallocated={refreshData}></c-packing-list-allocation-modal>    
</template>

JS

openAllocationModal(e) {
        this.editItemId = e.detail;
        this.isLineItemModalOpen = true;
    }
closeAllocModal() {
        this.isLineItemModalOpen = false;
    }

=== РЕБЕНОК 1: упаковка-список-строка-элемент-карточка === (тот, что справа, показывает детали продукта, передает идентификатор в модальное окно)

HTML

<lightning-button variant="brand" label="<< Allocate" data-itemid={lineitem.Id} onclick={openAllocationModal}></lightning-button>

JS

openAllocationModal(e) {
        const selectEvent = new CustomEvent('allocatebuttonpress', { detail: e.target.dataset.itemid });
        this.dispatchEvent(selectEvent);
    }

=== РЕБЕНОК 2: модальное распределение (условное отображение) ===

HTML

<template if:true={showInsertButton}>
                <button class="slds-button slds-button_brand" onclick={handleAllocationInsert}>Insert</button>
            </template>

JS (вас, вероятно, интересует только closeModal () {}, остальное - для полноты)

handleAllocationInsert() {
[...]
createRecord(recordInput)
.then((allocId) => {
  this.showToast('success', 'Success', 'Product Allocated');
  this.handleInsert(this.palletValue);
  this.refreshPallets();
})
.catch((error) => {
  this.showToast('error', 'Error creating record', error.body.message);
  this.closeModal();
});

}

closeModal() {
        // On save parent closes modal
        console.log('closing 1');
        const selectEvent = new CustomEvent('closeallocationmodal');
        this.dispatchEvent(selectEvent);
        this.spinner = false;
    }

    handleInsert(palletValue) {
        // On save parent refreshes container view
        const selectEvent = new CustomEvent('productallocated', {
            detail: this.palletcontainers[palletValue].containerId
        });
        this.dispatchEvent(selectEvent);
        this.closeModal();
    }

Ответы

4 sfdcfox Aug 17 2020 at 20:36

Извините за новый вопрос: есть ли способ получить доступ к скрытым методам компонента?

Нет . Ребенок не физически удаляется из DOM и больше не существует вообще (кроме , возможно , в компоненте кэша).

Я также думал о том, чтобы переместить вызов Apex-запроса в родительский компонент (легко запустить), а затем передать его дочернему модальному модулю в качестве аргумента api, поэтому при появлении модального окна будут обновляться данные. это кажется немного неправильным, поскольку родители снимают бремя с ребенка (возможно, я ошибаюсь)

На самом деле это очень стандартный способ сделать это. Ребенку нужно беспокоиться только о том, чтобы показать данные, для чего он предназначен. Как правило, рекомендуется получать / устанавливать данные как можно выше в иерархии компонентов (но не выше, чем необходимо).

Я также думал об использовании дочернего renderedCallback для этого запроса вершины, но боюсь бесконечных циклов рендеринга?

Плохая идея, вы получите бесконечные циклы, как и ожидаете. Однако здесь может иметь смысл использование connectedCallback компонента. Это событие вызывается только тогда, когда компонент добавляется или перемещается в DOM, поэтому у него не будет проблем с бесконечным циклом.

Отключение кеширования auraEnabled apex было бы неплохо, но кажется невозможным.

Это вполне возможно, но я считаю это последним средством. В большинстве случаев данные не меняются, пока пользователь что-то не сделает, поэтому кеширование может повысить производительность. Тем не менее, ваш дизайн, похоже, не требует частых вызовов, поэтому отключение кеширования, вероятно, также приемлемо.


В заключение я бы сказал, что управление данными от родителя было бы самым простым решением. Это также общеприемлемое решение в большинстве случаев, поскольку дочерние компоненты обычно работают только с данными, которые им переданы от родителя. Например, lightning-inputничего не делает с сервером напрямую, но передает изменения родительскому объекту через события. Это совершенно естественный дизайн.

Если вы хотите, чтобы дети продолжали выполнять работу, это тоже нормально, просто вызовите refreshApex в своем connectedCallback или отключите кеширование. Любой из них должен решить вашу проблему, хотя вы можете протестировать на большом наборе данных, чтобы увидеть, какие из доступных вам вариантов предлагают наилучшую производительность.