2

我的问题场景如下,

我有一个带有大表格的页面(+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这个方法访问关联的组件,它将无缝地工作。

是否有任何建议可以解决此问题,或者是否有任何其他方法可以在不更改表单部分组件的情况下实现此目的?

注意:表单部分可以是任何角度组件。

4

3 回答 3

0

我能想到的最好的解决方案是拥有以下架构:

Step-manager(该组件将处理进入和退出每个表单部分)

  • step-manager 还将提供一项服务,该服务可供所有子组件使用。
  • Step-manager 还可以有进度指示器(Progress-bar)之类的东西
  • 步骤管理器可以对服务上的行为主题做出反应,并在组件/组件内的不同模板之间切换。

服务

  • 该服务可以处理各种组件交互。
  • 该服务将包含一个行为主题,所有子组件都将使用该主题来通知步骤管理器按下了后退按钮或按下了下一步按钮。
  • 该服务还将能够对每个表单输入数据进行所有服务调用。

子组件

  • 子组件可以是单独的组件(不推荐)
  • 它们可以分为不同的部分(例如:“个人信息”的 3 个步骤可以是 1 个组件,“教育信息”的 5 个步骤可以是 1 个组件)(我推荐这个)
  • 每个子组件都将嵌入不同的模板,这些模板将根据布尔值显示/隐藏。(全部由step-manager处理)

请让我知道这是否有意义。

于 2018-11-19T11:03:22.193 回答
0

我认为主题对您的要求是个好主意。事实上,我的应用程序中有 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()之后,父母知道孩子完成了。希望它是有帮助的。

于 2018-11-19T11:08:36.797 回答
0

这么少,你走得太远了。

使用绑定到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); }

因为您的组件相互依赖,所以您必须明确声明条件。

有更高级的方法可以做到这一点(例如使用数组),但我相信你会找到自己的方法!

于 2018-11-19T10:51:36.713 回答