Перейти к содержанию

Построение формы, управляемой шаблоном

📅 28.02.2022

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

Формы, основанные на шаблонах, используют двустороннее связывание данных для обновления модели данных в компоненте по мере внесения изменений в шаблон и наоборот.

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

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

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

В этом учебном пособии показано, как построить упрощенную форму, подобную форме из учебника Tour of Heroes, чтобы проиллюстрировать приемы.

Запустите или загрузите пример приложения:

Задачи

Этот учебник научит вас делать следующее:

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

Предварительные условия

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

Построение формы, управляемой шаблоном

Шаблонно-ориентированные формы полагаются на директивы, определенные в FormsModule.

Директива Подробности
NgModel Согласовывает изменения значений в прикрепленном элементе формы с изменениями в модели данных, позволяя вам реагировать на ввод пользователя с помощью валидации ввода и обработки ошибок.
NgForm Создает экземпляр верхнего уровня FormGroup и привязывает его к элементу <form> для отслеживания агрегированных значений формы и статуса проверки. Как только вы импортируете FormsModule, эта директива становится активной по умолчанию для всех тегов <form>. Вам не нужно добавлять специальный селектор.
NgModelGroup Создает и привязывает экземпляр FormGroup к элементу DOM.

Пример приложения

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

Эта форма помогает агентству найти подходящего героя и подходящий кризис.

Clean Form

Форма отличается некоторыми особенностями дизайна, которые облегчают ее использование. Например, два обязательных поля имеют зеленую полосу слева, чтобы их было легко заметить. Эти поля имеют начальные значения, поэтому форма действительна и кнопка Submit включена.

Работа с этой формой покажет вам:

  • Как включить логику валидации
  • Как настроить представление с помощью стандартного CSS
  • Как обрабатывать условия ошибки для обеспечения корректного ввода

Если пользователь, например, удаляет имя героя, форма становится недействительной. Приложение обнаруживает изменение статуса и отображает ошибку валидации в привлекающем внимание стиле.

Кнопка Submit не включается, а полоса "требуется" слева от элемента управления вводом меняется с зеленой на красную.

Invalid, Name Required

Обзор шагов

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

  1. Создайте базовую форму.

    • Определите образец модели данных.
    • Включите необходимую инфраструктуру, такую как FormsModule.
  2. Привяжите элементы управления формы к свойствам данных, используя директиву ngModel и синтаксис двусторонней привязки данных.

    • Изучите, как ngModel сообщает о состоянии элементов управления с помощью классов CSS
    • Назовите элементы управления, чтобы сделать их доступными для ngModel.
  3. Отслеживание валидности ввода и состояния элемента управления с помощью ngModel.

    • Добавьте пользовательский CSS для визуального отображения состояния.
    • Показывать и скрывать сообщения об ошибках валидации
  4. Реагируйте на событие нажатия кнопки в HTML, добавляя данные в модель.

  5. Обработка отправки формы с помощью выходного свойства ngSubmit формы.

    • Отключите кнопку Submit, пока форма не станет валидной.
    • После отправки поменяйте готовую форму на другой контент на странице

Постройте форму

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

  1. В представленном примере приложения создается класс Hero, который определяет модель данных, отражаемую в форме.

    1
    2
    3
    4
    5
    6
    7
    8
    export class Hero {
        constructor(
            public id: number,
            public name: string,
            public power: string,
            public alterEgo?: string
        ) {}
    }
    
  2. Макет и детали формы определяются в классе HeroFormComponent.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    import { Component } from '@angular/core';
    
    import { Hero } from '../hero';
    
    @Component({
        selector: 'app-hero-form',
        templateUrl: './hero-form.component.html',
        styleUrls: ['./hero-form.component.css'],
    })
    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;
        }
    }
    

    Значение selector компонента "app-hero-form" означает, что вы можете поместить эту форму в родительский шаблон с помощью тега <app-hero-form>.

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

    1
    2
    3
    4
    5
    6
    7
    8
    const myHero = new Hero(
        42,
        'SkyDog',
        'Fetch any object at any distance',
        'Leslie Rollover'
    );
    // "My hero is called SkyDog"
    console.log('My hero is called ' + myHero.name);
    

    В данной демонстрации используются фиктивные данные для model и powers. В реальном приложении для получения и сохранения реальных данных можно использовать сервис данных или представить эти свойства в качестве входов и выходов.

  4. Приложение включает функцию Forms и регистрирует созданный компонент формы.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { CommonModule } from '@angular/common';
    import { FormsModule } from '@angular/forms';
    
    import { AppComponent } from './app.component';
    import { HeroFormComponent } from './hero-form/hero-form.component';
    
    @NgModule({
        imports: [BrowserModule, CommonModule, FormsModule],
        declarations: [AppComponent, HeroFormComponent],
        providers: [],
        bootstrap: [AppComponent],
    })
    export class AppModule {}
    
  5. Форма отображается в макете приложения, заданном шаблоном корневого компонента.

    1
    <app-hero-form></app-hero-form>
    

    Начальный шаблон задает макет формы с двумя группами форм и кнопкой отправки. Группы форм соответствуют двум свойствам модели данных Hero — name и alterEgo. Каждая группа имеет метку и поле для ввода данных пользователем.

    • Элемент управления Name <input> имеет атрибут HTML5 required.
    • Элемент управления Alter Ego <input> не имеет, поскольку alterEgo является необязательным

    Кнопка Submit имеет несколько классов для стилизации. На данный момент макет формы представляет собой обычный HTML5, без привязок и директив.

  6. В примере формы используются некоторые классы стилей из Twitter Bootstrap: container, form-group, form-control и btn.

    Чтобы использовать эти стили, таблица стилей приложения импортирует библиотеку.

    1
    @import url('https://unpkg.com/[email protected]/dist/css/bootstrap.min.css');
    
  7. Форма предлагает кандидату в герои выбрать одну суперспособность из фиксированного списка способностей, одобренных агентством. Предопределенный список способностей является частью модели данных, поддерживаемой внутри HeroFormComponent. Директива Angular NgForOf выполняет итерации по значениям данных для заполнения элемента <select>.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    <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>
    

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

