angular使用NG ZORRO来构建博客展示项目(简单实现展示页面)

返回目录

使用 NG ZORRO

在上一篇文章中,我们已经安装了NG ZORRO,并在跟模块中引入了,在子模块中使用还需要再次引入。

编辑layout模块中的header组件

在layout.module.ts中引入NG ZORRO

import { NgZorroAntdModule } from 'ng-zorro-antd';

  imports: [
    CommonModule,
    RouterModule,
    NgZorroAntdModule
  ],

编辑header.component.html简单布局

  • 主页
  • 博客
  • 秘密
    • 秘密1
    • 秘密2
  • 神马

在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"模块

angular使用NG ZORRO来构建博客展示项目(简单实现展示页面)_第1张图片
展开二级菜单的时候报错了

在app.module.ts中引用

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

  imports: [
    RouterModule,
    BrowserModule,
    NgZorroAntdModule.forRoot(),
    RoutesModule,
    BrowserAnimationsModule
  ],
angular使用NG ZORRO来构建博客展示项目(简单实现展示页面)_第2张图片
这次好了

简单编辑下footer组件


.footer{
  background-color: darkgray;
  padding: 20px 50px;
  width: 100%;
  text-align: center;
}

创建服务

要和后台交互,我们就需要有http请求,需要用到angular的http模块。从angular2到现在的angular5http模块也有些变化。
我是这样设计的,把api请求封装成一个基类,然后在此基础上封装一个针对后台apb框架的基类,最后才是我们应用所需要的api请求数据组件。

angular使用NG ZORRO来构建博客展示项目(简单实现展示页面)_第3张图片
这两个并没有设计成core模块的组件,但是也放在这里,不知道放在哪里合适。有的可以不用设计成angular模块或者组件,初学者真烦恼

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 {
    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 {
    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(url: string, params ? : any): Observable {
    return this.get(url,params).map(r=>{
      return this.process(r);
    });
  }
  abpPost(url: string, body?: any, params?: any): Observable {
    return this.post(url,body,params).map(r=>{
      return this.process(r);
    })
  }
  private process(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;
}

angular使用NG ZORRO来构建博客展示项目(简单实现展示页面)_第4张图片
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"
};
// 要使该服务可以依赖注入,需要加上下面这个标签,并且在模块中声明
@Injectable()
export class BlogService extends AbpApiService{

  constructor(protected http: HttpClient) {
    super(http)
  }

  public GetNoteList(params:GetNoteDto):Observable> {
    const url = blogApiUrl.getNoteList;
    return this.abpGet>(url,params);
  }

  public GetNote(id:number):Observable{
    const url = blogApiUrl.getNoteList+"?Id="+id;
    return this.abpGet(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{
  items:T[];
  totalCount:number;
}

blog.module.ts中声明

import {BlogService} from "./blog.service";


  providers:    [ BlogService ],

博客模块列表组件

我打算这样实现列表,上面一个大的搜索框,下面就是列表,不用分页,使用加载更多的方式。
注意这个子模块我们要使用NG ZORRO,所以还是要在子模块中引入。后面这些和样式调整就不再写详细的内容了

布局note-list.component.html

{{note.title}}

{{note.creationTime}}
{{note.scan}} {{note.like}}
点击加载更多

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

}


angular使用NG ZORRO来构建博客展示项目(简单实现展示页面)_第5张图片
image.png

博客文章显示

布局 note.component.html

{{note.title}}

{{note.creationTime}} {{note.scan}} {{note.like}}
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);
    }
  }

}

angular使用NG ZORRO来构建博客展示项目(简单实现展示页面)_第6张图片
image.png

先简单实现,后面再慢慢优化吧

简单实现

添加点动画效果

定义两组动画:入场浮动动画,点击喜欢时的动画效果

在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

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

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);
    }
  }
加入动画效果

有动画使用相关疑惑的可以参考我的这篇文章及其相关文章:Angular练习之animations动画

源码下载

思考

angular模块,组件,普通的ts文件之间的关系和区别。
动态路由是如何传值的
页面样式和布局如何优化

你可能感兴趣的:(angular使用NG ZORRO来构建博客展示项目(简单实现展示页面))