Spec-Zone.ru › Angular 2

Формы

Форма создаёт цельный, эффективный и убедительный опыт ввода данных. Ангулярная форма координирует набор пользовательских элементов управления, связанных с данными, отслеживает изменения, проверяет ввод и отображает ошибки.

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

При разработке формы важно создать опыт ввода данных, который эффективно и надёжно направляет пользователя через рабочий процесс.

Разработка форм требует навыков дизайна (которые не входят в сферу охвата этой страницы), а также поддержки фреймворка для двусторонней привязки данных, отслеживания изменений, проверки и обработки ошибок, о которых вы узнаете на этой странице.

На этой странице показано, как построить простую форму с нуля. По ходу вы узнаете, как:

  • Создать ангулярную форму с компонентом и шаблоном.
  • Использовать ngModel для создания двусторонней привязки данных для чтения и записи значений элементов управления вводом.
  • Отслеживать изменения состояния и валидность элементов управления формой.
  • Предоставлять визуальную обратную связь с использованием специальных классов CSS, которые отслеживают состояние элементов управления.
  • Отображать сообщения об ошибках проверки пользователям и включать/отключать элементы управления формой.
  • Обмениваться информацией между элементами HTML с помощью переменных ссылок шаблонов.

Вы можете запустить пример в режиме реального времени в Plunker и загрузить код оттуда.

Шаблонно-управляемые формы

Вы можете создавать формы, написав шаблоны на языке Angular синтаксис шаблонов с помощью специфичных для форм директив и техник, описанных на этой странице.

Вы также можете использовать реактивный (или управляемый моделью) подход для создания форм. Однако на этой странице делается упор на шаблонно-управляемые формы.

Вы можете создать практически любую форму с помощью ангулярного шаблона — формы входа, формы обратной связи и практически любую бизнес-форму. Вы можете творчески размещать элементы управления, привязывать их к данным, указывать правила проверки и отображать сообщения об ошибках проверки, условно включать или отключать определённые элементы управления, запускать встроенную визуальную обратную связь и многое другое.

Angular упрощает процесс, обрабатывая многие повторяющиеся и шаблонные задачи, с которыми вы в противном случае столкнулись бы сами.

Вы узнаете, как создать шаблонно-управляемую форму, которая выглядит следующим образом:

Clean Form

Агентство по трудоустройству героев использует эту форму для ведения личной информации о героях. Каждый герой нуждается в работе. Миссия компании — сопоставить подходящего героя с соответствующим кризисом.

Два из трёх полей в этой форме обязательны. Обязательные поля имеют зелёную полоску слева, чтобы их было легко распознать.

Если вы удалите имя героя, форма отобразит сообщение об ошибке проверки в привлекательном стиле:

Invalid, Name Required

Обратите внимание, что кнопка Отправить отключена, а полоска «обязательно» слева от элемента управления ввода меняет цвет с зелёного на красный.

Вы можете настроить цвета и расположение полоски «обязательно» с помощью стандартного CSS.

Вы создадите эту форму небольшими шагами:

  1. Создайте Hero класс модели.
  2. Создайте компонент, который управляет формой.
  3. Создайте шаблон с начальной формой макета.
  4. Привяжите свойства данных к каждому элементу управления формы, используя ngModel синтаксис двусторонней привязки данных.
  5. Добавьте name атрибут к каждому элементу управления вводом формы.
  6. Добавьте пользовательский CSS для предоставления визуальной обратной связи.
  7. Отображайте и скрывайте сообщения об ошибках проверки.
  8. Обработайте отправку формы с помощью ngSubmit.
  9. Отключите кнопку Отправить формы до тех пор, пока форма не будет валидной.

Установка

Следуйте инструкциям по установке для создания нового проекта с именем angular-forms.

Создайте класс модели Hero

По мере ввода данных в форму вы будете фиксировать их изменения и обновлять экземпляр модели. Вы не можете разместить форму, пока не знаете, как выглядит модель.

