Первая часть туториала была хорошим введением в DORF, результатом которого стало следующее приложение (примерно):



Шаг за шагом

Основные проблемы, обозначенные в первой части:

  1. Нет индикаторов на обязательных полях; в нашем случае все поля обязательны, но все же…
  2. Видны 2 кнопки, и только первая что-то делает
  3. Первая кнопка 'Save' вместо 'Submit'
  4. Функция update не является идеальным способом работы с DORF Object.

Обязательные поля

Поставить характерную красную звездочку после метки требуемого поля в DORF действительно тривиально. Все, что вам нужно, это установить requiredWithStar в true где-то внутри метода DorfModule.forRoot в app.module.

Кнопки стиля

В настоящее время у нас есть 2 кнопки, и одна из них не нужна. К тому же они некрасивые. Решение для этого — еще раз — является модификацией метода DorfModule.forRoot. Конечный результат может выглядеть так:

DorfModule.forRoot({
  css: {
    section: 'row',
    wrapper: 'form-group col-12 row',
    label: 'col-2 col-form-label',
    fieldGeneralization: 'col-10',
    htmlField: 'form-control',
    buttons: {
      save: 'btn btn-primary',
      reset: 'hidden-xs-up'
    }
  },
  dorfFields: [{
    tag: DorfField.CHECKBOX,
    css: {
      wrapper: 'checkbox col-12 row',
      htmlField: 'checkbox'
    }
  }],
  requiredWithStar: true
})

Стили берутся прямо из Bootstrap.

Изменение кнопок

Время для чего-то посложнее. В текущей версии DORF нет механизмов настройки текста кнопок. Но мы все еще можем достичь нашей цели следующим образом:

  1. Переопределение компонента(ов) DORF
  2. Разговор с ДОРФом по-другому

Давайте рассмотрим первый вариант.

Переопределение компонентов DORF

DORF написан модульным способом. Зависимости представлены ниже:

Есть 3 основных модуля:

  1. DorfCoreModule — сущность, которая экспортирует конфигурацию, ReactiveFormsModule из Angular и абстрактных классов TypeScript, используемых в дальнейшем полями.
  2. DorfFieldsModule — модуль, который собирает информацию, связанную с полем: ввод, выбор, радио, флажок и обобщение поля. Идея одного модуля для всех полевых компонентов проста — он должен позволять легко переключаться. Например. можно определить компоненты с помощью DORF-подобных селекторов, которые используют, например. Angular Material за кулисами. Затем новый модуль, содержащий их, следует использовать поверх DorfCoreModule. Рано или поздно DORF необходимо будет улучшить, чтобы обеспечить еще более простой способ переопределения полей по умолчанию.
  3. DorfModule — финальный модуль, который использует предыдущие и добавляет обертки и кнопки.

Как написано выше, DorfModule хранит компонент кнопок. Давайте посмотрим на это глубже:

Что нужно сделать аналогично тому, что произошло в примере definition-extras из официального репозитория DORF. DorfButtonsComponent имеет HTML-шаблон, который является источником нашей проблемы. И решение состоит в том, чтобы создать новый компонент, например. src/app/ext/custom-buttons-component.ts:

import { Component } from '@angular/core';
import { DorfButtonsComponent } from 'dorf';
@Component({
    selector: 'dorf-buttons',
    template: `
    <section [ngClass]="config.css.buttons?.group">
        <button (click)="submit()" [ngClass]="config.css.buttons?.save" [disabled]="!form || !form.valid">Submit</button>
    </section>
    `
})
export class CustomButtonsComponent extends DorfButtonsComponent { }

HTML был изменен, чтобы соответствовать нашим требованиям. Мы даже убрали ненужную кнопку «Сброс». Подсказки:

  • Селектор должен соответствовать исходному селектору
  • Может быть полезно расширить исходный компонент

