AngularDart4.0 指南- 表單

表單是商業應用程序的主流。您可使用表單登陸,提交幫助請求,下訂單,預訂航班,安排會議,並執行無數其餘數據錄入任務。css

在開發表單時,建立一個數據錄入體驗很是重要,該體驗能夠經過工做流高效地引導用戶。html

開發表單須要設計技巧(超出本頁面的範圍),以及雙向數據綁定,更改跟蹤,驗證和錯誤處理的框架支持,您將在本頁面上了解這些信息。java

本頁面向您展現瞭如何從頭構建一個簡單的表單。一路上你將學習如何:git

  • 用組件和模板構建一個Angular表單。
  • 使用ngModel建立讀取和寫入輸入控制值的雙向數據綁定。
  • 跟蹤狀態變化和表單控件的有效性。
  • 使用跟蹤控件狀態的特殊CSS類提供視覺反饋。
  • 向用戶顯示驗證錯誤並啓用/禁用表單控件。
  • 使用模板引用變量在HTML元素之間共享信息。

您能夠在Plunker中運行實例(查看源代碼)並從那裏下載代碼。github

模板驅動的形式

您能夠經過使用本頁中描述的特定於表單的指令和技術在Angular模板語法中編寫模板來構建表單。web

您也可使用響應式(或模型驅動)方法來構建表單。 可是,此頁面重點介紹模板驅動的表單。編程

您可使用Angular模板 構建幾乎任何表單- 登陸表單,聯繫表單和幾乎任何業務表單。 您能夠創造性地設計控件,將它們綁定到數據,指定驗證規則和顯示驗證錯誤,有條件地啓用或禁用特定控件,觸發內置的視覺反饋等等。bootstrap

Angular經過許多重複的,模板化的任務使處理過程變得簡單。api

您將學習如何構建一個模板驅動的表單,以下所示:瀏覽器

英雄就業機構使用這種形式來維護關於英雄的我的信息。 每一個英雄都須要一份工做。 讓正確的英雄與正確的危機相匹配是公司的使命。

這個表格中的三個字段中的兩個是必需的。 遵循材料設計準則,必填字段帶有星號(*)。

若是您刪除了英雄名稱,表單將以吸引人注意的風格顯示驗證錯誤:

請注意提交按鈕被禁用,而且輸入控件從綠色變爲紅色。

您將以小步驟構建此表單:

  1. 建立英雄模型類。
  2. 建立控制表單的組件。
  3. 用初始表單佈局建立一個模板。
  4. 使用ngModel雙向數據綁定語法將數據屬性綁定到每一個表單控件。
  5. 爲每一個表單輸入控件添加一個ngControl指令。
  6. 添加自定義CSS來提供視覺反饋。
  7. 顯示和隱藏驗證錯誤消息。
  8. 使用ngSubmit處理表單提交。
  9. 禁用窗體的提交按鈕,直到窗體有效。

創建

按照設置說明建立一個名爲表單的新項目。

添加angular_forms

Angular表單功能位於angular_forms庫中,該庫位於其本身的包中。 將該包添加到pubspec依賴項:

建立一個模型

當用戶輸入表單數據時,您將捕獲其更改並更新模型的實例。 直到你知道模型是什麼樣子,你才能佈置表格。

一個模型能夠像「錢包」同樣簡單,掌握關於應用程序重要事實的事實。 這很好地描述了英雄類與三個必填字段(id, name, power)和一個可選字段(alterEgo)。

在lib目錄中,使用給定的內容建立如下文件:lib/src/hero.dart

class Hero {
  int id;
  String name, power, alterEgo;
  Hero(this.id, this.name, this.power, [this.alterEgo]);
  String toString() => '$id: $name ($alterEgo). Super power: $power';
}

這是一個缺少要求,沒有行爲的雞肋模型,對於演示來講足夠了。

alterEgo是可選的,因此構造函數可讓你忽略它。 請注意[this.alterEgo]中的括號。

你能夠像這樣建立一個新的英雄:

var myHero = new Hero(
    42, 'SkyDog', 'Fetch any object at any distance', 'Leslie Rollover');
print('My hero is ${myHero.name}.'); // "My hero is SkyDog."

建立一個基本的表單

