Getting started with Angular 2: Part 2

Getting Started

In the first post, we used Angular CLI to generate the project skeleton, and used ng serve to run this application on Angular CLI built-in webpack dev server.

Now we wil add more components, Angular CLI provides ng generate to create a component quickly. Follow the steps that we have done in Getting started with Angular 1.5 and ES6, we will create a simple blog application.

You can get source codes from Github.

Generate posts component

Enter the project root folder, and generate a new component via:

ng g component posts

You will see the following info.

create src\app\posts\posts.component.css
create src\app\posts\posts.component.html
create src\app\posts\posts.component.spec.ts
create src\app\posts\posts.component.ts 

By default, it will create a new folder under src\app folder, named posts. And the newly created component will be generated into the posts folder.

Have a look at the home of posts.component.ts file.

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-posts',
  templateUrl: './posts.component.html',
  styleUrls: ['./posts.component.css']
})
export class PostsComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

}

It declares a component named PostsComponent.

posts.component.css is the component specific CSS style file used for PostsComponent, posts.component.html is the template file for PostsComponent.

posts.component.spec.ts is the unit test file.

import { TestBed, async } from '@angular/core/testing';
import { PostsComponent } from './posts.component';

describe('Component: Posts', () => {
  it('should create an instance', () => {
    let component = new PostsComponent();
    expect(component).toBeTruthy();
  });
});

Common page layout

Generally, a page layout could be consist of header, sidebar, home body, and footer section, and only home section are replaced with different home in different views.

In this sample, we will create a simple navbar as header and footer components to demonstrate common page layout.

Modify the AppComponent template file app.component.html, which act as the template layout for all chidren components.

<app-navbar>
</app-navbar>
<h1>
  {{title}}
</h1>
<app-footer>
</app-footer>

I will try to use the same layout in my Angular 1.5 and ES6 sample.

In the project root folder, install bootstrap and Ng2Bootstrap(the Angular 2 compatible component and directives).

npm install ng2-bootstrap bootstrap@next --save 

You should includes the bootstrap css file. Angular CLI leaves some room to extend. Add the bootstrap css path in angular-cli.json.

//...
  "styles": [
	"styles.css",
	"../node_modules/bootstrap/dist/css/bootstrap.min.css"
  ],
//...	  

Follow official Angular 2 style guild, generate navbar and footer components in app/shared folder.

Enter app/shared folder, execute the following command.

ng g component navbar --flat

--flat tells ng command do not create a new folder for the created component.

It generates 4 files for navbar component, similar witht the former posts component.

Replace the template home of navbar.component.html file.

<nav class="navbar navbar-fixed-top navbar-light bg-faded">
  <div class="container">
    <a class="navbar-brand" [routerLink]="['/admin']" ><i class="fa fa-admin"></i>ANGULAR2 SAMPLE</a>
    <button class="navbar-toggler hidden-sm-up" type="button" data-toggle="collapse" data-target="#exCollapsingNavbar" aria-controls="exCollapsingNavbar2"
      aria-expanded="false" aria-label="Toggle navigation">
    &#9776;
  </button>

    <!-- Collect the nav links, forms, and other home for toggling -->

    <div class="collapse navbar-toggleable-xs" id="exCollapsingNavbar">
      <ul class="nav navbar-nav">
        <li class="nav-item" [routerLinkActive]="['active']"><a class="nav-link" [routerLink]="['/posts']"  >{{'posts'}}</a></li>
        <li class="nav-item" [routerLinkActive]="['active']"><a class="nav-link" [routerLink]="['/posts', 'new']"  >{{'new-post'}}</a></li>
        <li class="nav-item" [routerLinkActive]="['active']"><a class="nav-link" [routerLink]="['/about']"  >{{'about'}}</a></li>
      </ul>

      <!-- /.navbar-collapse -->
    </div>
  </div>
  <!-- /.container-fluid -->
</nav>

In this navbar template, it uses Bootstrap navbar component, and adds some links to different views. We will cover this links in Route section.

Similarly, generates footer component via:

ng g component footer --flat

Fill the following home in footer.component.html file.

<footer class="bg-faded" style="background-color: #e3f2fd;">
  <div class="container">
    <div class="row">
      <div class="col-md-6 ">
      </div>
      <div class="col-md-6">Copyright: HantsyLabs</div>
    </div>
  </div>
  <!-- /.container-fluid -->
