angular8案例-網易雲音樂

技術棧

angular8+ngrx8+ng-zorro+material/odk

ng模塊化設計/proxy,http攔截器/依賴注入/自定義指令和管道、自定義表單控件/動態組件/rxjs操作符/變更檢測策略/ngrx8

api:

創建項目

依賴 步驟 報錯/效果
ng-zorro

npm install rxjs@^6.5.2 

只出現fsevents的WARN可忽略
npm install ng-zorro-antd --save //安裝到本地

8.3.1版本需要依賴rxjs@^6.5.2

ng add ng-zorro-antd //項目中添加

? Add icon assets [ Detail: https://ng.ant.design/components/icon/en ] Yes
? Set up custom theme file [ Detail: https://ng.ant.design/docs/customize-theme/en ] Yes
? Choose your locale code: zh_CN
? Choose template to create project: blank

found 1 high severity vulnerability
  run `npm audit fix` to fix them, or `npm audit` for details可忽略

ng serve --open //重啓項目

頁面變成ng-zorro則爲成功
<button nz-button nzType="primary">button</button> 使用ng-zorro
minireset

npm install minireset  //安裝minireset

出現fseven警告可hulue

@import '~minireset.css'; //樣式文件中引用

使用minireset

頁面結構、模塊化設計

 

文件類型 步驟 解釋
style文件

style樣式文件位置:

1.手動剪切style.css黏貼到assets/styles

2.angular.json上改樣式的配置,包括"projects"下“styles”和"test"下“styles”路徑都要改爲src/assets/styles/index.css

3.ctrl+c退出再重新npm server --open

 
module

創建module

ng g module core

 
route

ng g module app-routing --flat --module=app 

--flat 把這個文件放進了 src/app 中,而不是單獨的目錄中。 
--module=app 告訴 CLI 把它註冊到 AppModule 的 imports 數組中。

 

輪播圖數據+推薦歌單數據+入駐歌手數據

知識點:自定義pipe,get請求+傳參,resolve守衛

步驟
獲取數據 接口 services.module.ts common.type.ts

下載

clone API的庫

安裝依賴

npm install

運行

node ./app.js

export const API_CONFIG=new InjectionToken('ApiConfigToken')

@NgModule({

providers:[

 {provide:API_CONFIG,useValue

:'http://localhost:3000/'}

]

})

創建

services/data-types/common.type.ts

數據原形

export type Banner={

  targetId:number;

  url:string;

  imageUrl:string

}

export type Singer={

  id:number;

  name:string;

  picUrl:string;

  albumSize:number;

}

export type SongSheet={

  id:number;

  name:string;

  picUrl:string;

  playCount:number;

 /*tracks:Song[]*/   

}

服務home.service.ts

服務singer.service.ts

創建服務

ng g s services/home

引入

import { Banners } from './data-type/comment.type';

import { ServicesModule, API_CONFIG } from './services.module';

@Injectable({

  providedIn: ServicesModule

})

http請求

export class HomeService {

  constructor(private http:HttpClient,@Inject(API_CONFIG) private uri:string) {}

  goBanners():Observable<Banners[]>{

    return this.http.get(this.uri+'banner')

    .pipe(map((res:{banners:Banners[]})=> res.banners))

  }

  getPersonalSheetList():Observable<SongSheet[]>{

    return this.http.get(this.uri+'personalized')

    .pipe(map((res:{result:SongSheet[]})=> res.result.slice(0,16)))

  }

}

創建服務

ng g s services/singer

數據

 type SingerParams={

  offset:number;

  limit:number;

  cat?:string

}

const defaultParams:SingerParams={

  offset:0,

  limit:9,

  cat:'5001'

}

get請求加傳參

getEnterSinger(args:SingerParams=defaultParams)

:Observable<Singer[]{

const params=new HttpParams({

fromString: queryString.stringify(args) });

return this.http.get(this.uri+'artist/list',{ params })

    .pipe(map((res:{artists:Singer[]})=> res.artists))

守衛home-resolve.service.ts

創建守衛

home目錄下創建home-resolve.service.ts

引入HomeService

import { HomeService } from 'src/app/services/home.service';

import {Banners} from '../../services/data-type/comment.type'

@Injectable()

數據類型

type HomeDataType = [Banners[],HotTag[],SongSheet[],Singer[]];

@Injectable()

繼承守衛

export class HomeResolveServices implements Resolve<HomeDataType>{

 引入服務,調用方法

constructor(private homeServe:HomeService,private singerService:SingerService){}

  resolve():Observable<HomeDataType>{

    return forkJoin([  //線程池

      this.homeServe.goBanners(),

      this.homeServe.goHotTag(),

      this.homeServe.getPersonalSheetList(),

       this.singerService.getEnterSinger()

    ]).pipe(first())

  }

}

路由home-routing.module.ts 組件中home.component.ts

引入守衛,傳入內容

Routers:[

{path:"home",component:HomeComponent,data:{title:"首頁"},resolve:{homeData:HomeResolveServices}}

]

private banners:any;

獲取數據

constructor(private route:ActivatedRoute) {

    this.route.data.pipe(map(res => res.homeData)).subscribe

(([banners,hotTags,songSheetlist,singers]) =>{

      this.banners=banners;

      this.hotTag=hotTags;

      this.SongSheet=songSheetlist;

      this.Singers=singers;

    });

 輪播圖

輪播組件 home.component.html父 carousel.component.html子

<div class="home">

<app-carousel #myCarousel [activeIndex]="beforeChangeN" //1.6父傳子(changeSlide)="onChangeSlide($event)"> //2.3父方法傳給子

  <nz-carousel

   nzAutoPlay

   nzEffect="fade" 

  [nzDotRender]="myCarousel.dotRet"  //1.3渲染模板並傳出參數number

  (nzBeforeChange)="onBeforeChange($event)"> //1.4獲取輪播回調的數據

    <div nz-carousel-content *ngFor="let item of banners" class="carousel" >

     <a [href]="item.url">

       <img [src]="item.imageUrl"/>

     </a>

    </div>

  </nz-carousel>

</app-carousel>

</div>

創建

ng g c pages/home/components/carousel

內容

<div class="carousel">

<div class="wrap">

  <i nz-icon

   nzType="left" 

  nzTheme="outline"

   class="arrow left"></i>

  <ng-content></ng-content>

  <ng-template #dot let-number> //1.1模板 傳入參數

  <i class="dot" [class.active]="activeIndex===number" (click)="onChangeSlideData('pre')"></i> //2.5觸發子方法

  </ng-template>

  <i nz-icon nzType="right" nzTheme="outline" class="arrow right"

(click)="onChangeSlideData('next')"></i>

  </div>

</div>

home.component.ts carousel.component.ts

beforeChangeN:any;

@ViewChild(NzCarouselComponent,{static:true}) private nzCarousel:NzCarouselComponent; //2.1獲取輪播組件

onBeforeChange({to}){  //1.5父獲取輪播回調的數據

    this.beforeChangeN=to;

  }

onChangeSlide(type:string){ //2.2定義父方法,觸發輪播方法

    this.nzCarousel[type]();

  }

//1.2獲取模板

@ViewChild('dot',{static:true}) dotRet:TemplateRef<any>;

@Input() activeIndex=0; //1.7子接收父傳來的回調

//2.4子觸發父方法並傳值

 @Output() changeSlide=new EventEmitter<string>(); //

onChangeSlideData(type:string){ 

    this.changeSlide.emit(type);

  }

原點替換 home.component.html父 carousel.component.html子

<app-carousel #myCarousel>

 <nz-carousel [nzDotRender]="myCarousel.dotRet" > //1.3渲染模板並傳出參數number

 </nz-carousel>

</app-carousel>

<ng-template #dot let-number> //1.1模板 傳入number參數

 </ng-template>

home.component.ts carousel.component.ts
X

//1.2獲取模板

@ViewChild('dot',{static:true}) dotRet:TemplateRef<any>;

[nzDotRender]是Dot渲染模板,類型爲TemplateRef

原點觸發

home.component.html父 carousel.component.html子

 <app-carousel #myCarousel [activeIndex]="beforeChangeN" >//1.6父將數據傳給子

<nz-carousel (nzBeforeChange)="onBeforeChange($event)"> //1.4輪播組件回調的數據傳給home組件

</nz-carousel>

</app-carousel>

//子使用數據

<i class="dot" [class.active]="activeIndex===number" ></i>

home.component.ts carousel.component.ts

beforeChangeN:any;

onBeforeChange({to}){  //1.5home組件接收輪播回調的數據

    this.beforeChangeN=to;

  }

@Input() activeIndex=0; //1.7子接收父傳來的回調
(nzBeforeChange) 是輪播組件切換面板的回調函數,可以傳出輪播數據EventEmitter<{ from: number; to: number }>
箭頭觸發 home.component.html父 carousel.component.html子

<app-carousel (changeSlide)="onChangeSlide($event)"> //2.3父方法傳給子

</app-carousel>

<i  (click)="onChangeSlideData('pre')"></i> //2.5觸發子方法

<i (click)="onChangeSlideData('next')"></i>

home.component.ts carousel.component.ts

@ViewChild(NzCarouselComponent,{static:true}) private nzCarousel:NzCarouselComponent; //2.1獲取輪播組件

onChangeSlide(type:string){ //2.2定義父方法,觸發輪播方法

    this.nzCarousel[type]();

  }

@component({

ntg

}) //觸發時進行變更檢測

/2.4子觸發父方法並傳值

 @Output() changeSlide=new EventEmitter<string>(); 

onChangeSlideData(type:string){ 

    this.changeSlide.emit(type);

  }

 

NzCarouselComponent中的next()和pre()方法用作切換面板

 

 

播放器

 

推薦歌單點擊播放 home.component.html父 singer-sheet.component.html子

3.父方法傳給子

<app-singer-sheet

*ngFor="let item of SongSheet"

[sheet]="item"

(onPlay)="onPlaySheet($event)"

></app-singer-sheet>

1.觸發子方法(傳入歌單id)

<i class="icon play" (click)="playSheet(sheet.id)"> 

home.component,ts父 singer-sheet.component.ts子

4觸發getSongSheetDetail方法

constructor(private sheetServe:SheetServices)

onPlaySheet(id:number){

this.sheetServe.getSongSheetDetail(id).subscribe(res =>{console.log(res)})

}

2.子向父傳參

@output onPlay=new EventEmitter<number>();

playSheet(id:number){

  this.onPlay.emit(id);  

}

  SheetServices服務 SongServices服務

6.獲取歌單列表詳情(get+HttpParams傳參)

 getSongSheetDatail(id:number): Observable<SongSheet>{

    const params=new HttpParams().set('id',id.toString());

    return this.http.get(this.uri+'playlist/detail',{ params })

    .pipe(map((res:{playlist:SongSheet})=> res.playlist))

  }

5.

觸發getSongSheetDetail

篩選出song[]並賦值給getSongList

playSheet(id: number): Observable<Song[]> {

    return this.getSongSheetDetail(id)

    .pipe(pluck('tracks'), switchMap(tracks => 

this.SongServer.getSongList(tracks)));

  }

8.獲取音樂URL

getSongUrl(ids: string): Observable<SongUrl[]> {

    const params = new HttpParams().set('id', ids);

    return this.http.get(this.uri + 'song/url', { params })

    .pipe(map((res: { data: SongUrl[] }) => res.data));

  }

7.找出所有id對應的Url並把song[]和url數組傳給拼接函數

getSongList(songs: Song | Song[]): Observable<Song[]> {

    const songArr = Array.isArray(songs) ? songs.slice() : [songs];

    const ids = songArr.map(item => item.id).join(',');

    return this.getSongUrl(ids).pipe(map(urls => this.generateSongList(songArr, urls)));

  }

9.拼接函數:

 private generateSongList(songs: Song[], urls: SongUrl[]): Song[] {

    const result = [];

    songs.forEach(song => {

      const url = urls.find(songUrl => songUrl.id === song.id).url;

      if (url) {

        result.push({ ...song, url });

      }

    });

    return result;

  }

數據類型 解釋

export type SongSheet={

  id:number; //歌單id

  name:string;

  picUrl:string;

  playCount:number;

  tracks:Song[]

}

export type Song={

id:number; //歌曲id

name:string;

url:string;

ar:Singer[];

al:{id:number,name:string;picUrl:string};

dt:number

}

export type SongUrl={

  id:number;

  url:string

}

首先獲取指定id的歌單詳情getSongSheetDetail

篩選出歌單內所有歌曲信息trucks:song[]

 

 

 

     
     
     
     

播放面板

 

搜索組件

 

登錄

 

個人中心

 

 

收藏功能

 

分享功能

 

註冊功能

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章