Вернуть Promise из activ () после загрузки пользовательских элементов
Я знаю, что Аурелия позволяет нам вернуть Promise()
от ВМ activate()
метод, и если это так, он будет ждать разрешения обещания, прежде чем перейти к новому представлению.
Но, скажем, у меня есть представление, которое состоит из одного или нескольких дочерних компонентов, и все они делают HTTP-запросы, как я могу узнать, когда все дочерние элементы закончили из моего родительского компонента?
Бонусный вопрос: это правильно, что только виртуальные машины, которые идут в <router-outlet>
использовать activate()
метод, тогда как виртуальные машины, которые используются в качестве пользовательских элементов, используют attached()
метод?
Изменить: Чтобы уточнить немного, вот как может выглядеть одна из моих страниц / маршрутов / основных видов:
<template>
<main>
<section id="item">
<h1>${item.title}</h1>
<img src="${item.image}">
<p>${item.description}</p>
<ul>
<li repeat.for="si of item.subItems">
${si.subItem}
</li>
</ul>
</section>
<aside>
<author-info></author-info>
<recent-items limit="3"></recent-items>
<random-quote></random-quote>
</aside>
</main>
</template>
Я могу легко ждать ${item}
загрузить и затем разрешить обещание в главном представлении activate
метод, но это не гарантирует, что три дочерних элемента в aside
загрузили. Это заставляет их всплывать один за другим, и это не выглядит великолепно.
Я бы очень хотел использовать встроенную функциональность Aurelia, если это вообще возможно, но я думаю, что мне, возможно, придется прибегнуть к своему собственному загрузчику с помощью EventAggregator или двусторонней привязки, как предложила Эшли.
Хотелось бы услышать от кого-то из команды Aurelia, возможно ли это вообще?
3 ответа
Я не уверен в такой сложной цепи с activate
а также attached
, но вы всегда можете использовать пользовательские события.
В activate
родительского элемента:
return new Promise((resolve, reject) => {
var subscription = this.eventAggregator.subscribe('child-element-loaded', e => {
subscription.dispose();
resolve();
});
});
А в пользовательском элементе вам нужно будет запускать это событие при необходимости:
this.eventAggregator.publish('child-element-loaded');
И, конечно же, вам нужно будет импортировать Event Aggregator
import {EventAggregator} from 'aurelia-event-aggregator';
И внедрить его в дочерние и родительские элементы.
Загрузить все данные в маршрут activate()
Перезвоните
Как заметила Эшли в комментарии выше, лучшая стратегия состоит в том, чтобы загрузить все данные в родительском маршруте и передать эти данные в пользовательский элемент с помощью привязок. Вы упомянули, что это приведет к копированию / вставке кода в каждый маршрут, содержащий этот элемент, но мы можем решить эту проблему, переместив код загрузки в класс обслуживания и внедрив этот класс в маршрут. Таким образом, мы можем сохранить код сухим, а также сделать его простым и читаемым.
Мы продемонстрируем, создав <random-quote>
пользовательский элемент вместе с сервисом, который будет предоставлять нам случайные кавычки.
randomQuoteCustomElement.ts
export class RandomQuoteCustomElement {
@bindable quote: IQuote;
}
randomQuoteCustomElement.html
<template>
<i>${quote.text}</i>
<strong>${quote.author}</strong>
</template>
randomQuoteService.ts
export class RandomQuoteService {
getRandomQuote: Promise<IQuote>() {
return this.http.fetch('api/quote/random')
.then((response) => response.json())
});
}
}
Далее мы включим пользовательский элемент в наше представление, добавим сервис в нашу модель представления, получим данные через сервис и получим наш activate()
Метод зависит от возвращаемого обещания.
main.ts
import { RandomQuoteService} from './randomQuoteService';
@inject(RandomQuoteService)
export class MainViewModel {
quote: IQuote;
constructor(quotes: RandomQuoteService) {
this.quotes = quotes;
}
activate() {
return Promise.all([
this.quotes.getRandomQuote()
.then((quote) => this.quote = quote)
]);
}
}
main.html
<template>
<require from="./randomQuoteCustomElement"></require>
<random-quote quote.bind="quote"></random-quote>
</template>
Потому что мы хотим, чтобы наши activate()
функция сильно зависит от результатов RandomQuoteService
нам нужно включить этот код непосредственно в наш activate()
Перезвоните. Мы также можем спроектировать пользовательский элемент, чтобы разрешить привязку данных, но вернемся к извлечению собственных данных, используя тот же сервис.
randomQuoteCustomElement.ts
export class RandomQuoteCustomElement {
@bindable quote;
constructor(quotes) {
if (!this.quote) {
quotes.getRandomQuote()
.then((quote) => !this.quote && this.quote = quote);
}
}
}
Вот рабочий пример: https://gist.run/?id=c5570192afe5631355efe6b5da3e44b5
Если у вас есть несколько HTTP-вызовов, которые должны быть возвращены до рендеринга представления, вы можете использовать Promise.all
, а затем связать результаты с дочерними компонентами. Например:
activate () {
let promise1 = new Promise()...;
let promise2 = new Promise()...;
let promise3 = new Promise()...;
return Promise.all([promise1, promise2, promise3]);
//bind the results to children components
}
В этом случае, activate()
буду ждать всех обещаний. Однако, как сказал @AshleyGrant в своем комментарии, вы должны быть осторожны с этим. Это может привести к медленному процессу.