Вернуть 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 в своем комментарии, вы должны быть осторожны с этим. Это может привести к медленному процессу.

Другие вопросы по тегам