Новый компонент должен быть зарегистрирован в модуле:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { 
  DorfFieldsModule,
  DorfField,
  DorfFieldWrapperComponent,
  DorfGroupWrapperComponent
} from 'dorf';
import { CustomButtonsComponent } from './ext/custom-buttons-component';
import { UserFormComponent } from './user/user-form.component';
import { AppComponent } from './app.component';
@NgModule({
  declarations: [
    UserFormComponent,
    AppComponent,
    CustomButtonsComponent,
    DorfFieldWrapperComponent,
    DorfGroupWrapperComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    DorfFieldsModule.forRoot({
      css: {
        section: 'row',
        wrapper: 'form-group col-12 row',
        label: 'col-2 col-form-label',
        fieldGeneralization: 'col-10',
        htmlField: 'form-control',
        buttons: {
          save: 'btn btn-primary'
        }
      },
      dorfFields: [{
        tag: DorfField.CHECKBOX,
        css: {
          wrapper: 'checkbox col-12 row',
          htmlField: 'checkbox'
        }
      }],
      requiredWithStar: true
    })
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Обратите внимание, что мы перешли с DorfModule на DorfFieldsModule, поэтому DorfFieldWrapperComponent и DorfGroupWrapperComponent нужно было прописать «вручную».

Конечный результат этой части доступен здесь: http://embed.plnkr.co/38WWfdqRICzB4zlzmKon/

Разговор с ДОРФ по-другому

Переопределение частей DORF — мощная техника, но в нашем случае это слишком много. Давайте отменим последние изменения и изменим декоратор DorfForm на UserFormComponent:

@DorfForm({
  renderWithoutButtons: true
})

Затем добавим <button class="btn btn-primary">Submit</button> вручную в шаблон AppComponent.

Лучший способ работы с DORF Object

Что, если я скажу вам, что вы можете использовать DORF и получать удовольствие от [(ngModel)]? Начнем модификацию с удаления функции update из нашей модели. Затем для каждого поля, которое мы хотим немедленно обновить, мы должны добавить опцию updateModelOnChange: true:

@DorfObject()
export class User {
  @DorfInput({
    label: 'Username',
    type: 'input' as InputType,
    validator: Validators.required,
    updateModelOnChange: true
  })
  private _login: string;
  @DorfInput({
    label: 'Password',
    type: 'password' as InputType,
    validator: Validators.required,
    updateModelOnChange: true
  })
  private _password: string;
  @DorfCheckbox({
    innerLabel: 'I accept the terms and conditions',
    validator: Validators.requiredTrue,
    updateModelOnChange: true
  })
  private _acceptance: boolean;
  constructor(options?: IUser) {
    if (options) {
      this._login = options._login;
      this._password = options._password;
      this._acceptance = options._acceptance;
    }
  }
  get login() { return this._login; }
  get password() { return btoa(this._password); }
  get acceptance() { return this._acceptance; }
  get basicAuth() {
    if (this._login && this._password) {
      return btoa(`${this._login}:${this._password}`);
    }
  }
}

Бонус: вы можете указать параметр debounce, чтобы отложить обновление. Он ожидает количество миллисекунд в качестве значения. Аналогичен одному из ngModelOptions параметров из Angular 1.3.

Вот и все. Имея такое обновление, можно потреблять user прямо в AppComponent.

Резюме

Мы выполнили все требования из предыдущей части и расширили наши знания о DORF:

  1. метод DorfModule.forRoot позволяет назначать CSS-классы не только полям, но и кнопкам; он также имеет дополнительные параметры, например. для установки красной звезды на обязательные поля
  2. DORF является модульным, и его компоненты можно довольно легко переопределить.
  3. DORF позволяет выполнять [(ngModel)] подобное обновление. За кулисами немедленное обновление использует события, поэтому это реактивный способ, а не двусторонняя привязка.

Готовое приложение представлено здесь:



Будущее

DORF все еще находится в разработке, но его код уже позволяет обрабатывать множество вариантов использования и сценариев, которые еще не представлены в руководствах.

Запланированные уроки

  • Дополнительные параметры и дальнейшее переопределение компонентов DORF
  • Вложенные объекты и расположение столбцов
  • Добавление настраиваемых полей
  • Тестирование ДОРФ