使用 NG ZORRO
在上一篇文章中腾降,我們已經(jīng)安裝了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中簡單調(diào)整下樣式
.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;
}
創(chuàng)建服務(wù)
要和后臺交互靡挥,我們就需要有http請求,需要用到angular的http模塊鸯绿。從angular2到現(xiàn)在的angular5http模塊也有些變化跋破。
我是這樣設(shè)計的簸淀,把api請求封裝成一個基類,然后在此基礎(chǔ)上封裝一個針對后臺apb框架的基類毒返,最后才是我們應(yīng)用所需要的api請求數(shù)據(jù)組件租幕。
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盆赤,主要解決:
* + 優(yōu)化HttpClient在參數(shù)上便利性
* + 統(tǒng)一實現(xiàn) loading
* + 統(tǒng)一處理時間格式問題
*/
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];
// 將時間轉(zhuǎn)化為:時間戳 (秒)
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;
}
/** 服務(wù)端URL地址 */
get SERVER_URL(): string {
return environment.SERVER_URL;
}
/**
* GET請求
*
* @param {string} url URL地址
* @param {*} [params] 請求參數(shù)
*/
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內(nèi)容
* @param {*} [params] 請求參數(shù)
*/
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框架返回數(shù)據(jù)的解析
*/
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, body?: any, 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;
}
}
}
// 后臺返回的結(jié)構(gòu)體
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"
};
// 要使該服務(wù)可以依賴注入颤枪,需要加上下面這個標(biāo)簽,并且在模塊中聲明
@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;
}
// 分頁數(shù)據(jù)類
export class PagedData<T>{
items:T[];
totalCount:number;
}
blog.module.ts中聲明
import {BlogService} from "./blog.service";
providers: [ BlogService ],
博客模塊列表組件
我打算這樣實現(xiàn)列表淑际,上面一個大的搜索框畏纲,下面就是列表,不用分頁春缕,使用加載更多的方式霍骄。
注意這個子模塊我們要使用NG ZORRO,所以還是要在子模塊中引入淡溯。后面這些和樣式調(diào)整就不再寫詳細(xì)的內(nèi)容了
布局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);
}
}
}
先簡單實現(xiàn)读整,后面再慢慢優(yōu)化吧
添加點動畫效果
定義兩組動畫:入場浮動動畫,點擊喜歡時的動畫效果
在share文件夾下添加一個動畫效果文件animations.ts咱娶。
import {trigger, animate, style, group, animateChild, query, stagger, transition} from '@angular/animations';
//入場浮動效果
export const Float = trigger('Float', [
transition(':enter', [
style({ opacity: 0.5,transform: 'translate3d(-10px,10px,0)'}),
animate(500)
])
]);
export const Bubble = trigger('Bubble', [
transition('*=>Bubble', [
animate(500, style({ opacity: 0,transform: 'scale(1.5)'}))
]),
transition('*=>UnBubble', [
animate(500, style({ opacity: 0,transform: 'scale(0.5)'}))
])
]);
在note-list使用中使用
html
<div *ngFor="let note of preNoteList" class="note-list" [@Float]="">
ts
import {Float} from "../../../share/animations"
@Component({
selector: 'app-note-list',
templateUrl: './note-list.component.html',
styleUrls: ['./note-list.component.css'],
animations: [ Float ]
})
在note中使用
html
<div class="like" [ngClass]="{'liked':_like}" (click)="ILike()" [@Bubble]="State">
ts
import {Float,Bubble} from "../../../share/animations"
@Component({
selector: 'app-note',
templateUrl: './note.component.html',
styleUrls: ['./note.component.css'],
animations: [ Float,Bubble ]
})
State="";
ILike(){
this._like = !this._like;
if(this._like){
this.note.like++;
this.State="Bubble";
this.server.Like(this.note.id);
}else {
this.note.like--;
this.State="UnBubble";
this.server.UnLike(this.note.id);
}
}
有動畫使用相關(guān)疑惑的可以參考我的這篇文章及其相關(guān)文章:Angular練習(xí)之a(chǎn)nimations動畫
思考
angular模塊米间,組件,普通的ts文件之間的關(guān)系和區(qū)別膘侮。
動態(tài)路由是如何傳值的
頁面樣式和布局如何優(yōu)化