一個Angular表單有兩個部分:一個基於HTML的模板和一個組件類,以編程方式處理數據和用戶交互。 從課程開始,由於它簡要地說明了英雄編輯能夠作什麼。

建立一個表單組件

使用給定的內容建立如下文件:lib/src/hero_form_component.dart (v1)

import 'package:angular/angular.dart';
import 'package:angular_forms/angular_forms.dart';

import 'hero.dart';

const List<String> _powers = const [
  'Really Smart',
  'Super Flexible',
  'Super Hot',
  'Weather Changer'
];

@Component(
  selector: 'hero-form',
  templateUrl: 'hero_form_component.html',
  directives: const [CORE_DIRECTIVES, formDirectives],
)
class HeroFormComponent {
  Hero model = new Hero(18, 'Dr IQ', _powers[0], 'Chuck Overstreet');
  bool submitted = false;

  List<String> get powers => _powers;

  void onSubmit() => submitted = true;
}

這個組件沒有什麼特別之處,沒有任何特定的形式,沒有什麼區別它與你以前寫的任何組件。

理解這個組件只須要前面幾頁中介紹的Angular概念。

  • 代碼導入您剛建立的主Angular庫和Hero模型。
  • hero-form@Component選擇器值意味着您可使用<hero-form>元素將此表單放在父模板中。
  • templateUrl屬性指向模板HTML的單獨文件(您將很快建立)。
  • 您爲modelpower定義了模擬數據。

順便說一句,您能夠注入數據服務來獲取和保存真實數據,或者將這些屬性做爲輸入和輸出(請參閱「模板語法」頁面中的輸入和輸出屬性)來綁定到父組件。 這不是如今的問題,這些將來的變化不會影響表單。

修改應用程序組件

AppComponent是應用程序的根組件。 它將承載HeroFormComponent

將初學者應用版本的內容替換爲如下內容:lib/app_component.dart

import 'package:angular/angular.dart';

import 'src/hero_form_component.dart';

@Component(
  selector: 'my-app',
  template: '<hero-form></hero-form>',
  directives: const [HeroFormComponent],
)
class AppComponent {}

建立一個初始表單模板

使用如下內容建立模板文件:lib/src/hero_form_component.html (start)

<div class="container">
  <h1>Hero Form</h1>
  <form>
    <div class="form-group">
      <label for="name">Name&nbsp;*</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>
    <div class="row">
      <div class="col-auto">
        <button type="submit" class="btn btn-primary">Submit</button>
      </div>
      <small class="col text-right">*&nbsp;Required</small>
    </div>
  </form>
</div>

該語言只是HTML5。 您將展現兩個Hero字段,namealterEgo,並在輸入框中將其打開以供用戶輸入。

Name <input>控件具備HTML5必需屬性; Alter Ego <input>控件什麼也不作,由於alterEgo是可選的。

您在底部添加了一個提交按鈕,其中有一些類用於樣式。

你尚未使用Angular。 沒有綁定或額外的指令,只是佈局。

在模板驅動的表單中,若是已經導入了angular_forms庫,則沒必要爲了使用庫功能而對<form>標記執行任何操做。 繼續看看這是如何工做的。

刷新瀏覽器。 你會看到一個簡單的,沒有樣式的表單。

表單的樣式

通常的CSS類containerbtn來自Bootstrap。 Bootstrap還具備form-specific的類,包括form-controlform-group。 一塊兒,這些給表單了一些樣式。

Angular可不使用Bootstrap類或任何外部庫的樣式。 Angular的應用程序可使用任何CSS庫或不使用。

經過將如下連接插入到index.html的<head>中來添加Bootstrap樣式:web/index.html (bootstrap)

<link rel="stylesheet"
      href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css"
      integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M"
      crossorigin="anonymous">

刷新瀏覽器。 你會看到一個樣式化的表單!

使用* ngFor添加powers

英雄必須從一個固定的機構批准的權力列表中選擇一個超級大國。 您在內部維護該列表(在HeroFormComponent中)。

您將在表單中添加一個select,並使用ngFor(先前在「顯示數據」頁面中看到的一種技術)將選項綁定到powers列表。

在Alter Ego group下方添加如下HTML:lib/src/hero_form_component.html (powers)

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

