表单
- 表单是业务应用程序的主体。您可以使用表单登录、提交帮助请求、下订单、预订航班、安排会议,以及执行无数其他数据输入任务。
- 在开发表单时,创建一种数据输入体验非常重要,这种体验可以有效地指导用户完成工作流。
一、Template-driven表单介绍
-
开发表单需要设计技能(这超出了本页面的范围),以及框架对双向数据绑定、更改跟踪、验证和错误处理的支持,您将在本页面了解这些内容。
-
这个页面向您展示了如何从头构建一个简单的表单。一路上你会学到:
- 使用组件和模板构建Angular表单。
- 使用ngModel创建双向数据绑定来读写输入控制值。
- 跟踪状态变化和表单控件的有效性。
- 使用跟踪控件状态的特殊CSS类提供可视化反馈。
- 向用户显示验证错误并启用/禁用表单控件。
- 使用模板引用变量在HTML元素之间共享信息。
-
您可以通过使用本页面中描述的特定于表单的指令和技术,在Angular模板语法中编写模板来构建表单。
-
您还可以使用反应性(或模型驱动)方法来构建表单。然而,本页主要关注模板驱动的表单。
-
您可以使用Angular模板构建几乎任何表单——登录表单、联系人表单,以及几乎任何业务表单。您可以创造性地布局控件,将它们绑定到数据,指定验证规则并显示验证错误,有条件地启用或禁用特定控件,触发内置的视觉反馈,等等。
-
Angular通过处理许多重复性的、样板式的任务,简化了这个过程,否则你就得自己处理这些任务了。
-
您将学习构建一个模板驱动的表单,如下所示:
-
Hero职业介绍所使用此表单来保存关于heroes的个人信息。每个英雄都需要一份工作。将正确的英雄与正确的危机匹配起来是公司的使命。
-
此表单上的三个字段中有两个是必需的。必填字段的左侧有一个绿色栏,以便于查找。
-
如果你删除了英雄的名字,表单会显示一个验证错误,以引起用户注意的方式显示:
-
注意,Submit按钮是禁用的,输入控件左侧的“required”栏从绿色变为红色。
-
你可以用标准的CSS自定义“必选”栏的颜色和位置。
-
您将构建这个表单的小步骤:
- 创建Hero模型类。
2. 创建控制表单的组件。
3. 使用初始表单布局创建模板。
4. 使用ngModel双向数据绑定语法将数据属性绑定到每个表单控件。
5. 为每个表单输入控件添加一个name属性。
6. 添加自定义CSS来提供视觉反馈。
7. 显示和隐藏验证错误消息。
8. 使用ngSubmit处理表单提交。
9. 在表单有效之前,禁用表单的Submit按钮。
- 创建Hero模型类。
二、Setup
-
创建一个名为angular-forms的新项目:
ng new angular-forms
三、 创建Hero模型类
-
当用户输入表单数据时,您将捕获他们的更改并更新模型的实例。在您知道模型是什么样子之前,您不能布置表单。
-
模型可以像“属性包”一样简单,它包含关于应用程序重要性的事实。这很好地描述了Hero类,它有三个必需字段(id、name、power)和一个可选字段(alterEgo)。
-
使用Angular的CLI命令ng生成类,生成一个名为Hero的新类:
```
ng generate class Hero
```
这个内容:
<-src/app/hero.ts->
export class Hero {
constructor(
public id: number,
public name: string,
public power: string,
public alterEgo?: string
) { }
}
-
它是一个贫血的模型,几乎没有需求,也没有行为。完美的演示。
-
TypeScript编译器为每个公共构造函数参数生成一个公共字段,并在创建英雄时自动将参数的值赋给该字段。
-
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"
```
四、 创建表单组件
-
Angular表单有两个部分:一个基于html的模板和一个以编程方式处理数据和用户交互的组件类。从类开始,因为它简单地说明了hero编辑器可以做什么。
-
使用Angular的CLI命令ng生成组件,生成一个名为HeroForm的新组件:
```
ng generate component HeroForm
```
-
这个内容:
<-src/app/hero-form/hero-form.component.ts (v1)-> 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; } // 待办事项:完成后将其移除 get diagnostic() { return JSON.stringify(this.model); } }
-
这个组件没有什么特别之处,没有什么特定于表单的地方,没有什么可以将它与您以前编写的任何组件区分开来。
-
理解这个组件只需要了解前面几页中提到的Angular概念。
- 代码导入了Angular核心库和你刚刚创建的Hero模型。
- “app-hero-form”的@Component选择器值意味着您可以使用标记将此表单放入父模板中。
- templateUrl属性指向模板HTML的一个单独文件。
- 您为模型和幂定义了虚拟数据,这与演示非常吻合。
-
接下来,您可以注入数据服务来获取和保存真实的数据,或者将这些属性公开为输入和输出(请参阅模板语法页面上的输入和输出属性),以便绑定到父组件。现在不需要考虑这个问题,将来的这些更改也不会影响表单。
-
您添加了一个诊断属性来返回模型的JSON表示。它将帮助您了解您在开发期间所做的工作;你给自己留了一张清理便条,以后可以把它丢掉。
五、Revise app.module.ts
-
app.module.ts定义应用程序的根模块。在其中,您将标识将在应用程序中使用的外部模块,并声明属于此模块的组件,例如HeroFormComponent。
-
因为模板驱动的表单位于它们自己的模块中,所以在使用表单之前,需要将FormsModule添加到应用程序模块的导入数组中。
-
更新如下:
```
<-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/hero-form.component';
@NgModule({
imports: [
BrowserModule,
FormsModule
],
declarations: [
AppComponent,
HeroFormComponent
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule { }
```
```
有两个变化:
1.你导入FormsModule。
2.您可以将FormsModule添加到@NgModule装饰器中定义的导入列表中。这使应用程序能够访问所有模板驱动的表单特性,包括ngModel。
```
```
如果组件、指令或管道属于导入数组中的模块,不要在声明数组中重新声明它。如果您编写了它,并且它应该属于这个模块,那么请在声明数组中声明它。
```
六、创建一个初始的HTML表单模板
-
用以下内容更新模板文件:
<-src/app/hero-form/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,并在输入框中为用户输入打开它们。
-
Name control具有HTML5 required属性;改变自我<输入>控制不因为改变自我是可选的。
-
您在底部添加了一个提交按钮,其中包含一些用于样式化的类。
-
你还没有使用Angular。没有绑定或额外的指令,只有布局
-
在模板驱动的表单中,如果您已经导入了FormsModule,那么您不需要对标记做任何操作就可以使用FormsModule。继续看它是如何工作的。
-
容器、表单组、表单控制和btn类都来自于Twitter引导。这些课程纯粹是装点门面。引导使表单有了一点样式。
ANGULAR表单不需要样式库
Angular没有使用容器、表单组、表单控件和btn类,也没有使用任何外部库的样式。Angular应用可以使用任何CSS库,也可以根本不使用。
- 要添加样式表,打开styles.css并在顶部添加以下导入行:
<-src/styles.css->
@import url('https://unpkg.com/[email protected]/dist/css/bootstrap.min.css');
七、 添加*ngFor的能力
-
英雄必须从一个固定的被机构认可的超能力列表中选择一个。您在内部维护这个列表(在HeroFormComponent中)。
-
您将向表单添加一个select,并使用ngFor将这些选项绑定到power列表,这是在前面的显示数据页面中看到的一种技术。
-
在Alter Ego组下面添加以下HTML:
<-src/app/hero-form/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>
- 这段代码为权力列表中的每个权力重复标记。pow模板输入变量在每次迭代中是不同的幂次;使用插值表达式显示它的名称。
八、 使用ngModel进行双向数据绑定
- 现在运行这个应用程序会让人失望。
-
你看不到英雄数据,因为你还没有绑定到英雄。您可以从前面的页面了解如何做到这一点。显示数据教会了属性绑定。用户输入显示了如何监听带有事件绑定的DOM事件,以及如何使用显示的值更新组件属性。
-
现在您需要同时显示、侦听和提取。
-
您可以使用您已经知道的技术,但是您将使用新的[(ngModel)]语法,这使得将表单绑定到模型非常容易。
-
找到 tag for Name并像这样更新它:
<-src/app/hero-form/hero-form.component.html (excerpt)->
<input type="text" class="form-control" id="name"
required
[(ngModel)]="model.name" name="name">
TODO: remove this: {{model.name}}
-
您在输入标签之后添加了一个诊断内插,这样您就可以看到您在做什么。你给自己留了张便条,写完就把它扔掉。
-
重点关注绑定语法:[(ngModel)]="…"。
-
为了显示数据,您还需要一个附加功能。为表单声明一个模板变量。用#heroForm="ngForm"更新标签如下:
<-src/app/hero-form/hero-form.component.html (excerpt)->
<form #heroForm="ngForm">
- 变量heroForm现在是一个NgForm指令的引用,该指令控制整个表单。
NgForm指令
1.NgForm指令是什么?您没有添加NgForm指令。
2.Angular会自动创建一个NgForm指令,并将其附加到<form>标记中。
3.NgForm指令用附加的特性来补充表单元素。它保存您使用ngModel指令和name属性为元素创建的控件,并监视它们的属性,包括它们的有效性。它还有自己的有效属性,只有在每个包含的控件都有效时才为真。
- 如果你现在运行应用程序,并开始在名称输入框中输入、添加和删除字符,你会看到它们在插入的文本中出现和消失。在某种程度上,它可能是这样的:
-
诊断是值确实从输入框流向模型并再次返回的证据。
-
这就是双向数据绑定。有关更多信息,请参见模板语法中的NgModel双向绑定。
-
注意,您还向标记添加了一个name属性,并将其设置为“name”,这对于英雄的名字是有意义的。任何惟一的值都可以,但是使用描述性名称是有帮助的。在将[(ngModel)]与表单结合使用时,需要定义name属性。
-
在内部,Angular创建FormControl实例,并用NgForm指令注册它们,并将其附加到标签上。每个FormControl都注册在您分配给name属性的名称下。阅读前一节的更多内容,NgForm指令。
-
添加类似的[(ngModel)]绑定和名字属性来改变自我和英雄力量。您将丢弃输入框绑定消息,并向组件的诊断属性添加一个新的绑定(在顶部)。然后您可以确认双向数据绑定适用于整个hero模型。
-
修改后,表单的核心应该是这样的:
```
<-src/app/hero-form/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>
```
```
1.每个输入元素都有一个id属性,label元素的for属性使用该属性将label与它的输入控件匹配。
2.每个输入元素都有一个name属性,Angular表单需要这个属性来将控件注册到表单中。
```
- 如果你现在运行应用程序,并改变每个英雄模型属性,表单可能会显示如下:
- 靠近表单顶部的诊断确认您的所有更改都反映在模型中。
删除顶部的{{diagnostic}}绑定,因为它已经达到了它的目的。
九、使用ngModel跟踪控制状态和有效性
-
在表单中使用ngModel不仅可以实现双向数据绑定。它还会告诉您用户是否触摸了控件,值是否发生了更改,或者值是否变得无效。
-
NgModel指令不仅仅跟踪状态;它使用反映状态的特殊Angular CSS类来更新控件。您可以利用这些类名来更改控件的外观。
- 临时将一个名为spy的模板引用变量添加到Name 标记中,并使用它来显示输入的CSS类。
<-src/app/hero-form/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}}
-
现在运行应用程序并查看名称输入框。准确地遵循以下步骤:
- 看但不要摸。
- 单击“名称”框内,然后单击“名称”框外。
- 在名字后面加上斜杠。
- 删除这个名字。
-
其作用和效果如下:
-
你应该看到以下转换和类名:
- ng-valid/ng-invalid对是最有趣的,因为您希望在值无效时发送强烈的可视信号。您还需要标记必填字段。要创建这样的视觉反馈,请添加ng-* CSS类的定义。
- 删除#spy模板引用变量和TODO,因为它们已经达到了它们的目的。
十、 为视觉反馈添加自定义CSS
- 您可以在输入框的左侧用一个有颜色的工具条同时标记必填字段和无效数据:
- 您可以通过将这些类定义添加到一个新forms.css文件中来实现这种效果,该文件是您作为index.html的兄弟添加到项目中的:
<-src/assets/forms.css->
.ng-valid[required], .ng-valid.required {
border-left: 5px solid #42A948; /* green */
}
.ng-invalid:not(form) {
border-left: 5px solid #a94442; /* red */
}
- 更新index.html的来包含这个样式表:
<-src/index.html (styles)->
<link rel="stylesheet" href="assets/forms.css">
十一、 显示和隐藏验证错误消息
-
您可以改进表单。名称输入框是必需的,清除它会使栏变成红色。这表明有些东西是错误的,但用户不知道什么是错误的,也不知道该怎么做。利用控件的状态来显示有用的消息。
-
当用户删除名称时,表单应该是这样的:
-
为了达到这个效果,将 tag扩展如下:
- 模板引用变量。
2. “is required”消息位于附近的中,只有在控件无效时才会显示该消息。
- 模板引用变量。
-
下面是一个添加到名称输入框的错误消息示例:
<-src/app/hero-form/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”。
-
通过将名称控件的属性绑定到消息元素的隐藏属性来控制名称错误消息的可见性。
<-src/app/hero-form/hero-form.component.html (hidden-error-msg)-> <div [hidden]="name.valid || name.pristine" class="alert alert-danger">
-
在本例中,在控件有效或原始状态时隐藏消息;“原始的”表示自该值在此表单中显示以来,用户没有更改过它。
-
这种用户体验是开发人员的选择。一些开发人员希望消息一直显示。如果忽略原始状态,则仅在值有效时才隐藏消息。如果您带着一个新的(空白的)英雄或者一个无效的英雄到达这个组件,您将在您做任何事情之前立即看到错误消息。
-
一些开发人员希望仅当用户进行无效更改时才显示消息。在控件处于“原始状态”时隐藏消息可以达到这个目的。当您向表单添加一个新的英雄时,您将看到这个选择的重要性。
-
英雄的另一个自我是可选择的,所以你可以离开。
-
需要英雄力量选择。如果需要,您可以向添加相同类型的错误处理,但这不是必须的,因为选择框已经将权限限制为有效值。
-
现在您将在这个表单中添加一个新的英雄。在表单底部放置一个新的英雄按钮,并将其单击事件绑定到一个newHero组件方法。
```
<-src/app/hero-form/hero-form.component.html (New Hero button)->
<button type="button" class="btn btn-default" (click)="newHero()">New Hero</button>
```
```
<-src/app/hero-form/hero-form.component.ts (New Hero method)->
newHero() {
this.model = new Hero(42, '', '');
}
```
-
再次运行应用程序,单击New Hero按钮,表单将被清除。输入框左侧所需的栏是红色的,表示无效的名称和电源属性。这是可以理解的,因为这些都是必填项。由于表单是原始的,所以隐藏了错误消息;你还没改变什么
-
输入一个名称,然后再次单击New Hero。应用程序显示一个名为is required的错误消息。当您创建一个新的(空的)英雄时,您不想要错误消息。你为什么现在要买一个?
-
在浏览器工具中检查元素,会发现名称输入框不再是原始状态。该表单记得您在单击New Hero之前输入了一个名称。替换hero对象并不能恢复表单控件的原始状态。
-
您必须严格地清除所有标记,这可以通过在调用newHero()方法之后调用表单的reset()方法来实现。
```
<-src/app/hero-form/hero-form.component.html (Reset the form)->
<button type="button" class="btn btn-default" (click)="newHero(); heroForm.reset()">New Hero</button>
```
- 现在单击“New Hero”将重置表单及其控制标志。
十二、 使用ngSubmit提交表单
-
用户应该能够提交此表单后,填写它。表单底部的Submit按钮本身不做任何事情,但是它将触发一个表单提交,因为它的类型(type=" Submit ")。
-
“表单提交”现在已经没用了。为了使它有用,将表单的ngSubmit事件属性绑定到英雄表单组件的onSubmit()方法:
<-src/app/hero-form/hero-form.component.html (ngSubmit)->
<form (ngSubmit)="onSubmit()" #heroForm="ngForm">
-
您已经定义了一个模板引用变量#heroForm,并使用值“ngForm”初始化它。现在,使用该变量通过Submit按钮访问表单。
-
您可以使用事件绑定,通过heroForm变量将表单的整体有效性绑定到按钮的禁用属性上。这是代码:
<-src/app/hero-form/hero-form.component.html (submit-button)->
<button type="submit" class="btn btn-success" [disabled]="!heroForm.form.valid">Submit</button>
- 如果现在运行应用程序,您会发现按钮是启用的—尽管它还没有做任何有用的事情。
- 现在,如果删除了名称,就违反了“required”规则,该规则在错误消息中得到了适当的注意。提交按钮也被禁用。
- 没有印象?想一下。如果没有Angular的帮助,你该怎么做才能把按钮的启用/禁用状态和表单的有效性联系起来呢?
- 对你来说,就是这么简单:
- 在(增强的)表单元素上设置一个模板引用变量。
2. 在许多行之外的按钮中引用该变量。
- 在(增强的)表单元素上设置一个模板引用变量。
十三、 切换两个表单区域(额外学分)
-
目前提交表单还不是很复杂。
-
对于一个演示来说,这是一个不足为奇的观察结果。说实话,让它活跃起来并不能教会你任何关于表单的新东西。但这是一个锻炼你新获得的绑定技能的机会。如果你不感兴趣,请跳到这一页的结论。
-
为了获得更引人注目的视觉效果,隐藏数据输入区域并显示其他内容。
-
将表单包装在中,并将其隐藏属性绑定到HeroFormComponent。提交属性。
<-src/app/hero-form/hero-form.component.html (excerpt)-> <div [hidden]="submitted"> <h1>Hero Form</h1> <form (ngSubmit)="onSubmit()" #heroForm="ngForm"> <!-- ... all of the form ... --> </form> </div>
-
主表单从一开始就是可见的,因为在您提交表单之前,提交的属性都是假的,正如来自HeroFormComponent的这段代码所示:
<-src/app/hero-form/hero-form.component.ts (submitted)->
submitted = false;
onSubmit() { this.submitted = true; }
-
当您单击Submit按钮时,Submit标志将变为true,表单将按计划消失。
-
现在,当表单处于提交状态时,应用程序需要显示其他内容。在包装下面添加以下HTML:
<-src/app/hero-form/hero-form.component.html (excerpt)-> <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 class="btn btn-primary" (click)="submitted=false">Edit</button> </div>
-
这又是一个英雄,用插值绑定显示为只读。只有当组件处于提交状态时才会出现这个。
-
该HTML包含一个Edit按钮,其单击事件绑定到一个清除已提交标记的表达式。
-
当您单击Edit按钮时,此块将消失,可编辑表单将重新出现。
十四、总结
本页中讨论的Angular表单利用了以下框架特性来提供对数据修改、验证等的支持:
- Angular的HTML表单模板。
2. 带有@Component装饰器的表单组件类。
3. 通过绑定到NgForm来处理表单提交。ngSubmit事件属性。
4. 模板引用变量,例如#heroForm和#name。
5. [(ngModel)]用于双向数据绑定的语法。
6. 使用名称属性进行验证和表单元素更改跟踪。
7. 用于检查控件是否有效并显示/隐藏错误消息的输入控件上的引用变量的有效属性。
8. 通过绑定到NgForm有效性来控制提交按钮的启用状态。
9. 自定义CSS类,为用户提供关于无效控件的可视反馈 。
十五、 下面是最终版本的应用程序代码:
<-hero-form/hero-form.component.ts->
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, '', '');
}
}
<-hero-form/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">{{ 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 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/hero-form.component';
@NgModule({
imports: [
BrowserModule,
FormsModule
],
declarations: [
AppComponent,
HeroFormComponent
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule { }
<-app.component.html->
<app-hero-form></app-hero-form>
<-app.component.ts->
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent { }
<-main.ts->
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
<-forms.css->
.ng-valid[required], .ng-valid.required {
border-left: 5px solid #42A948; /* green */
}
.ng-invalid:not(form) {
border-left: 5px solid #a94442; /* red */
}
ort { Component } from ‘@angular/core’;
@Component({
selector: ‘app-root’,
templateUrl: ‘./app.component.html’,
styleUrls: [’./app.component.css’]
})
export class AppComponent { }
<-main.ts->
import { enableProdMode } from ‘@angular/core’;
import { platformBrowserDynamic } from ‘@angular/platform-browser-dynamic’;
import { AppModule } from ‘./app/app.module’;
import { environment } from ‘./environments/environment’;
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
<-forms.css->
.ng-valid[required], .ng-valid.required {
border-left: 5px solid #42A948; /* green */
}
.ng-invalid:not(form) {
border-left: 5px solid #a94442; /* red */
}