</footer>

Since Angular 2.0 RC, a new router module is introduced for reorganizing the application logic structure. To use them, we have to import them in AppModule and declares them in declarations property of NgModule annotation.

import {NavbarComponent} from './shared/navbar.component';
import {FooterComponent} from './shared/footer.component';

@NgModule({
declarations:[NavbarComponent, FooterComponent]
})
export class AppModule{}

A more flexible approach is creating a SharedModule for these component fragments, directives, pipes, etc.

mport { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';

import { NavbarComponent } from './navbar.component';
import { FooterComponent } from './footer.component';


@NgModule({
  imports: [
    CommonModule,
    RouterModule
  ],
  declarations: [
    NavbarComponent,
    FooterComponent
    ],
  exports: [
    NavbarComponent,
    FooterComponent
  ],
})
export class SharedModule { }

And import this module in AppModule.

import { SharedModule } from './shared/shared.module';
//...

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
  //...
  SharedModule,
  //...
  }]
export class AppModule { }

When this application is reloaded, you will see the app component is decorated with navbar and footer.

Route

To navigate between pages, you have to define routing rules to make all page oriented components work.

Generates some samples components/modules for demonstration.

In the project root folder, generates a admin and an about modules.

ng g component admin
ng g module admin --routing

It will generate admin component files, and two an extra file: admin.module.ts and admin-routing.module.ts.

Declares AdminComponent in admin.module.ts.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AdminRoutingModule } from './admin-routing.module';
import { AdminComponent } from './admin.component';

@NgModule({
  imports: [
    CommonModule,
    AdminRoutingModule
  ],
  declarations: [AdminComponent]
})
export class AdminModule { }

In admin.module.ts file, it imports another module file, admin-routing.module.ts.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AdminComponent } from './admin.component';

const routes: Routes = [
  { path: 'admin', component: AdminComponent }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
  providers: []
})
export class AdminRoutingModule { }

The routes defines the routing rules for this module, and we also wrap this definition in to a standard Angular 2 NgModule.

Create another sample module, named about.

ng g component about
ng g module about --routing

The home of about.module.ts:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AboutRoutingModule } from './about-routing.module';
import { AboutComponent } from './about.component';

@NgModule({
  imports: [
    CommonModule,
    AboutRoutingModule
  ],
  declarations: [AboutComponent]
})
export class AboutModule { }

The home of about-routing.module.ts:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { AboutComponent } from './about.component';

const routes: Routes = [
  { path: 'about', component: AboutComponent }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
  providers: []
})
export class AboutRoutingModule { }

We have added admin and about link in navbar. Two directives are used: routerLink and routerLinkActive. routerLink will process the target route and routerLinkActive will toggle css class when the link is active or not.

Import these modules into app.module.ts.

import { AdminModule } from './admin/admin.module';
import { AboutModule} from './about/about.module';

@NgModule({
	//...
  imports: [
	//...
    AdminModule,
    AboutModule
  ],

Do not forget add route-outlet directive into app.component.html file.

<app-navbar></app-navbar>
<div class="page">
  <div class="container">
    <router-outlet></router-outlet>
  </div>
</div>
<app-footer></app-footer>

Add css to adjust the default layout, add the following into styles.css.

.page {
  margin-top: 80px;
  min-height: 600px;
}

Now run this application again, you could navigate between admin and about via links in navbar.

Loading module lazily

By default, as above settings, all modules will be loadded when application is started.

The new router supports loading modules lazily as possible.

Generates more componets in posts folder, such as new-post, post-details, edit-post.

ng g component new-post --flat
ng g component edit-post --flat
ng g component post-details --flat

Add these modules into posts.module.ts.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {FormsModule} from '@angular/forms';

import { PostsRoutingModule } from './posts-routing.module';
import { PostsComponent } from './posts.component';
import { NewPostComponent } from './new-post.component';
import { EditPostComponent } from './edit-post.component';
import { PostDetailsComponent } from './post-details.component';

@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    PostsRoutingModule
  ],
  declarations: [
    PostsComponent,
    NewPostComponent,
    EditPostComponent,
    PostDetailsComponent
  ]
})
export class PostsModule { }