Модель может быть такой простой, как «набор свойств», который содержит факты о чём-то важном для приложения. Это хорошо описывает Hero класс с тремя обязательными полями (id, name, power) и одним необязательным полем (alterEgo).

В каталоге app создайте следующий файл с указанным содержимым:

src/app/hero.ts

export class Hero {

  constructor(
    public id: number,
    public name: string,
    public power: string,
    public alterEgo?: string
  ) {  }

}

Это анемичная модель с небольшими требованиями и без поведения. Идеально подходит для демонстрации.

Компилятор TypeScript генерирует общедоступное поле для каждого public параметра конструктора и автоматически присваивает значение параметра этому полю при создании героев.

alterEgo — необязательно, поэтому конструктор позволяет опустить его; обратите внимание на знак вопроса (?) в alterEgo?.

Вы можете создать нового героя следующим образом:

let myHero =  new Hero(42, 'SkyDog',
                       'Fetch any object at any distance',
                       'Leslie Rollover');
console.log('My hero is called ' + myHero.name); // "My hero is called SkyDog"

Создайте компонент формы

Ангулярная форма состоит из двух частей: HTML-шаблона и класса компонента для обработки данных и пользовательских взаимодействий программным способом. Начните с класса, потому что он кратко описывает то, что может делать редактор героев.

Создайте следующий файл с указанным содержимым:

src/app/hero-form.component.ts (v1)

import { Component } from '@angular/core';

import { Hero }    from './hero';

@Component({
  selector: 'hero-form',
  templateUrl: './hero-form.component.html'
})
export class HeroFormComponent {

  powers = ['Really Smart', 'Super Flexible',
            'Super Hot', 'Weather Changer'];

  model = new Hero(18, 'Dr IQ', this.powers[0], 'Chuck Overstreet');

  submitted = false;

  onSubmit() { this.submitted = true; }

  // TODO: Remove this when we're done
  get diagnostic() { return JSON.stringify(this.model); }
}

В этом компоненте нет ничего особенного, ничего специфичного для форм, ничего, что отличало бы его от других компонентов, которые вы писали ранее.

Понимание этого компонента требует только концепций Angular, рассмотренных на предыдущих страницах.

  • Код импортирует библиотеку ядра Angular и Hero модель, которую вы только что создали.
  • Значение селектора @Component «hero-form» означает, что вы можете поместить эту форму в родительский шаблон с тэгом <hero-form>.
  • Свойство templateUrl указывает на отдельный файл для HTML-шаблона.
  • Вы определили фиктивные данные для model и powers, как это подходит для демонстрации. В дальнейшем вы можете ввести сервис данных для получения и сохранения реальных данных или, возможно, представить эти свойства как входные и выходные параметры (см. Входные и выходные свойства на странице Синтаксис шаблонов) для привязки к родительскому компоненту. Это не проблема сейчас, и эти будущие изменения не повлияют на форму.
  • Вы добавили свойство diagnostic для возврата JSON-представления модели. Это поможет вам увидеть, что вы делаете во время разработки; вы оставили себе примечание о том, чтобы удалить его позже.

Зачем отдельный файл шаблона?

Почему вы не пишете шаблон встроеным в файл компонента, как вы часто делаете в других случаях?

Нет «правильного» ответа на все случаи. Встроенные шаблоны полезны, когда они короткие. Большинство шаблонов форм не короткие. Файлы TypeScript и JavaScript обычно не являются лучшим местом для написания (или чтения) больших фрагментов HTML, и немногие редакторы помогают с файлами, содержащими смесь HTML и кода.

Шаблоны форм имеют тенденцию быть большими, даже при отображении небольшого количества полей, поэтому обычно лучше поместить HTML-шаблон в отдельный файл. Вы напишете этот файл шаблона через некоторое время. Сначала пересмотрите app.module.ts и app.component.ts для использования нового HeroFormComponent.

Пересмотрите app.module.ts

app.module.ts определяет корневой модуль приложения. В нём вы определяете внешние модули, которые будете использовать в приложении, и объявляете компоненты, принадлежащие этому модулю, такие как HeroFormComponent.

