angular使用NG ZORRO來構(gòu)建博客展示項目(簡單實現(xiàn)展示頁面)

返回目錄

使用 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ù)組件租幕。

這兩個并沒有設(shè)計成core模塊的組件,但是也放在這里拧簸,不知道放在哪里合適劲绪。有的可以不用設(shè)計成angular模塊或者組件,初學(xué)者真煩惱

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;
}

blog.service.ts,這個寫的是組件牺六,并在模塊中聲明了
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]);
  }

}


image.png

博客文章顯示

布局 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);
    }
  }

}

image.png

先簡單實現(xiàn)读整,后面再慢慢優(yōu)化吧

簡單實現(xiàn)

添加點動畫效果

定義兩組動畫:入場浮動動畫,點擊喜歡時的動畫效果

在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)化

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末屈糊,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子琼了,更是在濱河造成了極大的恐慌逻锐,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雕薪,死亡現(xiàn)場離奇詭異昧诱,居然都是意外死亡,警方通過查閱死者的電腦和手機所袁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門盏档,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人燥爷,你說我怎么就攤上這事蜈亩∨尘剑” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵稚配,是天一觀的道長畅涂。 經(jīng)常有香客問我,道長道川,這世上最難降的妖魔是什么午衰? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮愤惰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赘理。我一直安慰自己宦言,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布商模。 她就那樣靜靜地躺著奠旺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪施流。 梳的紋絲不亂的頭發(fā)上响疚,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機與錄音瞪醋,去河邊找鬼忿晕。 笑死,一個胖子當(dāng)著我的面吹牛银受,可吹牛的內(nèi)容都是我干的践盼。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼宾巍,長吁一口氣:“原來是場噩夢啊……” “哼咕幻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起顶霞,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤肄程,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后选浑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蓝厌,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年古徒,在試婚紗的時候發(fā)現(xiàn)自己被綠了褂始。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡描函,死狀恐怖崎苗,靈堂內(nèi)的尸體忽然破棺而出狐粱,到底是詐尸還是另有隱情,我是刑警寧澤胆数,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布肌蜻,位于F島的核電站,受9級特大地震影響必尼,放射性物質(zhì)發(fā)生泄漏蒋搜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一判莉、第九天 我趴在偏房一處隱蔽的房頂上張望豆挽。 院中可真熱鬧,春花似錦券盅、人聲如沸帮哈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽娘侍。三九已至,卻和暖如春泳炉,著一層夾襖步出監(jiān)牢的瞬間憾筏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工花鹅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留氧腰,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓刨肃,卻偏偏與公主長得像容贝,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子之景,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容