And defines routing rules in posts-routing.module.ts.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { PostsComponent } from './posts.component';
import { NewPostComponent } from './new-post.component';
import { EditPostComponent } from './edit-post.component';
import { PostDetailsComponent } from './post-details.component';

const routes: Routes = [
  { path: '', redirectTo: 'admin' },
  { path: 'admin', component: PostsComponent },
  { path: 'new', component: NewPostComponent },
  { path: 'edit/:id', component: EditPostComponent },
  { path: 'view/:id', component: PostDetailsComponent },
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
  providers: []
})
export class PostsRoutingModule { }

In app-routing.module.ts file, add:

const routes: Routes = [
  { path: '', redirectTo: '/admin', pathMatch: 'full' }
  { path: 'posts', loadChildren: 'app/posts/posts.module#PostsModule' },
];

The first routing defines an empty path property, it means this is the default path of this application. It will redirectTo /admin(the path of AdminComponent).

The second routing rule uses the lazy way to load PostsModule when navigates /posts prefix urls. At runtime, it will append /posts as prefix to all urls defined in the posts-routing.module.ts file.

You can add another routing to process the notfound path, use ** as path value, put this definition at the last postion of the routes array.

{ path: '**', component:PageNotFoundComponent}

Now try to navigate posts and new-post links in navbar, and notice the network tracking in your Chrome developer tools.

Display post list

Open src\app\posts\posts.components.ts file, add posts data in ngOnInit method.

export class PostsComponent implements OnInit {
  posts: Post[];

  constructor() { }

  ngOnInit() {
    this.posts = [
      {
        id: 1,
        title: 'Getting started with REST',
        home: 'Home of Getting started with REST',
        createdAt: '9/22/16 4:15 PM'
      },
      {
        id: 2,
        title: 'Getting started with AngularJS 1.x',
        home: 'Home of Getting started with AngularJS 1.x',
        createdAt: '9/22/16 4:15 PM'
      },
      {
        id: 3,
        title: 'Getting started with Angular2',
        home: 'Home of Getting started with Angular2',
        createdAt: '9/22/16 4:15 PM'
      },
    ];
  }
}

Creat a new file src\app\posts\post.model.ts, it is a model class wrap the Post fields.

export interface Post {
  id?: number;
  title: string;
  home: string;
  createdAt?: string;
  createdBy?: string;
}

Add import in src\app\posts\posts.component.ts.

import {Post} from './post.model';

Open src\app\posts\posts.components.html.

<div class="card">
  <div class="card-block bg-faded">
    <div class="row">
      <div class="col-md-9">
        <form class="form-inline" (ngSubmit)="search()">
          <div class="form-group">
            <input type="text" name="q" class="form-control" [(ngModel)]="q" />
          </div>
          <button type="submit" class="btn btn-outline-info">{{'search'}}</button>
        </form>
      </div>
      <div class="col-md-3">
        <span class="pull-md-right">
          <a class="btn btn-success" [routerLink]="['/posts', 'new']">new-post</a>
        </span>
      </div>
    </div>
  </div>
</div>

<div class="row">
  <div class="col-md-6" *ngFor="let post of posts ">
    <div class="card card-block">
      <h4 class="card-title">{{post.title}}</h4>
      <h6 class="card-subtitle text-muted">{{post.createdAt|date:'short'}}</h6>
      <p class="card-text">{{post.home}}</p>
      <div class="text-md-right">
        <a [routerLink]="['/posts', 'edit', post.id]">edit</a>
        <a [routerLink]="['/posts', 'view', post.id]">view</a>
      </div>
    </div>
  </div>
</div>

Save all files, when application is reloaded, navigate to http://localhost:4200/posts, you should see the following screen.

Posts works

Try to click the edit and view links in each post card. It should route to the right component view.

Component lifecycle hooks

In PostsComponent, there is a ngOnInit method which is from OnInit interface and it will be invoked when the component is initialized.

Inversely, when the component is being destroyed, there is a hook named OnDestroy which has a method ngOnDestroy which will be called.

Besides these generic lifecycle hooks, there are some other specific hooks available in Angular 2 for component lifecycle. Please read the official lifecycle hooks.

Summary

In this post, we created some components, and create a common layout for the chidren components. Also experienced the new Angular Router and used it to navigate between master view(PostsComponent) and details view(PostDetailsComponent).

转载于:https://my.oschina.net/hantsy/blog/857099

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值