lazalu68
@lazalu68
Salmon

Как обработать XML в качестве шаблона?

Суть такова: на сервере хранятся XML-структуры, приблизительно такие:
example.xml
<Container Id="accordion">
	<VerticalLayout Margin="true" Spacing="true" PanelCaption="Основные атрибуты">
		<HorizontalLayout Margin="false" Spacing="true">
			Some content
		</HorizontalLayout>
	</VerticalLayout>
</Container>

Эти структурки надо будет скачать с сервера и использовать в качестве шаблона, то есть разместить в теле какого-нибудь топового компонента и рассчитывать на то, что будут корректно инициализированы используемые компоненты (в данном случае это `VerticalLayout`, `HorizontalLayout` и `Container`). Говоря "корректно" я имею в виду, что эти компоненты будут инициализированы абсолютно так же, как если бы эта структура была статическим шаблоном в каком-нибудь компоненте.

Сначала мне показалось, что это о Dynamic Component Loading, но чо-то в статье не нашёл инфы по теме. Будет вообще шикарно если подскажете как в терминологии ангуляра называется подобная гимнастика с шаблонами. Заранее большое спасибо!
  • Вопрос задан
  • 295 просмотров
Решения вопроса 2
gscraft
@gscraft
Программист, философ
Как следует из комментариев, задача отображать множественные формы. Источник форм не столько важен: создают их вручную, с инструментарием или они генерируются автоматически. Решить динамической загрузкой в Angular будет сложно (а тем более связать в MV*, обрабатывая в контроллере/модели компонента), следовательно, напрашивается другой подход к визуализации форм средствами статически-собранного приложения. Попробуйте решить это посредством мета-данных форм. Пусть форма будет представлена описанием полей, скажем, JSON или XML, и единый (и сложный) шаблон Angular будет заниматься генерацией, грубо говоря, что-то вроде этого:
formElementsList = [ { type: "text", label: "Поле ввода", value: null } ];

<div *ngFor="let element of formElementsList">
  <div *ngIf="element.type == 'text'">
    <label>{{ element.label }}:
      <input [(ngModel)]="element.value" />
    </label>
  </div>
</div>

Ограничений у этого подхода нет, можно создать форму любой сложности, с иерархией и вложенностью.
Ответ написан
lazalu68
@lazalu68 Автор вопроса
Salmon
В итоге задача решена почти так как и предложил Gennady S: данные из XML переведены в JSON, получившаяся структура обрабатывается с помощью компонентов, которые рекурсивно вызывают друг друга (сделано по материалам из статейки Ben Nadel: Playing With Recursive Components in Angular). В каждый из этих компонентов явно передается ссылка на экземпляр FormGroup, таким образом все поля ввода без проблем могут быть привязаны к одной форме.

Пример структуры описывающей форму
{
  "type": "Container",
  "id": "filter",
  "children": [
    {
      "type": "HorizontalLayout",
      "spacing": true,
      "children": [
        {
          "type": "TextField",
          "id": "code",
          "caption": "Код",
          "width": "100px"
        },
        {
          "type": "TextField",
          "id": "label",
          "caption": "Наименование",
          "width": "200px"
        },
        {
          "type": "TextField",
          "id": "days",
          "caption": "Дней",
          "width": "100px"
        },
        {
          "type": "TextField",
          "id": "months",
          "caption": "Месяцев",
          "width": "100px"
        },
        {
          "type": "Check3Box",
          "id": "isActive",
          "caption": "Активен",
          "height": "20px",
          "alignment": "bottom_left"
        },
        {
          "type": "Check3Box",
          "id": "isAbsDeleted",
          "caption": "Удален в АБС",
          "height": "20px",
          "alignment": "bottom_left"
        }
      ]
    }
  ]
}

Часть main.component.html

<ng-template [ngIf]="filterForm">
    <form [formGroup]="my_awesome_form">
        <my-tree [rootNode]="elements" [form]="my_awesome_form"></my-tree>
    </form>
</ng-template>

Часть main.component.ts

// создаём форму
public my_awesome_form = new FormGroup({});

// получаем описание лэйаута
public elements = some_data_service.getLayout();

// достаем из лэйаута только поля ввода
const inputs = Utils.deepFind(this.elements, 'caption', '');

// привязываем поля к форме
inputs.forEach((input) => {
    this.filterForm.addControl(input.id, new FormControl(null));
});

TreeComponent
import {Component} from "@angular/core";
import {LayoutElement} from "@toolkit/models/layout.model";
import {FormGroup} from "@angular/forms";

@Component({
  selector: "my-tree",
  inputs: [
    "rootNode",
    "form"
  ],
  outputs: [],
  template: `<my-tree-node [node]="rootNode" [form]="form"></my-tree-node>`
})
export class TreeComponent {
  public rootNode: LayoutElement | null;
  public form: FormGroup;

  constructor() {
    this.rootNode = null;
  }
}

TreeNodeComponent

import {Component} from "@angular/core";
import {LayoutElement} from "@toolkit/models/layout.model";
import {FormGroup} from "@angular/forms";

@Component({
  selector: "my-tree-node",
  inputs: [
    "node",
    "form"
  ],
  outputs: [],
  template:
      `
      <ng-template [ngIf]="node && node.type">
          <span [ngSwitch]="node.type" [formGroup]="form">
              <!-- пример создания контейнера -->
              <div *ngSwitchCase="'Container'">
                  <div *ngIf="node.children">
                      <ng-template ngFor let-child [ngForOf]="node.children">
                          <my-tree-node [node]="child" [form]="form"></my-tree-node>
                      </ng-template>
                  </div>
              </div>
              <!-- пример создания поля ввода -->
              <ng-template [ngSwitchCase]="'TextField'">
                  <input type="text" [attr.placeholder]="node.caption" [formControlName]="node.id"/>
              </ng-template>
              <!-- обработка не зарегистрированного типа -->
              <div *ngSwitchDefault>
                  <div class="p-3 mb-2 bg-danger text-white">Element of type "{{ node.type }}" can't be processed!</div>
              </div>
          </span>
      </ng-template>
    `
})
export class TreeNodeComponent {
  public node: LayoutElement | null;
  public form: FormGroup;

  constructor() {
    this.node = null;
  }
}
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы