使用 NG ZORRO
编辑layout模块中的header组件
在layout.module.ts中引入NG ZORRO
import { NgZorroAntdModule } from 'ng-zorro-antd'; imports: [ CommonModule,RouterModule,NgZorroAntdModule ],
编辑header.component.html简单布局
<div class="header"> <div class="logo"> <img src="../../../assets/img/logo.png"/> </div> <div class="top-menu"> <ul nz-menu [nzMode]="'horizontal'" style="line-height: 64px;"> <li nz-menu-item><i class="anticon anticon-home"></i> 主页</li> <li nz-menu-item routerLink="blog"><i class="anticon anticon-appstore"></i> 博客</li> <li nz-submenu> <span title><i class="anticon anticon-setting"></i> 秘密</span> <ul> <li nz-menu-item>秘密1 </li> <li nz-menu-item>秘密2 </li> </ul> </li> <li nz-menu-item><i class="anticon anticon-user"></i>神马</li> <li nz-menu-item><i class="anticon anticon-mail"></i>约</li> </ul> </div> </div>
在header.component.css中简单调整下样式
.logo { width: 120px; height: 66px; margin-left: 50px; float: left; } .logo img{ height: 100%; width: auto; } .top-menu{ float: right; margin-right: 50px; } .header{ height: 66px; border-bottom: 1px solid #e9e9e9; }
看看效果展开二级菜单的时候报错了,说我们要包含"BrowserAnimationsModule" 或者"NoopAnimationsModule"模块
在app.module.ts中引用
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; imports: [ RouterModule,BrowserModule,NgZorroAntdModule.forRoot(),RoutesModule,BrowserAnimationsModule ],
简单编辑下footer组件
<div class="footer"> 易兒善©2017 </div>
.footer{ background-color: darkgray; padding: 20px 50px; width: 100%; text-align: center; }
创建服务
要和后台交互,我们就需要有http请求,需要用到angular的http模块。从angular2到现在的angular5http模块也有些变化。
我是这样设计的,把api请求封装成一个基类,然后在此基础上封装一个针对后台apb框架的基类,最后才是我们应用所需要的api请求数据组件。
api-base-service.ts
import { HttpClient,HttpHeaders,HttpParams } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/do'; import 'rxjs/add/operator/catch'; import * as moment from 'moment'; import { environment } from '../../../environments/environment'; /** * 封装HttpClient,主要解决: * + 优化HttpClient在参数上便利性 * + 统一实现 loading * + 统一处理时间格式问题 */ export abstract class ApiBaseService { constructor(protected http: HttpClient) { } private _loading = false; /** 是否正在加载中 */ get loading(): boolean { return this._loading; } parseParams(params: any): HttpParams { let ret = new HttpParams(); if (params) { // tslint:disable-next-line:forin for (const key in params) { let _data = params[key]; // 将时间转化为:时间戳 (秒) if (moment.isDate(_data)) { _data = moment(_data).unix(); } ret = ret.set(key,_data); } } return ret; } private begin() { console.time('http'); this._loading = true; } private end() { console.timeEnd(); this._loading = false; } /** 服务端URL地址 */ get SERVER_URL(): string { return environment.SERVER_URL; } /** * GET请求 * * @param {string} url URL地址 * @param {*} [params] 请求参数 */ get(url: string,params?: any): Observable<any> { this.begin(); return this.http .get(url,{ params: this.parseParams(params) }) .do(() => this.end()) .catch((res) => { this.end(); return res; }); } /** * POST请求 * * @param {string} url URL地址 * @param {*} [body] body内容 * @param {*} [params] 请求参数 */ post(url: string,body?: any,params?: any): Observable<any> { this.begin(); return this.http .post(url,body || null,{ params: this.parseParams(params) }) .do(() => this.end()) .catch((res) => { this.end(); return res; }); }
abp-api-service.ts
import {ApiBaseService} from "./api-base-service" import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; /** * 进一步封装HttpClient,主要解决: * 后台apb框架返回数据的解析 */ export abstract class AbpApiService extends ApiBaseService { constructor(protected http: HttpClient) { super(http); } abpGet<T>(url: string,params ? : any): Observable<any> { return this.get(url,params).map(r=>{ return this.process<T>(r); }); } abpPost<T>(url: string,params?: any): Observable<any> { return this.post(url,body,params).map(r=>{ return this.process<T>(r); }) } private process<T>(r:any):any{ const data = r as Result; if(data.success){ return data.result as T; }else { console.error(data.error); throw data.error; } } } // 后台返回的结构体 export class Result{ success:boolean; error:any; result:any; }
import { Injectable } from '@angular/core'; import {HttpClient} from '@angular/common/http'; import {Observable} from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import 'rxjs/add/observable/of'; import {AbpApiService} from "../../core/services/abp-api-service" import {environment} from "../../../environments/environment" const blogApiUrl ={ getNoteList :environment.SERVER_URL+"/api/services/app/NoteServer/GetPreNoteList",getNote:environment.SERVER_URL+"/api/services/app/NoteServer/GetNote",like:environment.SERVER_URL+"/api/services/app/NoteServer/Like" }; // 要使该服务可以依赖注入,需要加上下面这个标签,并且在模块中声明 @Injectable() export class BlogService extends AbpApiService{ constructor(protected http: HttpClient) { super(http) } public GetNoteList(params:GetNoteDto):Observable<PagedData<PreNoteDto>> { const url = blogApiUrl.getNoteList; return this.abpGet<PagedData<PreNoteDto>>(url,params); } public GetNote(id:number):Observable<PreNoteDto>{ const url = blogApiUrl.getNoteList+"?Id="+id; return this.abpGet<PreNoteDto>(url); } public Like(id:number):void{ const url = blogApiUrl.getNoteList; this.abpPost(url,null,{id:id}) } } export class GetNoteDto{ SkipCount = 0; MaxResultCount = 10; key = ''; } export class PreNoteDto{ id:number; title:string; creationTime:string; like:number; collect:number; scan:number; isPublic:boolean; content:string; } // 分页数据类 export class PagedData<T>{ items:T[]; totalCount:number; }
blog.module.ts中声明
import {BlogService} from "./blog.service"; providers: [ BlogService ],
博客模块列表组件
我打算这样实现列表,上面一个大的搜索框,下面就是列表,不用分页,使用加载更多的方式。
注意这个子模块我们要使用NG ZORRO,所以还是要在子模块中引入。后面这些和样式调整就不再写详细的内容了
布局note-list.component.html
<div class="content"> <div class="serch-content"> <nz-input [nzType]="'search'" [(ngModel)]="key" [nzPlaceHolder]="'输入你想知道的'" style="height: 38px;"></nz-input> </div> <div> <div *ngFor="let note of preNoteList" class="note-list"> <div class="note-title"> <h1> <a (click)="linkTo(note.id)">{{note.title}}</a> </h1> <em>{{note.creationTime}}</em> </div> <article [innerHTML]="note.content"></article> <div class="note-btn"> <div> <i class="anticon anticon-eye"></i>{{note.scan}} <i class="anticon anticon-heart"></i> {{note.like}} </div> </div> </div> </div> <div *ngIf="loadMore" class="load-more" (click)="getNoteList()" > <span>点击加载更多</span><i class="anticon anticon-arrow-down"></i> </div> <div *ngIf="loading" class="load-more"> <nz-spin></nz-spin> </div> </div>
note-list.component.ts
import { Component,OnInit } from '@angular/core'; import { Router } from '@angular/router'; import marked from 'marked'; import {BlogService,PreNoteDto,GetNoteDto} from "../blog.service" @Component({ selector: 'app-note-list',templateUrl: './note-list.component.html',styleUrls: ['./note-list.component.css'] }) export class NoteListComponent implements OnInit { preNoteList:PreNoteDto[]=[]; loadMore = false; loading =false; key=""; constructor(private router: Router,private blogService :BlogService ) { } ngOnInit() { this.getNoteList(); } getNoteList(f=false){ this.loading= true; if(f)this.preNoteList =[]; const param = new GetNoteDto(); param.key = this.key; param.SkipCount = this.preNoteList.length; this.blogService.GetNoteList(param).do(()=>{ this.loading = false; }).subscribe(m=> { this.loadMore = m.totalCount>this.preNoteList.length; m.items.forEach((v,i)=>{ v.content = marked(v.content); this.preNoteList.push(v); }); }); } linkTo(id:number){ this.router.navigate(['blog/note',id]); } }
博客文章显示
布局 note.component.html
<div class="content"> <div class="note-title"> <h1> {{note.title}} </h1> <div class="note-btn"> <div> <em>{{note.creationTime}}</em> <i class="anticon anticon-eye"></i>{{note.scan}} <i class="anticon anticon-heart"></i> {{note.like}} </div> </div> </div> <article [innerHTML]="note.content"></article> <div *ngIf="loading" class="load"> <nz-spin></nz-spin> </div> <div style="margin: auto;padding: 50px 10px;"> <div class="like" [ngClass]="{'liked':_like}" (click)="ILike()"> <i class="anticon anticon-heart-o"></i>喜欢 | {{note.like}} </div> </div> </div>
import { Component,OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; // 路由 import {BlogService,PreNoteDto} from "../blog.service" import marked from 'marked'; @Component({ selector: 'app-note',templateUrl: './note.component.html',styleUrls: ['./note.component.css'] }) export class NoteComponent implements OnInit { _like=false; note= new PreNoteDto(); loading=true; constructor(private route: ActivatedRoute,private server:BlogService ) { } ngOnInit() { // 获取路由传值 this.route.params.subscribe((params) => { const id = params.id; this.server.GetNote(id).subscribe(r=>{ r.content = marked(r.content); this.note = r; },r=>{ console.error(r); },()=>{ this.loading= false; }) }); } ILike(){ this._like = !this._like; if(this._like){ this.note.like++; this.server.Like(this.note.id); }else { this.note.like--; this.server.UnLike(this.note.id); } } }
先简单实现,后面再慢慢优化吧