Поскольку шаблонно-управляемые формы находятся в собственном модуле, вам необходимо добавить FormsModule в массив imports для модуля приложения, прежде чем вы сможете использовать формы.

Замените содержимое версии «QuickStart» следующим:

src/app/app.module.ts

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule }   from '@angular/forms';

import { AppComponent }  from './app.component';
import { HeroFormComponent } from './hero-form.component';

@NgModule({
  imports: [
    BrowserModule,
    FormsModule
  ],
  declarations: [
    AppComponent,
    HeroFormComponent
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

Есть три изменения:

  1. Вы импортируете FormsModule и новый HeroFormComponent.

  2. Вы добавляете FormsModule в список imports , определённых в декораторе ngModule . Это даёт приложению доступ ко всем функциям шаблонно-управляемых форм, включая ngModel.

  3. Вы добавляете HeroFormComponent в список declarations , определённых в декораторе ngModule . Это делает компонент HeroFormComponent видимым во всём модуле.

Если компонент, директива или трубка принадлежат модулю в массиве imports , ​не​ объявляйте его повторно в массиве declarations . Если вы его написали и он должен принадлежать этому модулю, ​объявите​ его в массиве declarations.

Пересмотрите app.component.ts

AppComponent — корневой компонент приложения. Он будет содержать новый HeroFormComponent.

Замените содержимое версии «QuickStart» следующим:

src/app/app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: '<hero-form></hero-form>'
})
export class AppComponent { }

Есть только два изменения. template — это просто новый тег элемента, определённый свойством компонента selector . Это отображает форму героя при загрузке компонента приложения. Вы также удалили поле name из тела класса.

Создайте начальный HTML-шаблон формы

Создайте файл шаблона со следующим содержимым:

src/app/hero-form.component.html

<div class="container">
    <h1>Hero Form</h1>
    <form>
      <div class="form-group">
        <label for="name">Name</label>
        <input type="text" class="form-control" id="name" required>
      </div>

      <div class="form-group">
        <label for="alterEgo">Alter Ego</label>
        <input type="text" class="form-control" id="alterEgo">
      </div>

      <button type="submit" class="btn btn-success">Submit</button>

    </form>
</div>

Язык — просто HTML5. Вы представляете два из Hero полей, name и alterEgo, и открываете их для ввода пользователем в полях ввода.

Элемент управления Имя имеет HTML5 required атрибут; элемент управления Псевдоним — нет, потому что alterEgo — необязательно.

Вы добавили кнопку Отправить внизу с некоторыми классами для стилизации.

Вы ещё не используете Angular. Нет привязок или дополнительных директив, только макет.

Классы container, form-group, form-control, и btn взяты из Twitter Bootstrap. Эти классы чисто косметические. Bootstrap придаёт форме немного стиля.

Ангулярные формы не требуют библиотеки стилей

Angular не использует классы container, form-group, form-control, и btn или стили любой внешней библиотеки. Приложения Angular могут использовать любую библиотеку CSS или не использовать её вовсе.

Для добавления стилей, откройте index.html и добавьте следующую ссылку в <head>:

src/index.html (bootstrap)

<link rel="stylesheet"
      href="https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css">

Добавление способностей с помощью *ngFor

Герой должен выбрать одну суперспособность из фиксированного списка одобренных агентством способностей. Вы храните этот список внутри (в HeroFormComponent).

Вы добавите select в форму и привяжете опции к списку powers с помощью ngFor, техника, которую вы видели ранее на странице Отображение данных.

Добавьте следующий HTML непосредственно под группой Псевдоним:

src/app/hero-form.component.html (powers)

<div class="form-group">
  <label for="power">Hero Power</label>
  <select class="form-control" id="power" required>
    <option *ngFor="let pow of powers" [value]="pow">{{pow}}</option>
  </select>
</div>

Этот код повторяет тег <option> для каждой способности в списке способностей. Переменная шаблона pow в каждом итерации — это разная способность; вы отображаете её название, используя синтаксис интерполяции.

Двусторонняя привязка данных с помощью ngModel

Запуск приложения сейчас будет разочаровывающим.

Early form with no binding

Вы не видите данных героя, потому что ещё не привязались к Hero. Вы знаете, как это сделать из предыдущих страниц. Страница Отображение данных учит привязке свойств. Страница Ввод данных пользователем показывает, как прослушивать события DOM с помощью привязки событий и как обновлять свойство компонента с помощью отображаемого значения.

Теперь вам нужно одновременно отобразить, прослушать и извлечь данные.

Вы могли бы использовать уже известные вам техники, но вместо этого вы будете использовать новый синтаксис [(ngModel)], который упрощает привязку формы к модели.

Найдите тег <input> для Имени и обновите его так:

src/app/hero-form.component.html (excerpt)

<input type="text" class="form-control" id="name"
       required
       [(ngModel)]="model.name" name="name">
TODO: remove this: {{model.name}}

Вы добавили диагностическую интерполяцию после тега input, чтобы видеть, что делаете. Вы оставили себе заметку, чтобы удалить её, когда закончите.

Фокусируйтесь на синтаксисе привязки: [(ngModel)]="...".

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

ngModel in action

Диагностика подтверждает, что значения действительно передаются из поля ввода в модель и обратно.

Это двусторонняя привязка данных. Более подробную информацию см. на странице Двусторонняя привязка с NgModel страницы Синтаксис шаблона.

Обратите внимание, что вы также добавили атрибут name к тегу <input> и установили его в "name", что логично для имени героя. Любое уникальное значение подойдет, но использование описательного имени полезно. Определение атрибута name является требованием при использовании [(ngModel)] в сочетании с формой.

Внутри Angular создаются экземпляры FormControl и регистрируются с помощью директивы NgForm, которую Angular прикрепил к тегу <form>. Каждый экземпляр FormControl регистрируется под именем, которое вы назначили атрибуту name. Подробнее об этом в директиве NgForm далее на этой странице.

Добавьте аналогичные привязки [(ngModel)] и атрибуты name к Псевдониму и Суперспособности героя. Вы удалите сообщение привязки к полю ввода и добавите новую привязку (вверху) к свойству компонента diagnostic. Затем вы сможете подтвердить, что двусторонняя привязка работает для всей модели героя.

После переработки ядро формы должно выглядеть так:

src/app/hero-form.component.html (excerpt)

{{diagnostic}}
<div class="form-group">
  <label for="name">Name</label>
  <input type="text" class="form-control" id="name"
         required
         [(ngModel)]="model.name" name="name">
</div>

<div class="form-group">
  <label for="alterEgo">Alter Ego</label>
  <input type="text"  class="form-control" id="alterEgo"
         [(ngModel)]="model.alterEgo" name="alterEgo">
</div>

<div class="form-group">
  <label for="power">Hero Power</label>
  <select class="form-control"  id="power"
          required
          [(ngModel)]="model.power" name="power">
    <option *ngFor="let pow of powers" [value]="pow">{{pow}}</option>
  </select>
</div>
  • Каждый элемент ввода имеет свойство id, которое используется атрибутом label элемента for для сопоставления метки с элементом управления вводом.
  • Каждый элемент ввода имеет свойство name, которое требуется формам Angular для регистрации управления с формой.

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

ngModel in action

Диагностика вверху формы подтверждает, что все ваши изменения отражены в модели.

Удалите привязку {{diagnostic}} вверху, так как она выполнила свою функцию.

Отслеживание состояния и валидности управления с помощью ngModel

Использование ngModel в форме даёт вам больше, чем просто двустороннюю привязку данных. Оно также сообщает вам, если пользователь взаимодействовал с элементом управления, изменилось ли значение или оно стало недопустимым.

Директива NgModel не только отслеживает состояние; она также обновляет элемент управления специальными классами CSS Angular, которые отражают состояние. Вы можете использовать эти имена классов для изменения внешнего вида элемента управления.

Состояние Класс, если true Класс, если false
Элемент управления был посещён. ng-touched ng-untouched
Значение элемента управления изменилось. ng-dirty ng-pristine
Значение элемента управления валидно. ng-valid ng-invalid

Временно добавьте переменную шаблона с именем spy к тегу <input> Имя и используйте её для отображения классов CSS элемента ввода.

src/app/hero-form.component.html (excerpt)

<input type="text" class="form-control" id="name"
  required
  [(ngModel)]="model.name" name="name"
  #spy>
<br>TODO: remove this: {{spy.className}}

Теперь запустите приложение и посмотрите на поле ввода Имя. Следуйте этим инструкциям точно:

  1. Посмотрите, но не трогайте.
  2. Нажмите внутрь поля имени, затем нажмите вне его.
  3. Добавьте косые черты в конец имени.
  4. Удалите имя.

Действия и эффекты таковы:

Control State Transition

Вы должны увидеть следующие переходы и имена классов:

Control state transitions

Пара ng-valid/ng-invalid самая интересная, потому что вы хотите отправить чёткое визуальное сообщение, когда значения недопустимы. Вы также хотите отметить обязательные поля. Чтобы создать такую визуальную обратную связь, добавьте определения для классов CSS ng-*.

Удалите переменную шаблона #spy и TODO, так как они выполнили свою функцию.

Добавление пользовательского CSS для визуальной обратной связи

Вы можете отметить обязательные поля и недопустимые данные одновременно с помощью цветной полосы слева от поля ввода:

Invalid Form

Этого эффекта можно добиться, добавив эти определения классов в новый файл forms.css в проект, как брата index.html:

src/forms.css

.ng-valid[required], .ng-valid.required  {
  border-left: 5px solid #42A948; /* green */
}

.ng-invalid:not(form)  {
  border-left: 5px solid #a94442; /* red */
}

Обновите <head> index.html для включения этого файла стилей:

src/index.html (styles)

<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="forms.css">

Отображение и скрытие сообщений об ошибках валидации

Вы можете улучшить форму. Поле ввода Имя обязательно, и его очистка делает полосу красной. Это говорит о проблеме, но пользователь не знает что не так или что делать. Воспользуйтесь состоянием элемента управления, чтобы отобразить полезное сообщение.

Когда пользователь удаляет имя, форма должна выглядеть так:

Name required

Чтобы добиться этого эффекта, расширьте тег <input> следующим:

  • Переменную шаблона.
  • Сообщение "обязательно" в соседнем <div>, которое вы будете отображать только если элемент управления недопустим.

Вот пример сообщения об ошибке, добавленного в поле ввода имя:

src/app/hero-form.component.html (excerpt)

        <label for="name">Name</label>
        <input type="text" class="form-control" id="name"
               required
               [(ngModel)]="model.name" name="name"
               #name="ngModel">
        <div [hidden]="name.valid || name.pristine"
             class="alert alert-danger">
          Name is required
        </div>

Вам нужна переменная шаблона, чтобы получить доступ к элементу управления Angular в шаблоне. Здесь вы создали переменную с именем name и присвоили ей значение "ngModel".

Почему "ngModel"? Свойство exportAs директивы сообщает Angular, как связать переменную ссылки с директивой. Вы установили name в ngModel, потому что свойство ngModel директивы exportAs случайно является "ngModel".

Вы управляете видимостью сообщения об ошибке имени, привязывая свойства элемента управления name к свойству <div> элемента сообщения hidden.

<div [hidden]="name.valid || name.pristine"
     class="alert alert-danger">

В этом примере вы скрываете сообщение, когда элемент управления валиден или не изменён; "неизменённый" означает, что пользователь не изменял значение с момента его отображения в этой форме.

Этот пользовательский опыт — выбор разработчика. Некоторые разработчики хотят, чтобы сообщение отображалось всегда. Если вы игнорируете состояние pristine, вы будете скрывать сообщение только тогда, когда значение валидно. Если вы попали в этот компонент с новым (пустым) героем или невалидным героем, вы сразу увидите сообщение об ошибке, прежде чем что-либо сделать.

Некоторые разработчики хотят, чтобы сообщение отображалось только когда пользователь делает невалидное изменение. Скрытие сообщения, пока элемент управления "неизменён", достигает этой цели. Вы увидите значение этого выбора, когда добавите нового героя в форму.

Выбор героя Псевдонима необязательный, поэтому вы можете оставить его.

Выбор героя Суперспособности обязательный. Вы можете добавить такой же обработку ошибок к <select> если хотите, но это не обязательно, потому что поле выбора уже ограничивает способность допустимыми значениями.

Теперь вы добавите нового героя в эту форму. Поместите кнопку Новый герой внизу формы и привяжите её событие нажатия к методу компонента newHero.

src/app/hero-form.component.html (New Hero button)

<button type="button" class="btn btn-default" (click)="newHero()">New Hero</button>

src/app/hero-form.component.ts (New Hero method)

newHero() {
  this.model = new Hero(42, '', '');
}

Запустите приложение снова, нажмите кнопку Новый герой, и форма очистится. Полосы обязательно слева от полей ввода красные, указывая на недопустимые свойства name и power. Это понятно, поскольку это обязательные поля. Сообщения об ошибках скрыты, потому что форма не изменённая; вы ещё ничего не изменили.

END_OF_DOCUMENT_MARKER

Введите имя и снова нажмите Создать героя. Приложение отобразит сообщение об ошибке Требуется имя. Вы не хотите сообщений об ошибках при создании нового (пустого) героя. Почему вы получаете его сейчас?

Просмотрев элемент в инструментах разработчика браузера, вы увидите, что поле ввода имени больше не является первичным. Форма запоминает, что вы ввели имя до нажатия на Создать героя. Замена объекта героя не восстановила первоначальное состояние элементов управления формы.

Вы должны очистить все флаги явно, что можно сделать, вызвав метод reset() формы после вызова метода newHero().

src/app/hero-form.component.html (Сброс формы)

<button type="button" class="btn btn-default" (click)="newHero(); heroForm.reset()">New Hero</button>

Теперь нажатие на «Создать героя» сбрасывает как форму, так и флаги её элементов управления.

Отправка формы с помощью ngSubmit

Пользователь должен иметь возможность отправить заполненную форму. Кнопка Отправить внизу формы сама по себе ничего не делает, но она вызовет отправку формы из-за её типа (type="submit").

«Отправка формы» бесполезна в данный момент. Чтобы сделать её полезной, привяжите свойство события отправки формы ngSubmit к методу компонента формы героя onSubmit():

<form (ngSubmit)="onSubmit()" #heroForm="ngForm">

Вы добавили что-то лишнее в конце. Вы определили переменную шаблона, #heroForm, и инициализировали её значением «ngForm».

Переменная heroForm теперь является ссылкой на директиву NgForm, которая управляет формой в целом.

Директива NgForm

Что это за NgForm директива? Вы не добавили директиву NgForm.

Это делает Angular. Angular автоматически создаёт и прикрепляет директиву NgForm к тегу <form>.

Директива NgForm дополняет элемент form дополнительными функциями. Она содержит элементы управления, которые вы создали для элементов с директивой ngModel и атрибутом name, и отслеживает их свойства, включая их валидность. Она также имеет своё свойство valid, которое истинно только если каждый содержащийся элемент управления является валидным.

Вы свяжете общую валидность формы через переменную heroForm со свойством disabled кнопки с помощью привязки события. Вот код:

<button type="submit" class="btn btn-success" [disabled]="!heroForm.form.valid">Submit</button>

Если вы сейчас запустите приложение, вы обнаружите, что кнопка активна, хотя она ещё не выполняет полезных действий.

Теперь, если вы удалите Имя, вы нарушите правило «обязательно», что должным образом отмечено в сообщении об ошибке. Кнопка Отправить также будет отключена.

Это не впечатляет? Подумайте об этом на мгновение. Что вам нужно будет сделать, чтобы связать состояние включения/выключения кнопки с валидностью формы без помощи Angular?

Для вас это было так просто:

  1. Определите переменную шаблона на элементе формы (улучшенной).
  2. Укажите на эту переменную в кнопке, находящейся на многих строках ниже.

Переключение двух областей формы (дополнительное задание)

Отправка формы пока не очень драматична.

Неудивительное наблюдение для демонстрации. Честно говоря, придание ей большей «изюминки» не научит вас чему-то новому о формах. Но это возможность потренировать ваши недавно приобретённые навыки привязки. Если вас это не интересует, перейдите к заключению этой страницы.

Для более впечатляющего визуального эффекта скройте область ввода данных и отобразите что-то другое.

Оборачивайте форму в <div> и связывайте её свойство hidden со свойством HeroFormComponent.submitted.

src/app/hero-form.component.html (фрагмент)

  <div [hidden]="submitted">
    <h1>Hero Form</h1>
    <form (ngSubmit)="onSubmit()" #heroForm="ngForm">

       <!-- ... all of the form ... -->

    </form>
  </div>

Основная форма отображается с самого начала, потому что свойство submitted ложно до тех пор, пока вы не отправите форму, как показано в этом фрагменте из HeroFormComponent:

src/app/hero-form.component.ts (отправлено)

submitted = false;

onSubmit() { this.submitted = true; }

Когда вы нажимаете на кнопку Отправить, флаг submitted становится истинным, и форма исчезает, как и планировалось.

Теперь приложение должно отображать что-то другое, пока форма находится в состоянии отправки. Добавьте следующий HTML ниже <div> контейнера, который вы только что написали:

src/app/hero-form.component.html (фрагмент)

<div [hidden]="!submitted">
  <h2>You submitted the following:</h2>
  <div class="row">
    <div class="col-xs-3">Name</div>
    <div class="col-xs-9  pull-left">{{ model.name }}</div>
  </div>
  <div class="row">
    <div class="col-xs-3">Alter Ego</div>
    <div class="col-xs-9 pull-left">{{ model.alterEgo }}</div>
  </div>
  <div class="row">
    <div class="col-xs-3">Power</div>
    <div class="col-xs-9 pull-left">{{ model.power }}</div>
  </div>
  <br>
  <button class="btn btn-primary" (click)="submitted=false">Edit</button>
</div>

Герой снова здесь, отображается в режиме только для чтения с привязками интерполяции. Это <div> отображается только в то время, когда компонент находится в состоянии отправки.

HTML включает кнопку Изменить, щелчок по которой связан с выражением, которое очищает флаг submitted.

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

Заключение

Обсуждаемая на этой странице форма Angular использует следующие возможности фреймворка для поддержки изменения данных, проверки и прочего:

  • Шаблон формы Angular HTML.
  • Класс компонента формы с декоратором @Component.
  • Обработка отправки формы путём привязки к свойству события NgForm.ngSubmit.
  • Переменные шаблона, такие как #heroForm и #name.
  • [(ngModel)] синтаксис для двусторонней привязки данных.
  • Использование атрибутов name для проверки и отслеживания изменений элементов формы.
  • Свойство переменной ссылки valid на элементах управления ввода для проверки валидности элемента управления и отображения/скрытия сообщений об ошибках.
  • Управление состоянием включения/выключения кнопки Отправить путём привязки к валидности NgForm.
  • Настраиваемые CSS-классы, предоставляющие пользователям визуальную обратную связь о невалидных элементах управления.

Конечная структура папок проекта должна выглядеть так:

angular-forms
  src
    app
      app.component.ts
      app.module.ts
      hero.ts
      hero-form.component.html
      hero-form.component.ts
    main.ts
    tsconfig.json
    index.html
  node_modules ...
  package.json

Вот код окончательной версии приложения:

hero-form.component.ts
import { Component } from '@angular/core';

import { Hero }    from './hero';

@Component({
  selector: 'hero-form',
  templateUrl: './hero-form.component.html'
})
export class HeroFormComponent {

  powers = ['Really Smart', 'Super Flexible',
            'Super Hot', 'Weather Changer'];

  model = new Hero(18, 'Dr IQ', this.powers[0], 'Chuck Overstreet');

  submitted = false;

  onSubmit() { this.submitted = true; }

  newHero() {
    this.model = new Hero(42, '', '');
  }
}
hero-form.component.html
<div class="container">
  <div [hidden]="submitted">
    <h1>Hero Form</h1>
    <form (ngSubmit)="onSubmit()" #heroForm="ngForm">
      <div class="form-group">
        <label for="name">Name</label>
        <input type="text" class="form-control" id="name"
               required
               [(ngModel)]="model.name" name="name"
               #name="ngModel">
        <div [hidden]="name.valid || name.pristine"
             class="alert alert-danger">
          Name is required
        </div>
      </div>

      <div class="form-group">
        <label for="alterEgo">Alter Ego</label>
        <input type="text" class="form-control" id="alterEgo"
               [(ngModel)]="model.alterEgo" name="alterEgo">
      </div>

      <div class="form-group">
        <label for="power">Hero Power</label>
        <select class="form-control" id="power"
                required
                [(ngModel)]="model.power" name="power"
                #power="ngModel">
          <option *ngFor="let pow of powers" [value]="pow">{{pow}}</option>
        </select>
        <div [hidden]="power.valid || power.pristine" class="alert alert-danger">
          Power is required
        </div>
      </div>

      <button type="submit" class="btn btn-success" [disabled]="!heroForm.form.valid">Submit</button>
      <button type="button" class="btn btn-default" (click)="newHero(); heroForm.reset()">New Hero</button>
    </form>
  </div>

  <div [hidden]="!submitted">
    <h2>You submitted the following:</h2>
    <div class="row">
      <div class="col-xs-3">Name</div>
      <div class="col-xs-9  pull-left">{{ model.name }}</div>
    </div>
    <div class="row">
      <div class="col-xs-3">Alter Ego</div>
      <div class="col-xs-9 pull-left">{{ model.alterEgo }}</div>
    </div>
    <div class="row">
      <div class="col-xs-3">Power</div>
      <div class="col-xs-9 pull-left">{{ model.power }}</div>
    </div>
    <br>
    <button class="btn btn-primary" (click)="submitted=false">Edit</button>
  </div>
</div>
hero.ts
export class Hero {

  constructor(
    public id: number,
    public name: string,
    public power: string,
    public alterEgo?: string
  ) {  }

}
app.module.ts
import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule }   from '@angular/forms';

