网站首页 » 前端开发 » Angular 2+ » Angular 2+ 入门级示例
上一篇:
下一篇:

Angular 2+ 入门级示例

这是一篇关于angular 2+ 的入门级示例。因为是入门级,所以在这里不会对很多概念性的东西作过多的介绍,而是通过一个接近实战的业务来对 Angular 2 进行介绍。这样才不会让人觉得假大空的印象。这个实战示例分为三个部分:列表页、详情页、编辑页。数据通过模拟服务器请来来返回一个 promise 对象。

首先们来先来看看这个实战例子的一个整体结构:

angular 2+ 入门级示例

注意图片等静态资源要放到 assets 目录下。然后在.html 文件中这样引入就行:

<img src="assets/images/default.jpg" alt="">

在本示例中就不展示样式代码了,毕竟我们只需要关注业务代码就够了,首先我们来看看共用的部分:页面头部和页面底部。

header.component.html
<div id="header-box">
    <div class="header-back" *ngIf="showBack" (click)="back()">返回</div>
    <div class="header-title">{{title}}</div>
    <div class="trigger-button" *ngIf="trigger.status" (click)="triggerButton()">{{trigger.name}}</div>
</div>
header.component.ts
import { Component, EventEmitter, OnInit, Input, Output } from '@angular/core';
import { Router } from '@angular/router';
import { Location } from '@angular/common';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css']
})

export class HeaderComponent implements OnInit {
  @Input() title: string;
  @Input() trigger: any = { status: false };
  @Input() showBack: boolean;
  @Output() onTrigger = new EventEmitter<boolean>();

  constructor(
    private _router: Router,
    private _location: Location,
  ) { }

  ngOnInit() { }

  triggerButton = function () {
    // 还可以在这里添加统一的业务逻辑
    this.onTrigger.emit(); // 广播事件,让父组件捕获并执行相关操作
  }

  back = function (): void {
    this._location.back();
  }
}

头部代码中引入了 Location  来实现返回效果按钮效果,改入 EventEmitter 来向父组件传播事件。使用共用的头部在不同的页面下显示不同的东西,比如在列表页只显示页面标题,在详情页显示返回按钮、标题和编辑按钮,在编辑页则显示返回按钮、标题和保存按钮。我们通过给 header 组件传入相应的参数就可以实现。上面的 HeaderComponent 类继续了 OnInit 接口,所以必需在类中实现它的方法 ngOnInit() { },即使什么都没做。@Input 和 @Output 就是实现父子组件之间的数据传输的。

footer.component.html
<div id="footer-box">{{title}}</div>
footer.compenent.ts
import { Component, OnInit } from '@angular/core';
@Component({
  selector: 'app-footer',
  templateUrl: './footer.component.html',
  styleUrls: ['./footer.component.css']
})

export class FooterComponent implements OnInit {
  title = '底部';
  ngOnInit() { }
}

footer 组件在这里就非常地简单了,什么都没有只是单纯地显示两个文字。

list.compenent.html
<app-header title="列表"></app-header>
<div id="main">
  <ul class="list">
    <li *ngFor="let p of person" (click)='goDetail(p.id)'>
      <div class="user-icon">
        <img src="assets/images/default.jpg" alt="">
      </div>
      <div class="user-info">
        <div>{{p.name}}</div>
        <p>{{p.phone}}</p>
      </div>
    </li>
  </ul>
</div>
<app-footer></app-footer>
list.component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Person } from '../services/person';
import { PersonService } from '../services/person.service';

@Component({
  selector: 'list-person',
  templateUrl: './list.component.html',
  styleUrls: ['./list.component.css']
})

export class ListComponent implements OnInit {
  person: Person[];
  constructor(
    private _router: Router,
    private _personService: PersonService
  ) { }

  ngOnInit(): void {
    this.getData();
  }

  getData(): void {
    this._personService.getPerson().then(data => {
      this.person = data;
    });
  }

  goDetail = function (id): void {
    this._router.navigate(['/detail', id]);
  }
}

代码中的 person: Person[] 的意思就是声明一个元素类型为 Person 的数组。