這段代碼重複列表中每一個power 的<option>標籤。 p模板輸入變量在每次迭代中是不一樣的power; 您使用插值語法顯示其名稱。

與ngModel的雙向數據綁定

如今運行應用程序有點使人失望。

你沒有看到英雄數據,由於你尚未綁定到英雄。 你知道如何從早期的頁面作到這一點。 顯示數據教導屬性綁定。 用戶輸入顯示如何使用事件綁定監聽DOM事件以及如何使用顯示的值更新組件屬性。

如今您須要同時顯示,聆聽和提取。

你可使用你已經知道的技術,可是你會使用新的[(ngModel)]語法,這使得綁定到模型的表單變得容易。

找到Name的<input>標籤,並像下面這樣更新它:lib/src/hero_form_component.html (name)

<!-- TODO: remove the next diagnostic line -->
<mark>{{model.name}}</mark><hr>
<div class="form-group">
  <label for="name">Name&nbsp;*</label>
  <input type="text" class="form-control" id="name" required
         [(ngModel)]="model.name"
         ngControl="name">
</div>

你在form-group以前添加了一個診斷插值,因此你能夠看到你在作什麼。 當你完成的時候,你留下一張紙條扔掉它。

關注綁定語法:[(ngModel)] =「...」。

如今運行應用程序並輸入名稱輸入,添加和刪除字符。 您會看到這些字符出如今診斷文本中並消失。 在某個時候,它可能看起來像這樣:

診斷結果代表數值確實是從輸入流向模型,再返回。

這是雙向的數據綁定。 有關更多信息,請參見模板語法頁面上的與NgModel的雙向綁定。

請注意,您還爲<input>標記添加了一個ngControl指令,並將其設置爲「name」,這對於英雄的名字是有意義的。 任何惟一值將會這樣作,但使用描述性名稱是有幫助的。 將[(ngModel)]與表單結合使用時,定義ngControl指令是一項要求。

在內部,Angular建立NgFormControl實例,並使用Angular附加到<form>標籤的NgForm指令註冊它們。 每一個NgFormControl都是在您分配給ngControl指令的名稱下注冊的。 本指南稍後將詳細介紹NgForm

Alter EgoHero Power添加相似的[(ngModel)]綁定和ngControl指令。

model替換診斷綁定表達式。 經過這種方式,您能夠確認雙向數據綁定適用於整個英雄模型。

修改後,表單的核心應該是這樣的:lib/src/hero_form_component.html (controls)

<!-- TODO: remove the next diagnostic line -->
<mark>{{model}}</mark><hr>
<div class="form-group">
  <label for="name">Name&nbsp;*</label>
  <input type="text" class="form-control" id="name" required
         [(ngModel)]="model.name"
         ngControl="name">
</div>
<div class="form-group">
  <label for="alterEgo">Alter Ego</label>
  <input type="text" class="form-control" id="alterEgo"
         [(ngModel)]="model.alterEgo"
         ngControl="alterEgo">
</div>
<div class="form-group">
  <label for="power">Hero Power&nbsp;*</label>
  <select class="form-control" id="power" required
          [(ngModel)]="model.power"
          ngControl="power">
    <option *ngFor="let p of powers" [value]="p">{{p}}</option>
  </select>
</div>
  • 每一個input元素都有一個id屬性,label元素的for屬性使用它來匹配labelinput控件。
  • 每一個input元素都有一個ngControl指令,Angular表單須要用這個指令在表單上註冊控件。

若是您如今運行應用程序並更改每一個英雄model屬性,表單可能會顯示以下:

靠近表單頂部的診斷確認全部的更改都反映在model中。

從模板中刪除診斷綁定,由於它已經達到了目的。

根據控制狀態給出視覺反饋

使用CSS和類綁定,您能夠更改表單控件的外觀以反映其狀態。

跟蹤控制狀態

Angular表單控件能夠告訴您用戶是否觸摸了該控件,值是否改變,或者該值是否失效。

每一個Angular控制(NgControl)都跟蹤本身的狀態,並經過如下字段成員使狀態可供檢查:

  • dirtypristine代表控制的值是否已經改變。
  • toucheduntouched指示控件是否被訪問過。
  • valid反映了控制值的有效性。

樣式控件

