Angular2 不明真相第一个Demo例子
如果不是去年换工作接触到AngularJS,估计是不会花时间去学习这个框架的,毕竟是前端的框架,不是自己熟悉的领域。但是为了混得下去,去年就学习了AngularJS的一些用法,当时还整理了一些积累 《AnguarJS测试的实施步骤整理》。
原以为可以凑合够用应付项目开发,没想到上个月公司前端扛大旗的人就说要使用Angular2,现有的AnagularJS 1.XX版本的代码会逐步升级到Angular2。去年就听说了AngularJS已经升级了,并且跟typescript联姻了,还听说typescript的语法跟C#很类似,说什么后台程序员学Angular2没有什么问题。也没有实际去学习,但目前项目需要用到,所以最近就启动了Angular2的学习过程,不指望在短期内精通,总得在新技术落地之前自己有个基础的知识,不至于手忙脚乱,也显得自己淡定。
接着讲讲Angular2,为什么少了JS?这就是最大的升级嘛?这也是个升级,没毛病。之前AngularJS的名称用于1.XXX版本,去年7月份Angular正式发布了2.0版本,也就这里说的Angular2。2.0版本几乎是break changes,也就是说跟之前的版本不兼容了,几乎推翻重来了,所以苦逼开发者还得重复学习轮子。新版本引入了typescript,typescript是javascript的超集,通过typescript可以生成javascript代码。然后还提出了很多新的概念,比如component,import,新的module,新的指令等等。总之有得你去学习。
这里不深入分析各个模块,自己也刚了解不久,这篇博文演示用Angular2创建一个Demo例子,用于直观感受,提提学习兴趣,找找自信心。
一、准备环境
a.安装Node.js和NPM
步骤不说了,安装完成之后命令行node –v, npm -v看看版本,比如我的分别是v6.9.2和3.10.9
b.找个开发工具,这里使用visual studio code,简单快捷
二、准备项目
a.准备package.json文件
新建一个目录todo/app
mkdir –p todo/app
然后cd到todo目录,新建一个package.json文件,使用过node.js的应该知道这是干嘛的,类似于asp.net core的project.json,定义了一些依赖内容。
package.json
{ "dependencies": { "@angular/common": "2.2.0", "@angular/compiler": "2.2.0", "@angular/core": "2.2.0", "@angular/forms": "2.2.0", "@angular/platform-browser": "2.2.0", "@angular/platform-browser-dynamic": "2.2.0", "reflect-metadata": "0.1.8", "rxjs": "5.0.0-beta.12", "zone.js": "0.6.26", "core-js": "2.4.1", "classlist.js": "1.1.20150312", "systemjs": "0.19.40", "bootstrap": "4.0.0-alpha.4" }, "devDependencies": { "lite-server": "2.2.2", "typescript": "2.0.3", "typings": "1.4.0", "concurrently": "3.1.0" }, "scripts": { "start": "concurrently \"npm run tscwatch\" \"npm run lite\" ", "tsc": "tsc", "tscwatch": "tsc -w", "lite": "lite-server", "typings": "typings" } }
看起来又是一堆面条,这里暂时从字面意思知道三大块的意思
dependencies:运行程序需要用到的依赖组件
dveDependencies:用于开发环境的依赖组件
scripts:可在命令行运行的脚本,这里包含了TypeScript的编译器和开发时用到的HTTP server
准备了package.json之后,执行如下命令
npm install
这是讲package.json的依赖的包下载到本地,可以理解为nuget的restore,这个命令需要些时间,稍等。
下载完成之后,那么在todo目录下会新增一个node_modules目录,都是些依赖包信息。
b.配置TypeScript编译器
之前提到Angular2可用Typescript生成JS代码,这是新版的重大卖点,用typescript可以利用些高级特性生成复杂的JS代码,同时也为开发人员省去了对兼容性的考虑。
在todo目录下创建如下tsconfig.json文件
{ "compilerOptions": { "target": "es5", "module": "commonjs", "moduleResolution": "node", "emitDecoratorMetadata": true, "experimentalDecorators": true }, "exclude": [ "node_modules" ] }
c.新建一个Index.html
好了回到最原始的一个文件,还是在todo目录下,内容如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>ToDo</title> <link href="node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" /> </head> <body> <h1>Content will go here</h1> </body> </html>
到此基本项目已经准备好,在todo目录运行用visual studio code打开,目录结构应该是这样的
然后好奇这个简单的项目能运行吗,当然可以。在todo目录下运行如下命令
npm start
然后根据提供的Access URL,在浏览器打开就可以了(实际运行之后FireFox自动就开打了这个URL)
就显示一行内容而已,不过也证明了是可以运行的。
通过上面的命令行可以看到实际上是运行了一个简单的HTTP Server,这个server还会监控index.html的页面变化,并实时在浏览器自动刷新显示最新的内容。比如我们把index.html更新为如下内容
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>ToDo</title> <link href="node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" /> </head> <body class="m-a-1"> <h3 class="bg-primary p-a-1">Junwen's To Do List</h3> <div class="m-t-1 m-b-1"> <input class="form-control" /> <button class="btn btn-primary m-t-1">Add</button> </div> <table class="table table-striped table-bordered"> <thead> <tr> <th>Description</th> <th>Done</th> </tr> </thead> <tbody> <tr><td>Buy Flowers</td><td>No</td></tr> <tr><td>Get Shoes</td><td>No</td></tr> <tr><td>Collect Tickets</td><td>Yes</td></tr> <tr><td>Call Joe</td><td>No</td></tr> </tbody> </table> </body> </html>
那么浏览器的内容会自动更新为如下
三、将Angular框架引入到项目中
更新index.html文件,更新为
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>ToDo</title> <link href="node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" /> <script src="node_modules/classlist.js/classList.min.js"></script> <script src="node_modules/core-js/client/shim.min.js"></script> <script src="node_modules/zone.js/dist/zone.min.js"></script> <script src="node_modules/reflect-metadata/Reflect.js"></script> <script src="node_modules/systemjs/dist/system.src.js"></script> </head> <body class="m-a-1"> <todo-app>Angular placeholder</todo-app> </body> </html>
这里引入了一些Angular项目需要依赖的JS文件,并且将body的内容更改成了一个自定义的标记,是不是感觉又回到了ASP.NET Core。
目前这里todo-app的标记,浏览器是没法识别和解析的,所以直接原样输出了内容
实现todo-app指令
接下来就是要实现todo-app的内容,切换到todo/app目录
a.新建model.ts文件
没错这就是我们要写的第一个typescript文件,内容如下
export class Model{ user: items; constructor(){ this.user = "Junwen Luo"; this.items = [ new ToDoItem("Firt Item", false), new ToDoItem("Second Item", true), new ToDoItem("Third Item", false), new ToDoItem("Fourth Item", false) ]; } } export class ToDoItem{ action; done; constructor(action, done){ this.action = action; this.done = done; } }
大致的意思就是新建了两个类,分别是Model和ToDoItem,然后有各自的构造方法,是不是很熟悉的套路呢。
这个语法浏览器当然不认账啦,.ts什么鬼,之前提到ts是javascript的超集,那么会在typescript编译器会将.ts文件生成对应的.js代码文件,保存.ts之后应该会生成一个model.js文件,代码如下
"use strict"; var Model = (function () { function Model() { this.user = "Junwen Luo"; this.items = [ new ToDoItem("Firt Item", false), new ToDoItem("Second Item", true), new ToDoItem("Third Item", false), new ToDoItem("Fourth Item", false) ]; } return Model; }()); exports.Model = Model; var ToDoItem = (function () { function ToDoItem(action, done) { this.action = action; this.done = done; } return ToDoItem; }()); exports.ToDoItem = ToDoItem;
如果代码没有自动生成,可以重新运行npm start。
b.新建一个模板,还是在app目录下,新建文件名为app.component.html
<h3 class="bg-primary p-a-1">{{getName()}}'s To Do List</h3> <div class="m-t-1 m-b-1"> <input class="form-control" #todoText> <button class="btn btn-primary m-t-1" (click)="addItem(todoText.value)"> Add </button> </div> <table class="table table-striped table-bordered"> <thead> <tr> <th></th> <th>Description</th> <th>Done</th> <th></th> </tr> </thead> <tbody> <tr *ngFor="let item of getTodoItems(); let i = index"> <td>{{i+1}}</td> <td>{{item.action}}</td> <td><input type="checkbox" [(ngModel)]="item.done" /></td> <td [ngSwitch]="item.done"> <span *ngSwitchCase="true">Yes</span> <span *ngSwitchDefault>No</span> </td> <td> <button (click)="removeItem(i)">Delete</button> </td> </tr> </tbody> </table>
这里{{}}表示的是绑定的内容,应该很好理解。
这个模板提供了显示列表,添加和删除Item的功能,其中使用了*ngFor,*ngSwitch,*ngSwitchDefault,ngModel等这些指令。
ngModel是用于双向绑定
(click)是绑定单击事件
*ngFor都比较好理解,for循环嘛
c.新建app.component.ts,内容如下
import { Component } from "@angular/core"; import { Model, ToDoItem } from "./model"; @Component({ selector: "todo-app", templateUrl: "app/app.component.html" }) export class AppComponent { model = new Model(); getName() { return this.model.user; } getTodoItems() { return this.model.items; } addItem(newItem) { if (newItem != "") { this.model.items.push(new ToDoItem(newItem, false)); } } removeItem(index) { console.log(index); if (index >= 0) { this.model.items.splice(index, 1); } } }
这里创建一个组件,意思是匹配到todo-app的标记都要通过templateUrl对应的模板来解析显示。
然后还定义一个类和方法,感觉像Controller。
import的作用就是导入需要依赖的内容,类似于命名空间引入吧。
d.新建app.module.ts
上一个文件只是一个Component,当然还是不能够直接运行,需要通过一个Module来封装,更1.XXX版本的思想理念是一致的。
app.module.ts内容如下 import { NgModule } from "@angular/core"; import { BrowserModule } from "@angular/platform-browser"; import { FormsModule } from "@angular/forms"; import { AppComponent } from "./app.component"; @NgModule({ imports:[BrowserModule, FormsModule], declarations:[AppComponent], bootstrap:[AppComponent] }) export class AppModule{}
e.新建main.ts
到此还不能运行了,还需要一个入口,可以理解为main方法吧。
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; import { AppModule } from "./app.module"; platformBrowserDynamic().bootstrapModule(AppModule);
f.更新index.html文件
上面都定义好了Angular的内容,那需要加入到index.html页面中,在原有的head加入如下scripts
<script> var paths = { "rxjs/*": "node_modules/rxjs/bundles/Rx.min.js", "@angular/*": "node_modules/@angular/*" } var packages = { "app": {} }; var angularModules = ["common", "compiler", "core", "forms", "platform-browser", "platform-browser-dynamic"]; angularModules.forEach(function (pkg) { packages["@angular/" + pkg] = { main: "/bundles/" + pkg + ".umd.min.js" }; }); System.config({ paths: paths, packages: packages }); System.import("app/main").catch(function(err){ console.error(err); }); </script>
看起来很麻烦吧,没错就是这样,到这里应该就完成了,这样html页面就跟Angular胜利会师了,看看运行的效果
CRD没有U的功能就这么简单实现,只当做第一个体验学习吧。
代码路径
https://github.com/shenba2014/ProAngular2ndExamples/tree/master/FirstAngularApp/