detail.component.html
<app-header [showBack]="true" title="详情页" [trigger]="{status:true, name:'修改'}" (onTrigger)="edit()"></app-header>
<div id="main">
  <div class="detail" *ngIf="person">
    <ul class="detail-container">
      <li>
        <div class="detail-item">姓名:{{person.name}}</div>
        <div class="detail-item">性别:{{person.sex}}</div>
        <div class="detail-item">电话:{{person.phone}}</div>
        <div class="detail-item">email:{{person.email}}</div>
        <div class="detail-item">qq:{{person.qq}}</div>
      </li>
    </ul>
  </div>
</div>
<app-footer></app-footer>
detail.component.ts
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { Person } from '../services/person';
import { PersonService } from '../services/person.service';

@Component({
  selector: 'detail-person',
  templateUrl: './detail.component.html',
  styleUrls: ['./detail.component.css']
})

export class DetailComponent implements OnInit {
  person_id: string;
  person: Person;
  constructor(
    private _router: Router,
    private _activatedRoute: ActivatedRoute,
    private _personService: PersonService
  ) { }

  ngOnInit(): void {
    // 获取url中的参数
    this.person_id = this._activatedRoute.snapshot.params['id'];
    this._personService.queryPerson(this.person_id).then(person => {
      this.person = person;
    });
  }

  edit = function (): void {
    this._router.navigate(['/edit', this.person_id]);
  }
}

代码中引入了一个 ActivatedRoute,我们通过它来获取 url 中的参数。*ngIf=”person” 在代码中一定要添加这个,不然会报错,主要是因为页面渲染时,数据还没加载进来,导致 person 对象还是 undefined,而通过类似 person.name 来获取时就会报错。但方法也不是只有这一种,还有其它的方法。

聪明的你肯定会问:“那为什么列表页中没有添加 *ngIf=”person” 呢?”

因为 *ngFor 本身就可以达到这样的效果了。

下面的编辑页跟详情页一样,也需要添加 *ngIf=”person” 来防报错。

更详细的解决方案可以看这里:http://yunkus.com/angular-error-typeerror-cannot-read-property-of-undefined/

edit.component.html
<app-header [showBack]="true" title="编辑页" [trigger]="{name:'保存',status:true}" (onTrigger)="save()"></app-header>
<div id="main">
  <div class="edit" *ngIf="person">
    <form action="" #formStatus="ngForm">
      <ul class="edit-container">
        <li>
          <div class="edit-item">
            <label>姓名:</label>
            <input name="name" value="{{person.name}}" [(ngModel)]="person.name" type="text">
          </div>
          <div class="edit-item">
            <label>性别:</label>
            <select name="sexOptionSelect" [(ngModel)]="person.sex">
              <option *ngFor="let sexOption of sexOptions" [value]="sexOption.name">{{sexOption.name}}</option>
            </select>
          </div>
          <div class="edit-item">
            <label>电话:</label>
            <input name="phone" value="{{person.phone}}" [(ngModel)]="person.phone" type="text">
          </div>
          <div class="edit-item">
            <label>email:</label>
            <input name="email" value="{{person.email}}" [(ngModel)]="person.email" type="text">
          </div>
          <div class="edit-item">
            <label>qq:</label>
            <input name="qq" value="{{person.qq}}" [(ngModel)]="person.qq" type="text">
          </div>
        </li>
      </ul>
    </form>
  </div>
</div>
<app-footer></app-footer>
edit.component.ts
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { Person } from '../services/person';
import { PersonService } from '../services/person.service';

@Component({
  selector: 'edit-person',
  templateUrl: './edit.component.html',
  styleUrls: ['./edit.component.css']
})

export class EditComponent implements OnInit {
  person_id: string;
  person: Person;
  sexOptions: any[] = [
    { value: "male", name: "男" },
    { value: "female", name: "女" }
  ];

  constructor(
    private _router: Router,
    private _activatedRoute: ActivatedRoute,
    private _personService: PersonService
  ) { }

  ngOnInit(): void {
    // 获取url中的参数
    this.person_id = this._activatedRoute.snapshot.params['id'];
    this._personService.queryPerson(this.person_id).then(person => {
      this.person = person;
    });
  }

  save = function (): void {
    this._router.navigate(['/detail', this.person_id]);
  }
}

编辑页就很简单了,其实上面的 save 方法没有真正的保存数据,只是做了一个跳转而已,保存逻辑你可以根据自己的实现情况进行添加。

