轮播图

服务封装

  • 出现端口被占用,打开CMD窗口,输入netstat -ano

  • 然后找到相应端口对应的PID,打开任务管理员,点击详细信息关闭即可

1
ng g s services/home
  • 修改挂载root改为ServicesModule
1
2
3
4
5
6
7
8
9
10
import { Injectable } from '@angular/core';
import { ServicesModule } from './services.module';

@Injectable({
providedIn: ServicesModule
})
export class HomeService {

constructor() { }
}
  • 将端口封装在services.module.ts
1
2
3
4
5
6
7
8
9
10
11
import { InjectionToken, NgModule } from "@angular/core";
// 暴露出去
export const API_CONFIG = new InjectionToken("ApiConfigToken");

// HTTP服务模块
@NgModule({
declarations: [],
imports: [],
providers: [{ provide: API_CONFIG, useValue: "http://localhost:3000/" }],
})
export class ServicesModule {}
  • services文件夹下创建data-types文件夹,再创建common.types.ts文件,用于定义返回的数据类型
1
2
3
4
5
6
// 定义Banner的数据类型
export type Banner = {
targetId : number;
url : string;
imageUrl : string;
}
  • home.service.ts具体内容如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { HttpClient } from "@angular/common/http";
import { Inject, Injectable } from "@angular/core";
import { Observable } from "rxjs";
import { Banner } from "./data-types/common.types";
import { API_CONFIG, ServicesModule } from "./services.module";
import { map } from "rxjs/internal/operators";
@Injectable({
providedIn: ServicesModule,
})
export class HomeService {
constructor(private http: HttpClient,@Inject(API_CONFIG) private uri:string) {}

// 获取轮播图
getBanners(): Observable<Banner[]> {
return this.http
.get(this.uri+"banner")
.pipe(map((res: { banners: Banner[] }) => res.banners));
}
}

获取数据

  • 服务封装好后,在home.component.ts组件中依赖注入并使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { Component, OnInit } from "@angular/core";
import { Banner } from "src/app/services/data-types/common.types";
import { HomeService } from "src/app/services/home.service";

@Component({
selector: "app-home",
templateUrl: "./home.component.html",
styleUrls: ["./home.component.less"],
})
export class HomeComponent implements OnInit {
banners:Banner[];
constructor(private homeService: HomeService) {
this.homeService.getBanners().subscribe((banners) => {
console.log("banners", banners);
this.banners = banners
});
}
ngOnInit() {}
}

轮播样式

  • home目录下创建components/wy-carousel组件,对组件进行二次封装
1
ng g c pages/home/components/wy-carousel
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div class="carousel">
<div class="wrap">
<!--左箭头-->
<i nz-icon class="arrow left" nzType="left" nzTheme="outline" (click)="onChangeSlide('pre')"></i>
<!--占位符,插槽-->
<ng-content></ng-content>
<!--模板,用于覆盖原有样式,number为索引变量,接收索引值-->
<ng-template #dot let-number>
<!--拿到小圆点索引控制样式-->
<i class="dot" [class.active]="activeIndex === number"></i>
</ng-template>
<!--右箭头-->
<i nz-icon class="arrow right" nzType="right" nzTheme="outline" (click)="onChangeSlide('next')"></i>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import {
Component,
OnInit,
TemplateRef,
ViewChild,
Input,
Output,
EventEmitter,
ChangeDetectionStrategy,
} from "@angular/core";