Early form with no binding

Привязка элементов управления вводом к свойствам данных

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

Директива ngModel, объявленная в модуле FormsModule, позволяет привязать элементы управления в форме, управляемой шаблоном, к свойствам модели данных. При включении директивы с использованием синтаксиса двусторонней привязки данных [(ngModel)] Angular может отслеживать значение и взаимодействие элемента управления с пользователем и синхронизировать представление с моделью.

  1. Отредактируйте файл шаблона hero-form.component.html.
  2. Найдите тег <input> рядом с меткой Name.
  3. Добавьте директиву ngModel, используя синтаксис двустороннего связывания данных [(ngModel)]="...".
1
2
3
4
5
6
7
8
9
<input
    type="text"
    class="form-control"
    id="name"
    required
    [(ngModel)]="model.name"
    name="name"
/>
TODO: remove this: {{model.name}}

В этом примере после каждого входного тега `{{model.name}}' имеется временная диагностическая интерполяция, показывающая текущее значение данных соответствующего свойства. Комментарий напоминает вам о необходимости удалить диагностические строки, когда вы закончите наблюдать за работой двусторонней привязки данных.

Доступ к общему состоянию формы

Когда вы импортировали FormsModule в свой компонент, Angular автоматически создал и присоединил директиву NgForm к тегу <form> в шаблоне (поскольку NgForm имеет селектор form, который соответствует элементам <form>).

Чтобы получить доступ к NgForm и общему состоянию формы, объявите переменную template reference variable.

  1. Отредактируйте файл шаблона hero-form.component.html.

  2. Обновите тег <form> с переменной ссылки шаблона, #heroForm, и установите ее значение следующим образом.

    1
    <form #heroForm="ngForm"></form>
    

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

  3. Запустите приложение.

  4. Начните вводить текст в поле ввода Имя.

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

    Например:

    ngModel in action

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

Именование элементов управления

При использовании [(ngModel)] в элементе необходимо определить атрибут name для этого элемента. Назначенное имя используется Angular для регистрации элемента в директиве NgForm, прикрепленной к родительскому элементу <form>.

В примере атрибут name добавлен к элементу <input> и установлен в значение "name", что вполне логично для имени героя.

Можно использовать любое уникальное значение, но полезно использовать описательное имя.

  1. Добавьте аналогичные привязки [(ngModel)] и атрибуты name для Alter Ego и Hero Power.
  2. Теперь можно удалить диагностические сообщения, показывающие интерполированные значения.

  3. Чтобы убедиться в том, что двустороннее связывание данных работает для всей модели героя, добавьте в шаблон компонента новую текстовую привязку с пайпом json в верхней части, которая сериализует данные в строку.

    После этих доработок шаблон формы должен выглядеть следующим образом:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    {{ model | json }}
    <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>
    
    • Обратите внимание, что каждый элемент <input> имеет свойство id.

      Этот атрибут используется атрибутом for элемента <label> для сопоставления метки с элементом управления вводом.

      Это стандартная функция HTML.

    • Каждый элемент <input> также имеет необходимое свойство name, которое Angular использует для регистрации элемента управления в форме.

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

    ngModel in action

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

  4. Когда вы наблюдаете эффект, вы можете удалить текстовую привязку {{ model | json }}.

Отслеживание состояний формы

Angular применяет класс ng-submitted к элементам form после того, как форма была отправлена. Этот класс можно использовать для изменения стиля формы после ее отправки.

Отслеживание состояний элементов управления

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

В следующей таблице описаны имена классов, которые Angular применяет в зависимости от состояния элемента управления.

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

Angular также применяет класс ng-submitted к элементам form при отправке, но не к элементам управления внутри элемента form.

Эти классы CSS используются для определения стилей элементов управления в зависимости от их состояния.

Наблюдение за состояниями системы управления

Чтобы увидеть, как фреймворк добавляет и удаляет классы, откройте инструменты разработчика браузера и осмотрите элемент <input>, представляющий собой имя героя.

  1. Используя инструменты разработчика браузера, найдите элемент <input>, соответствующий полю ввода Имя. Видно, что этот элемент имеет несколько CSS-классов, помимо "form-control".

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

    1
    2
    3
    4
    5
    <input
        
        class="form-control ng-untouched ng-pristine ng-valid"
        
    />
    
  3. Выполните следующие действия с полем Name <input> и понаблюдайте, какие классы появляются.

    • Смотрите, но не трогайте.

      Классы указывают на то, что оно нетронутое, девственное и действительное.

    • Щелкните внутри поля имени, затем щелкните за его пределами.

      Теперь элемент управления посещен, и у него появился класс ng-touched вместо класса ng-untouched.

    • Добавьте косые черты в конец имени.

      Теперь элемент тронут и испачкан.

    • Стереть имя.

      Это делает значение недействительным, поэтому класс ng-invalid заменяет класс ng-valid.

Создание визуальной обратной связи для состояний

Пара ng-valid/ng-invalid особенно интересна, потому что вы хотите посылать сильный визуальный сигнал, когда значения недействительны.

Вы также хотите пометить обязательные поля.

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

Invalid Form

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

  1. Добавьте определения для классов CSS ng-*.

  2. Добавьте определения этих классов в новый файл forms.css.

  3. Добавьте новый файл в проект как дочерний к index.html:

    1
    2
    3
    4
    5
    6
    7
    8
    .ng-valid[required],
    .ng-valid.required {
        border-left: 5px solid #42a948; /* green */
    }
    
    .ng-invalid:not(form) {
        border-left: 5px solid #a94442; /* red */
    }
    
  4. В файле index.html обновите тег <head>, чтобы включить новую таблицу стилей.

    1
    <link rel="stylesheet" href="assets/forms.css" />
    

Показывать и скрывать сообщения об ошибках валидации

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

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

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

Name required

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

Чтобы определить и показать сообщение об ошибке, когда это необходимо, выполните следующие действия.

  1. Расширьте тег <input> переменной-ссылкой шаблона, с помощью которой можно получить доступ к элементу управления Angular в окне ввода из шаблона.

    В примере переменная имеет значение #name="ngModel".

    Ссылочная переменная шаблона (#name) имеет значение "ngModel", так как это значение свойства NgModel.exportAs.

    Это свойство указывает Angular, как связать ссылочную переменную с директивой.

  2. Добавьте <div>, содержащий соответствующее сообщение об ошибке.

  3. Покажите или скройте сообщение об ошибке, привязав свойства элемента управления name к свойству hidden элемента сообщения <div>.

    1
    2
    3
    4
    <div
        [hidden]="name.valid || name.pristine"
        class="alert alert-danger"
    ></div>
    
  4. Добавьте условное сообщение об ошибке в поле ввода name, как показано в следующем примере.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    <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>
    

Иллюстрация "первозданного" состояния

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

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

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

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

Добавить нового героя

В этом упражнении показано, как можно реагировать на событие нажатия кнопки в HTML, добавляя данные в модель. Чтобы позволить пользователям формы добавить нового героя, вы добавите кнопку New Hero, реагирующую на событие щелчка.

  1. В шаблоне разместите в нижней части формы элемент <button> "Новый герой".

  2. В файле компонента добавьте метод hero-creation в модель данных hero.

    1
    2
    3
    newHero() {
        this.model = new Hero(42, '', '');
    }
    
  3. Привяжите событие нажатия кнопки к методу создания героя newHero().

    1
    2
    3
    4
    5
    6
    7
    <button
        type="button"
        class="btn btn-default"
        (click)="newHero()"
    >
        New Hero
    </button>
    
  4. Снова запустите приложение и нажмите кнопку Новый герой.

    Форма очистится, и слева от поля ввода появятся красные полосы, указывающие на недопустимые свойства name и power.

    Обратите внимание, что сообщения об ошибках скрыты.

    Это связано с тем, что форма чиста, вы еще ничего не изменили.

  5. Введите имя и снова нажмите Новый герой.

    Теперь приложение отображает сообщение об ошибке Name is required, поскольку поле ввода больше не является нетронутым.

    Форма помнит, что вы ввели имя перед тем, как нажать Новый герой.

  6. Чтобы восстановить первозданное состояние элементов управления формы, императивно очистите все флаги, вызвав метод формы reset() после вызова метода newHero().

    1
    2
    3
    4
    5
    6
    7
    <button
        type="button"
        class="btn btn-default"
        (click)="newHero(); heroForm.reset()"
    >
        New Hero
    </button>
    

    Теперь нажатие New Hero сбрасывает и форму, и ее флаги управления.

Отправьте форму с помощью ngSubmit

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

Чтобы отреагировать на это событие, выполните следующие действия.

  1. Привяжите свойство события формы ngSubmit к методу onSubmit() компонента hero-form.

    1
    <form (ngSubmit)="onSubmit()" #heroForm="ngForm"></form>
    
  2. Используйте переменную ссылки шаблона #heroForm для доступа к форме, содержащей кнопку Submit, и создайте привязку события.

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

    1
    2
    3
    4
    5
    6
    7
    <button
        type="submit"
        class="btn btn-success"
        [disabled]="!heroForm.form.valid"
    >
        Submit
    </button>
    
  3. Запустите приложение.

    Обратите внимание, что кнопка включена — хотя она пока не делает ничего полезного.

  4. Удалите значение Name.

    Это нарушает правило "required", поэтому выводится сообщение об ошибке — и обратите внимание, что это также отключает кнопку Submit.

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

    Модуль FormsModule сделал это автоматически, когда вы определили переменную ссылки шаблона на расширенный элемент формы, а затем сослались на эту переменную в элементе управления кнопкой.

Ответ на отправку формы

Чтобы показать ответ на отправку формы, вы можете скрыть область ввода данных и отобразить на ее месте что-то другое.

  1. Оберните всю форму в <div> и привяжите его свойство hidden к свойству HeroFormComponent.submitted.

    1
    2
    3
    4
    5
    6
    <div [hidden]="submitted">
        <h1>Hero Form</h1>
        <form (ngSubmit)="onSubmit()" #heroForm="ngForm">
            <!-- ... all of the form ... -->
        </form>
    </div>
    
    • Главная форма видна с самого начала, поскольку свойство submitted имеет значение false, пока вы не отправите форму, как показано в этом фрагменте из HeroFormComponent:

      1
      2
      submitted = false;
      onSubmit() { this.submitted = true; }
      
    • При нажатии на кнопку Submit флаг submitted становится истинным и форма исчезает.

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

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <div [hidden]="!submitted">
        <h2>You submitted the following:</h2>
        <div class="row">
            <div class="col-xs-3">Name</div>
            <div class="col-xs-9">{{ model.name }}</div>
        </div>
        <div class="row">
            <div class="col-xs-3">Alter Ego</div>
            <div class="col-xs-9">{{ model.alterEgo }}</div>
        </div>
        <div class="row">
            <div class="col-xs-3">Power</div>
            <div class="col-xs-9">{{ model.power }}</div>
        </div>
        <br />
        <button
            type="button"
            class="btn btn-primary"
            (click)="submitted=false"
        >
            Edit
        </button>
    </div>
    

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

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

  3. Нажмите кнопку Редактировать, чтобы переключить отображение обратно в редактируемую форму.

Резюме

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

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import { Component } from '@angular/core';

import { Hero } from '../hero';

@Component({
    selector: 'app-hero-form',
    templateUrl: './hero-form.component.html',
    styleUrls: ['./hero-form.component.css'],
})
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, '', '');
    }
}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
<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">{{ model.name }}</div>
        </div>
        <div class="row">
            <div class="col-xs-3">Alter Ego</div>
            <div class="col-xs-9">{{ model.alterEgo }}</div>
        </div>
        <div class="row">
            <div class="col-xs-3">Power</div>
            <div class="col-xs-9">{{ model.power }}</div>
        </div>
        <br />
        <button
            type="button"
            class="btn btn-primary"
            (click)="submitted=false"
        >
            Edit
        </button>
    </div>
</div>
1
2
3
4
5
6
7
8
export class Hero {
    constructor(
        public id: number,
        public name: string,
        public power: string,
        public alterEgo?: string
    ) {}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

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

@NgModule({
    imports: [BrowserModule, CommonModule, FormsModule],
    declarations: [AppComponent, HeroFormComponent],
    providers: [],
    bootstrap: [AppComponent],
})
export class AppModule {}
1
<app-hero-form></app-hero-form>
1
2
3
4
5
6
7
8
import { Component } from '@angular/core';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css'],
})
export class AppComponent {}
1
2
3
4
5
6
7
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';

platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .catch((err) => console.error(err));
1
2
3
4
5
6
7
8
.ng-valid[required],
.ng-valid.required {
    border-left: 5px solid #42a948; /* green */
}

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

Ссылки

Комментарии