使用 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中简单调整下样式
看看效果展开二级菜单的时候报错了,说我们要包含"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);
- }
- }
- }
先简单实现,后面再慢慢优化吧