Как последовательно загружать угловые компоненты на страницу?

Мой сценарий проблемы выглядит следующим образом,

У меня есть страница с большой формой (+100 входов). Эти входные данные разделены на разделы, и эти разделы разрабатываются как разные компоненты (для повторного использования). Итак, общий вид страницы примерно такой:

<div>
    <form-section-a></form-section-a>
    <form-section-b></form-section-b>
    <form-section-c></form-section-c>
    ...
    <form-section-z></form-section-z>
</div>

Обработка каждого раздела формы займет некоторое время, потому что большинство входных данных представляют собой поля выбора материала, а необходимые данные необходимо загрузить из REST API и обработать.

В приведенном выше макете angular попытается отобразить все разделы формы одновременно, тем самым суммируя время обработки каждого раздела, что приведет к зависанию браузера.

Итак, мой план состоит в том, чтобы загружать разделы один за другим. Есть ли рекомендуемый способ добиться этого?

Я попытался написать структурную директиву для загрузки компонентов один за другим. Несмотря на то, что директива работает, я никак не могу узнать, когда компонент завершил свою внутреннюю обработку (может быть, хук AfterViewInit). Директива выглядит примерно так,

<div tcSequentialRenderer>
    <form-section-a *tcIfPreviousBlockLoaded></form-section-a>
    <form-section-b *tcIfPreviousBlockLoaded></form-section-b>
    <form-section-c *tcIfPreviousBlockLoaded></form-section-c>
    ...
    <form-section-z *tcIfPreviousBlockLoaded></form-section-z>
</div>

.

@Directive({selector: '[tcSequentialRenderer]'})
export class SequentialRendererDirective implements AfterViewInit {

  @ContentChildren(IfPreviousBlockLoadedDirective) directives: QueryList<IfPreviousBlockLoadedDirective>;

  ngAfterViewInit() {
    // start from first item
    if (this.directives.length > 0) {
      this.directives.toArray()[0].show();
    }

    let directivesArray = this.directives.toArray();
    for (let i = 1; i < directivesArray.length; i++) {
      directivesArray[i - 1].done.subscribe(status => {
        if (status) {
          directivesArray[i].show();
        }
      });
    }
  }
}

.

@Directive({selector: '[tcIfPreviousBlockLoaded]'})
export class IfPreviousBlockLoadedDirective {

  private isShown = false;

  public done: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

  constructor(
    private tplref: TemplateRef<any>,
    private vcref: ViewContainerRef
  ) { }

  public show(): void {
    if (!this.isShown) {
      this.vcref.createEmbeddedView(this.tplref);
      this.isShown = true;
    }
  }
}

Если я каким-то образом смогу получить доступ к связанному компоненту из IfPreviousBlockLoadedDirective, этот метод будет работать без проблем.

Есть ли какие-либо предложения по исправлению этого или есть другие способы добиться этого без изменения компонентов раздела формы?

ПРИМЕЧАНИЕ. Секции формы могут быть любым угловым компонентом.


person charith.arumapperuma    schedule 19.11.2018    source источник


Ответы (3)


Ты заходишь слишком далеко ради такого малого.

Используйте эмиттеры событий, привязанные к AfterViewInit хукам:

  <div>
    <form-section-a (viewLoaded)="displaySectionB = true"></form-section-a>
    <form-section-b *ngIf="displaySectionB" (viewLoaded)="displaySectionC = true"></form-section-b>
</div>
@Output() viewLoaded = new EventEmitter();
ngAfterViewInit() { this.viewLoaded.next(true); }

Поскольку ваши компоненты зависят друг от друга, вам придется явно объявить условия.

Есть более продвинутые способы сделать это (например, с помощью массивов), но я уверен, что вы найдете способы сделать это самостоятельно!

person Community    schedule 19.11.2018
comment
Да. Это мой последний вариант. Но я бы предпочел не менять ни один из существующих компонентов, поскольку они разрабатываются в общей библиотеке компонентов. - person charith.arumapperuma; 19.11.2018
comment
Ну, это будет сложно привязать к событиям ловушек (что, по сути, является вашим единственным вариантом, если вы хотите знать, когда ваше представление было инициализировано)... - person ; 19.11.2018

Лучшее возможное решение, которое я могу придумать, это иметь следующую архитектуру:

Step-manager (этот компонент будет обрабатывать вход и выход из каждого раздела формы)

  • Пошаговый менеджер также предоставит сервис, который будет доступен всем дочерним компонентам.
  • В Step-менеджере также могут быть такие вещи, как индикатор прогресса (Progress-bar).
  • Пошаговый менеджер может реагировать на тему поведения в сервисе и переключаться между компонентами/разными шаблонами внутри компонента.

Сервис

  • Сервис может обрабатывать все виды взаимодействия компонентов.
  • Сервис будет содержать объект поведения, который будет использоваться всеми дочерними компонентами для информирования пошагового менеджера о том, что была нажата кнопка «Назад» или «Далее».
  • Служба также будет иметь возможность выполнять все служебные вызовы для каждого из входных данных формы.

Дочерние компоненты

  • Дочерние компоненты могут быть отдельными компонентами (не рекомендуется)
  • Их можно разделить на разные разделы (например: 3 шага «Личной информации» могут быть 1 компонентом, 5 шагов «Информации об образовании» могут быть 1 компонентом) (я рекомендую это)
  • В каждый дочерний компонент будут встроены разные шаблоны, которые будут отображаться/скрываться в зависимости от логических значений. (Все обрабатывается пошаговым менеджером)

Пожалуйста, дайте мне знать, если это имеет смысл.

person Vinod Bhavnani    schedule 19.11.2018

Я думаю, что тема является хорошей идеей для вашего требования. На самом деле у меня в приложении есть 3 формы, и я использовал там ion-modal-controller. Простой пример ниже:

class OneService {
  public formSubject: Subject<Object> = new Subject<Object>();
}

class ParentPage {
  constructor(oneService: OneService ) {
    this.oneService.formSubject.subscribe(nextStepComponentName => {
      this.nextStep(nextStepComponentName);
    });
  }
  nextStep(nextStepComponentName) {
     switch nextStepComponentName:
         case 'ChildComponentB':
            //load it
            break;
         default:
            this.cancel();
  }
}

class ChildComponentA {
  constructor(oneService: OneService ) {
  }
  save() {
     //do save.subscribe and
     this.oneService.formSubject.next('ChildComponentB');
  }
}

после дочернего вызова subject.next(), тогда родитель знает, что дочерний процесс завершен. Надеюсь, это полезно.

person Nick Wang    schedule 19.11.2018