有效的控制屬性是最有趣的,由於當一個控制值無效時,你想發送一個強烈的視覺信號。 要建立這樣的視覺反饋,您將使用Bootstrap自定義表單類 is-validis-invalid

將名爲name模板引用變量添加到Name <input>標記中。 使用name類綁定來有條件地分配適當的表單有效性類。

臨時將另外一個名爲spy的模板引用變量添加到Name <input>標記,並使用它顯示輸入的CSS類。

lib/src/hero_form_component.html (name)

<input type="text" class="form-control" id="name" required
       [(ngModel)]="model.name"
       #name="ngForm"
       #spy
       [class.is-valid]="name.valid"
       [class.is-invalid]="!name.valid"
       ngControl="name">
<!-- TODO: remove the next diagnostic line -->
{{spy.className}}

模板引用變量

spy模板引用變量綁定到<input> DOM元素,而name變量(經過#name =「ngForm」語法)綁定到與input元素關聯的NgModel

爲何「ngForm」? 指令的exportAs屬性告訴Angular如何將引用變量連接到指令。 您將name設置爲「ngForm」,由於ngModel指令的exportAs屬性是「ngForm」。

刷新瀏覽器,而後按照下列步驟操做:

1.看看名字輸入。

  • 它有一個綠色的邊框。
  • 它具備類形式控制和有效性。

2.經過添加一些字符來更改name。 類保持不變。
3.刪除名稱。

  • 輸入框邊框變爲紅色。
  • is-invalid類替換爲is-valid。

刪除#spy模板引用變量和使用它的診斷。

做爲類綁定的替代方法,可使用NgClass指令來設置控件的樣式。 首先,添加如下方法來設置控件的依賴於狀態的CSS類名稱:

lib/src/hero_form_component.dart (setCssValidityClass)

Map<String, bool> setCssValidityClass(NgControl control) {
  final validityClass = control.valid == true ? 'is-valid' : 'is-invalid';
  return {validityClass: true};
}

使用此方法返回的映射值綁定到NgClass指令 - 在模板語法頁面中詳細瞭解此指令及其替代方法。

lib/src/hero_form_component.html (power)

<select class="form-control" id="power" required
        [(ngModel)]="model.power"
        #power="ngForm"
        [ngClass]="setCssValidityClass(power)"
        ngControl="power">
  <option *ngFor="let p of powers" [value]="p">{{p}}</option>
</select>

顯示並隱藏驗證錯誤消息


你能夠改善表格。 名稱輸入是必需的,清除它將框的輪廓變爲紅色。 這說明有些事情是錯的,但用戶不知道什麼是錯的,或者該怎麼作。 利用控件的狀態來顯示有用的消息。

使用有效的和原始的狀態

當用戶刪除名稱時,表單應該以下所示:

爲了達到這個效果,在Name <input>以後當即添加下面的<div>

lib/src/hero_form_component.html (hidden error message)

<div [hidden]="name.valid || name.pristine" class="invalid-feedback">
  Name is required
</div>

刷新瀏覽器並刪除Name 輸入。 顯示錯誤消息。

您能夠經過根據名稱控制的狀態設置<div>隱藏屬性來控制錯誤消息的可見性。

在這個例子中,當控件是有效的或者原始的時候隱藏消息 - 「pristine」意味着用戶沒有改變這個值,由於它是以這種形式顯示的。

用戶體驗是開發者的選擇

有些開發人員但願消息始終顯示。 若是您忽略原始狀態,則只有在該值有效時纔會隱藏該消息。 若是您使用新(空白)英雄或無效英雄到達此組件,則在您執行任何操做以前,您將當即看到錯誤消息。

有些開發人員但願僅在用戶進行無效更改時顯示消息。 當控件是「原始的」時隱藏消息實現了這個目標。 當您向表單添加一個「清除」按鈕時,您會看到此選項的重要性。

英雄Alter Ego是可選的,因此你能夠不用關那個。

英雄power選擇是必需的。 若是須要,能夠將相同類型的錯誤消息添加到<select>中,但這不是必須的,由於選擇框已經將權限限制爲有效值。

添加一個清除按鈕

clear()方法添加到組件類中:lib/src/hero_form_component.dart (clear)

void clear() {
  model.name = '';
  model.power = _powers[0];
  model.alterEgo = '';
}

在提交按鈕後面添加一個帶有點擊事件綁定的清除按鈕:lib/src/hero_form_component.html (Clear button)

<button (click)="clear()" type="button" class="btn">
  Clear
</button>

刷新瀏覽器。 點擊清除按鈕。 文本字段變爲空白,若是您更改了power,它將恢復爲默認值。

用ngSubmit提交表單

用戶應該可以在填寫表單後提交這個表單。表單底部的Submit按鈕自己不作任何事情,可是因爲它的類型(type =「submit」),它會觸發一個表單提交。

表單提交目前是無用的。 爲了使它有用,將表單組件的onSubmit()方法分配給表單的ngSubmit事件綁定:

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

請注意模板引用變量#heroForm。 正如前面所解釋的,變量heroForm被綁定到總體管理表單的NgForm指令。

NgForm指令

Angular自動建立並附加一個NgForm指令給<form>標籤。

NgForm指令補充表單元素的附加功能。 它包含用ngModelngControl指令爲元素建立的控件,並監視它們的屬性,包括它們的有效性。

您將經過heroForm變量將表單的總體有效性綁定到按鈕的disabled屬性:

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

刷新瀏覽器。 你會發現這個按鈕是啓用的,儘管它沒有作任何有用的事情。

如今,若是您刪除Name,則違反了「必需的」規則,這在錯誤消息中正確記錄。 提交按鈕也被禁用。

沒有留下深入印象? 想想。 若是沒有Angular的幫助,你須要作什麼才能將按鈕的啓用/禁用狀態鏈接到表單的有效性?

對你來講,這很簡單:

  • 在(加強的)表單元素上定義一個模板引用變量。
  • 在多處的按鈕中引用該變量。

顯示Model(可選)

提交表單目前沒有視覺效果。

如預期的演示。 增長代碼事後的demo不會教你任何關於表單的新東西。 可是這是一個鍛鍊一些新得到的綁定技巧的機會。 若是您不感興趣,請跳至本頁的摘要

做爲一種視覺效果,您能夠隱藏數據輸入區域並顯示其餘內容。

將表單封裝在<div>中,並將其hidden屬性綁定到HeroFormComponent.submitted屬性。

lib/src/hero_form_component.html (excerpt)

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

該表單從一開始就是可見的,由於在提交表單以前,提交的屬性爲false,由於HeroFormComponent中的片斷顯示爲:lib/src/hero_form_component.dart (submitted)

bool submitted = false;

void onSubmit() => submitted = true;

如今在剛剛寫的<div>包裝器下面添加下面的HTML:lib/src/hero_form_component.html (submitted)

<div [hidden]="!submitted">
  <h1>Hero data</h1>

  <table class="table">
    <tr>
      <th>Name</th>
      <td>{{model.name}}</td>
    </tr>
    <tr>
      <th>Alter Ego</th>
      <td>{{model.alterEgo}}</td>
    </tr>
    <tr>
      <th>Power</th>
      <td>{{model.power}}</td>
    </tr>
  </table>

  <button (click)="submitted=false" class="btn btn-primary">Edit</button>
</div>

刷新瀏覽器並提交表單。 提交的標誌變爲真,表格消失。 您將看到表格中顯示的英雄模型值(只讀)。

該視圖包含一個編輯按鈕,其單擊事件綁定將清除提交的標誌。 當您單擊編輯按鈕時,該表消失,而且可編輯的表單從新出現。

概要

Angular表單爲數據修改,驗證等提供支持。 在此頁面中,您學習瞭如何使用如下功能:

  • 一個HTML表單模板和一個帶有@Component註解的表單組件類。
  • 表單提交,經過ngSubmit事件綁定處理。
  • 模板引用變量,如heroFormname
  • 雙向數據綁定([(ngModel)])。
  • 用於驗證和表單元素更改跟蹤的NgControl 指令。
  • 輸入控件(經過模板引用變量訪問)的valid 屬性,用於檢查控件有效性以及顯示/隱藏錯誤消息。
  • NgForm.form的有效性來設置提交按鈕的啓用狀態。
  • 自定義CSS類爲用戶提供有關控制狀態的可視反饋。

最終的項目文件夾結構應該以下所示:

如下是應用程序最終版本的代碼:

lib/app_component.dart

import 'package:angular/angular.dart';
import 'src/hero_form_component.dart';
@Component(
  selector: 'my-app',
  template: '<hero-form></hero-form>',
  directives: const [HeroFormComponent],
)
class AppComponent {}

lib/src/hero.dart 

class Hero {
  int id;
  String name, power, alterEgo;
  Hero(this.id, this.name, this.power, [this.alterEgo]);
  String toString() => '$id: $name ($alterEgo). Super power: $power';
}

lib/src/hero_form_component.dart

import 'package:angular/angular.dart';
import 'package:angular_forms/angular_forms.dart';
import 'hero.dart';
const List<String> _powers = const [
  'Really Smart',
  'Super Flexible',
  'Super Hot',
  'Weather Changer'
];
@Component(
  selector: 'hero-form',
  templateUrl: 'hero_form_component.html',
  directives: const [CORE_DIRECTIVES, formDirectives],
)
class HeroFormComponent {
  Hero model = new Hero(18, 'Dr IQ', _powers[0], 'Chuck Overstreet');
  bool submitted = false;
  List<String> get powers => _powers;
  void onSubmit() => submitted = true;
  /// Returns a map of CSS class names representing the state of [control].
  Map<String, bool> setCssValidityClass(NgControl control) {
    final validityClass = control.valid == true ? 'is-valid' : 'is-invalid';
    return {validityClass: true};
  }
  void clear() {
    model.name = '';
    model.power = _powers[0];
    model.alterEgo = '';
  }
}

lib/src/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&nbsp;*</label>
        <input type="text" class="form-control" id="name" required
               [(ngModel)]="model.name"
               #name="ngForm"
               [class.is-valid]="name.valid"
               [class.is-invalid]="!name.valid"
               ngControl="name">
        <div [hidden]="name.valid || name.pristine" class="invalid-feedback">
          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"
               ngControl="alterEgo">
      </div>
      <div class="form-group">
        <label for="power">Hero Power&nbsp;*</label>
        <select class="form-control" id="power" required
                [(ngModel)]="model.power"
                #power="ngForm"
                [ngClass]="setCssValidityClass(power)"
                ngControl="power">
          <option *ngFor="let p of powers" [value]="p">{{p}}</option>
        </select>
      </div>
      <div class="row">
        <div class="col-auto">
          <button [disabled]="!heroForm.form.valid" type="submit" class="btn btn-primary">
            Submit
          </button>
          <button (click)="clear()" type="button" class="btn">
            Clear
          </button>
        </div>
        <small class="col text-right">*&nbsp;Required</small>
      </div>
    </form>
  </div>
  <div [hidden]="!submitted">
    <h1>Hero data</h1>
    <table class="table">
      <tr>
        <th>Name</th>
        <td>{{model.name}}</td>
      </tr>
      <tr>
        <th>Alter Ego</th>
        <td>{{model.alterEgo}}</td>
      </tr>
      <tr>
        <th>Power</th>
        <td>{{model.power}}</td>
      </tr>
    </table>
    <button (click)="submitted=false" class="btn btn-primary">Edit</button>
  </div>
</div>

web/index.html

<!DOCTYPE html>
<html>
  <head>
    <script>
      // WARNING: DO NOT set the <base href> like this in production!
      // Details: https://webdev.dartlang.org/angular/guide/router
      (function () {
        var m = document.location.pathname.match(/^(\/[-\w]+)+\/web($|\/)/);
        document.write('<base href="' + (m ? m[0] : '/') + '" />');
      }());
    </script>
    <title>Hero Form</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet"
          href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css"
          integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M"
          crossorigin="anonymous">
    <link rel="stylesheet" href="styles.css">
    <link rel="icon" type="image/png" href="favicon.png">
    <script defer src="main.dart" type="application/dart"></script>
    <script defer src="packages/browser/dart.js"></script>
  </head>
  <body>
    <my-app>Loading ...</my-app>
  </body>
</html>

web/main.dart

import 'package:angular/angular.dart';
import 'package:forms/app_component.dart';
void main() {
  bootstrap(AppComponent);
}
相關文章
相關標籤/搜索