app.component.html
<div id="wrap">
  <router-outlet></router-outlet>
</div>
app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'person';
}

页面基本就是上面这些,下面就做点数据

person.ts
export class Person {
  id: string;
  name: string;
  sex: string;
  phone: string;
  email: string;
  qq: string;
}

声明一个 Person 类。

person-mock.ts
import { Person } from '../services/person';
export const PERSON: Person[] = [
  { id: '1', name: "小明", sex: "男", phone: "15889879211", email: "123121@163. com", qq: "111110171" },
  { id: '2', name: "小花", sex: "女", phone: "15889879221", email: "123122@163. com", qq: "111110172" },
  { id: '3', name: "小绿", sex: "男", phone: "15889879231", email: "123123@163. com", qq: "111110173" },
  { id: '4', name: "小黑", sex: "男", phone: "15889879241", email: "123124@163. com", qq: "111110174" },
  { id: '5', name: "小黄", sex: "女", phone: "15889879251", email: "123125@163. com", qq: "111110175" },
  { id: '6', name: "小五", sex: "男", phone: "15889879261", email: "123126@163. com", qq: "111110176" },
  { id: '7', name: "小七", sex: "男", phone: "15889879271", email: "123127@163. com", qq: "111110177" },
  { id: '8', name: "小菜", sex: "女", phone: "15889879281", email: "123128@163. com", qq: "111110178" },
]; 
person-service.ts
import { Injectable } from '@angular/core';
import { Person } from '../services/person';
import { PERSON } from '../services/person-mock';

@Injectable()
export class PersonService {
  getPerson(): Promise<Person[]> {
    return Promise.resolve(PERSON);
  }

  queryPerson = function (id): Promise<Person> {
    for (var i = 0; i < PERSON.length; i++) {
      if (PERSON[i].id == id) {
        return Promise.resolve(PERSON[i]);
      }
    }
  }
}

页面基本就是上面这些,下面我们就得把这些页面配置到路由中,让它们都各自有自己的一个访问地址。

app.routers.ts
import { NgModule } from '@angular/core';
// 这里需要注意提我们从 '@angular/router' 中引入的是 routes ,注意这个单词的写法
import { Routes, RouterModule } from '@angular/router';
import { ListComponent } from './list/list.component';
import { DetailComponent } from './detail/detail.component';
import { EditComponent } from './edit/edit.component';

export const rootRouterConfig: Routes = [
  // 设置默认路由前,请确保 redirectTo 的路由存在
  {
    path: "",
    redirectTo: "list",
    pathMatch: "full"
  },
  {
    path: 'list',
    component: ListComponent
  },
  {
    path: 'detail/:id',
    component: DetailComponent
  },
  {
    path: 'edit/:id',
    component: EditComponent
  },
];

@NgModule({
  imports: [RouterModule.forRoot(rootRouterConfig)],
  exports: [RouterModule]
})

export class AppRoutingModule { }

路由配置好后,我们就来做最后一步,把页面和路由都引入到 app.module.ts 根模块中。

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

// 自定义组件
import { AppRoutingModule } from './app.routers';
import { AppComponent } from './app.component';
import { HeaderComponent } from './share/header.component';
import { FooterComponent } from './share/footer.component';
import { ListComponent } from './list/list.component';
import { DetailComponent } from './detail/detail.component';
import { EditComponent } from './edit/edit.component';
import { PersonService } from './services/person.service';

@NgModule({
  declarations: [
    AppComponent,
    HeaderComponent,
    FooterComponent,
    ListComponent,
    DetailComponent,
    EditComponent,
  ],
  imports: [
    FormsModule,
    BrowserModule,
    AppRoutingModule
  ],
  providers: [PersonService], // 服务提供者,保证全局使用同一实例
  bootstrap: [AppComponent]
})
export class AppModule { }

Angular 的入门级实例就分享到这里。

  • 微信扫一扫,赏我

  • 支付宝扫一扫,赏我

声明

原创文章,不经本站同意,不得以任何形式转载,如有不便,请多多包涵!

本文永久链接:http://yunkus.com/angular-primer-example/

Leave a Reply

Your email address will not be published. Required fields are marked *

评论 END