import { AppComponent }  from './app.component';
import { HeroFormComponent } from './hero-form.component';

@NgModule({
  imports: [
    BrowserModule,
    FormsModule
  ],
  declarations: [
    AppComponent,
    HeroFormComponent
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }
app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: '<hero-form></hero-form>'
})
export class AppComponent { }
main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(AppModule);
index.html
<html>
  <head>
    <title>Hero Form</title>
    <base href="/">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link rel="stylesheet"
          href="https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="styles.css">
    <link rel="stylesheet" href="forms.css">

    <!-- Polyfills -->
    <script src="node_modules/core-js/client/shim.min.js"></script>

    <script src="node_modules/zone.js/dist/zone.js"></script>
    <script src="node_modules/systemjs/dist/system.src.js"></script>

    <script src="systemjs.config.js"></script>
    <script>
      System.import('main.js').catch(function(err){ console.error(err); });
    </script>
  </head>

  <body>
    <my-app>Loading...</my-app>
  </body>

</html>
forms.css
.ng-valid[required], .ng-valid.required  {
  border-left: 5px solid #42A948; /* green */
}

.ng-invalid:not(form)  {
  border-left: 5px solid #a94442; /* red */
}

Следующий шаг

Инъекция зависимостей

© 2010–2017 Google, Inc.
Licensed under the Creative Commons Attribution License 4.0.
https://v2.angular.io/docs/ts/latest/guide/forms.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API