@Component({
selector: "app-wy-carousel",
templateUrl: "./wy-carousel.component.html",
styleUrls: ["./wy-carousel.component.less"],
// 变更检测策略改变 OnPush只会在Input属性发生变化才触发变更检测
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class WyCarouselComponent implements OnInit {
// 索引值,父组件home传过来
@Input() activeIndex = 0;
// 子组件需要触发父组件的事件 参数为联合参数
@Output() changeSlide = new EventEmitter<"pre" | "next">();

// 模板变量,ViewChild获取子组件的dot引用并赋值给dotRef
@ViewChild("dot", { static: true })
dotRef: TemplateRef<any>;

constructor() {}

ngOnInit() {}

// 触发事件,发送给父组件
onChangeSlide(type: "pre" | "next") {
this.changeSlide.emit(type);
}
}
  • home.component.html中编写轮播HTML结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<div class="home">
<!--子组件 #wyCarouse相当于ref
activeIndex传给子组件
changeSlide 监听子组件的事件 this.changeSlide.emit(type);
-->
<app-wy-carousel #wyCarousel [activeIndex]="carouselActiveIndex" (changeSlide)="onChangeSlide($event)">
<!--里面nz-carousel内容放插槽里
[nzDotRender]="wyCarousel.dotRef 引用模板
-->
<nz-carousel
nzAutoPlay
nzEffect="fade"
[nzDotRender]="wyCarousel.dotRef"
(nzBeforeChange)="onBeforeChange($event)"
>
<div
class="carousel-item"
nz-carousel-content
*ngFor="let item of banners"
>
<a [href]="item.url" target="_blank" class="banner-item">
<img [src]="item.imageUrl" />
</a>
</div>
</nz-carousel>
</app-wy-carousel>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import { Component, OnInit, ViewChild } from "@angular/core";
import { NzCarouselComponent } from "ng-zorro-antd";
import { Banner } from "src/app/services/data-types/common.types";
import { HomeService } from "src/app/services/home.service";

@Component({
selector: "app-home",
templateUrl: "./home.component.html",
styleUrls: ["./home.component.less"],
})
export class HomeComponent implements OnInit {
// 保存索引变量
carouselActiveIndex = 0;
// 自定义的banner数据格式
banners: Banner[];

// 拿到轮播图的引用
@ViewChild(NzCarouselComponent, { static: true })
private nzCarousel: NzCarouselComponent;

constructor(private homeService: HomeService) {
// 请求数据
this.homeService.getBanners().subscribe((banners) => {
this.banners = banners;
});
}
ngOnInit() {}

// 切换前回调,获取索引值 {to}为解构赋值
onBeforeChange({ to }) {
this.carouselActiveIndex = to;
}
// 通过轮播图组件的实例调用前后箭头翻页方法
onChangeSlide(type: string) {
this.nzCarousel[type]();
}
}

推荐歌单

封装服务

  • 首先根据接口在data-types/common.types.ts中定义数据格式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 定义Banner的数据类型
export type Banner = {
targetId : number;
url : string;
imageUrl : string;
}
// 定义热门标签所需的内容格式
export type HotTag = {
id: number;
name: string;
position: number;
}
// 定义歌单所需的内容格式
export type SongSheet = {
id: number;
name: string;
picUrl: string;
playCount: number;
}
  • 然后在home.service.ts中封装服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import { HttpClient } from "@angular/common/http";
import { Inject, Injectable } from "@angular/core";
import { Observable } from "rxjs";
import { Banner, HotTag, SongSheet } from "./data-types/common.types";
import { API_CONFIG, ServicesModule } from "./services.module";
import { map } from "rxjs/internal/operators";

@Injectable({
providedIn: ServicesModule,
})
export class HomeService {
constructor(
private http: HttpClient,
@Inject(API_CONFIG) private uri: string
) {}

// 获取轮播图
getBanners(): Observable<Banner[]> {
return this.http
.get(this.uri + "banner")
.pipe(map((res: { banners: Banner[] }) => res.banners));
}
// 获取热门标签
getHotTags(): Observable<HotTag[]> {
return this.http.get(this.uri + "playlist/hot").pipe(
map((res: { tags: HotTag[] }) => {
// 先排序再截取5条数据
return res.tags
.sort((x: HotTag, y: HotTag) => x.position - y.position)
.slice(0, 5);
})
);
}
// 获取热门歌单
getPersonalSheetList(): Observable<SongSheet[]> {
return this.http
.get(this.uri + "personalized")
.pipe(map((res: { result: SongSheet[] }) => res.result.slice(0,16))); // 截取16条数据
}
}
  • home.component.ts组件中使用即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import { Component, OnInit, ViewChild } from "@angular/core";
import {
Banner,
HotTag,
SongSheet,
} from "src/app/services/data-types/common.types";
import { HomeService } from "src/app/services/home.service";

@Component({
selector: "app-home",
templateUrl: "./home.component.html",
styleUrls: ["./home.component.less"],
})
export class HomeComponent implements OnInit {
carouselActiveIndex = 0;
banners: Banner[];
hotTags: HotTag[];
songSheetList: SongSheet[];

constructor(private homeService: HomeService) {
// 获取数据
this.getBanners();
this.getHotTags();
this.getPersonalSheetList();
}

// 获取轮播图
private getBanners() {
this.homeService.getBanners().subscribe((banners) => {
this.banners = banners;
});
}
// 获取热门标签
private getHotTags() {
this.homeService.getHotTags().subscribe((tags) => {
this.hotTags = tags;
});
}
// 获取推荐歌单
private getPersonalSheetList() {
this.homeService.getPersonalSheetList().subscribe((sheets) => {
this.songSheetList = sheets;
});
}
}

歌单组件

  • 歌单是一种卡片UI形式,大多数地方会使用到。所以可以封装成一个组件。创建一个用于管理自定义UI的模块
1
ng g m share/wy-ui
  • 然后在share.module.ts中导入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { WyUiModule } from './wy-ui/wy-ui.module'
@NgModule({
declarations: [],
imports: [
// ...
WyUiModule
],
// 共享模块 需要导出
exports: [
// ...
WyUiModule
],
})
export class ShareModule { }
  • 然后创建歌单组件
1
ng g c share/wy-ui/single-sheet
1
2
3
4
5
6
7
8
9
10
// 在wy-ui.module.ts中导入歌单组件
import { NgModule } from "@angular/core";
import { SingleSheetComponent } from "./single-sheet/single-sheet.component";

@NgModule({
declarations: [SingleSheetComponent],
imports: [],
exports: [SingleSheetComponent],
})
export class WyUiModule {}
  • 然后在home.componenet.html中使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<div class="home">
<!--轮播组件-->
<!--推荐歌单-->
<div class="main">
<div class="wrap">
<div class="left">
<div class="sec">
<div class="up">
<div class="navs">
<h2>
<i></i>
<a>热门标签</a>
</h2>
<nav>
<a *ngFor="let item of hotTags">{{ item.name }}</a>
</nav>
</div>
<a>
更多
<i nz-icon type="arrow-right" theme="outline"></i>
</a>
</div>
<!--歌单-->
<div class="down">
<div class="down-wrap">
<!--使用歌单组件,传数据进去-->
<app-single-sheet
class="sheet-item"
*ngFor="let item of songSheetList"
[sheet]="item"
></app-single-sheet>
</div>
</div>
</div>
</div>
<div class="right"></div>
</div>
</div>
</div>
  • 父组件把歌单数据传过来,需要接受并展示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
import { SongSheet } from 'src/app/services/data-types/common.types';

@Component({
selector: 'app-single-sheet',
templateUrl: './single-sheet.component.html',
styleUrls: ['./single-sheet.component.less'],
// 简单组件都推荐改变一下显示策略
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SingleSheetComponent implements OnInit {
// 接受父组件传来的数据,然后进行遍历,SongSheet为数据格式类型
@Input() sheet:SongSheet
constructor() { }

ngOnInit() {
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<ng-container>
<a class="cover">
<img [src]="sheet.picUrl" [alt]="sheet.name" />
<div class="bottom">
<div class="num">
<!--播放量-->
<i class="icon erji"></i>
<!--播放量进行处理 管道-->
<span>{{ sheet.playCount | playCount }}</span>
</div>
<i class="icon play"></i>
</div>
</a>
<span class="dec"> {{ sheet.name }} </span>
</ng-container>

创建管道

  • 播放量超过万级,需要进行处理,可以定义一个管道
1
ng g p share/play-count
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
name: 'playCount'
})
export class PlayCountPipe implements PipeTransform {

// value为传进来的参数
transform(value: number): number | string{
if(value > 10000 ){
return Math.floor(value / 1000) + '万'
}else{
return value
}
}
}
  • 然后在share.module.ts中导入导出
1
2
3
4
5
6
7
8
9
10
import { NgModule } from "@angular/core";
import { SingleSheetComponent } from "./single-sheet/single-sheet.component";
import { PlayCountPipe } from "../play-count.pipe";

@NgModule({
declarations: [SingleSheetComponent, PlayCountPipe],
imports: [],
exports: [SingleSheetComponent, PlayCountPipe],
})
export class WyUiModule {}