上篇总结:
- 管道 pipe
相当于vue中的过滤器. 使用时: {{值 | 管道名:参数:参数...}}
angular默认官方提供了一些常用管道, 可以直接使用
也提供了自定义的方式: ng g p 管道名
- 指令 directive
生成命令: ng g d 指令名, 用于操作元素的 DOM 属性
- 生命周期
初始化 ngOnInit -> 挂载时 init -> 更新时 checked -> 销毁 destroy
- 组件间的参数传递
– 父子: 子中用 @Input() 关键词声明属性即可
– 子父: 子中要声明事件, 向外传递数据 @Output(), 父通过事件方式 传递一个 自身方法 给子, 子触发传入的方法 向方法中传递数据
– 兄弟: 子1 -> 父 -> 子2 或 服务进行全局状态管理.
- 服务: 同vue 的 Vuex
生成服务命令: ng g s 服务名
使用时, 要通过 依赖注入 机制来完成.
– 依赖注入: 在构造函数的参数 中声明类型, 组件在使用时 就必须传递对应的类型的值.
– 其中: 我们负责声明依赖即可; 注入操作由系统自动完成;
语法糖用法: constructor(权限词 参数名: 服务类型)
- 网络服务: 官方提供了很多强大的服务, 网络服务是其中一种
– 网络模块默认未加载, 必须到 app.module.ts 文件中加载才可以
话不多说直入主题 ;接下来介绍手机端的开发
Ionic_________________
Vue 有 mintUI, 制作手机端的界面
Angular 有 ionic, 制作手机端的 UI库
脚手架的安装:
npm install -g @ionic/cli --force
生成项目包
ionic start ionicApp blank
// 生成 包名 类型((blank/sidemenu/tabs)
/**
*- blank: 基础包
*- tabs: 带有标签栏导航的包
*- sidemenu: 带有侧边栏导航的包
/
包的运行:
ionic s
vscode插件:
主要容器:
- ion-app: 根容器
- ion-header: 头部导航栏容器
- ion-content: 主体内容部分
- ion-footer: 底部容器
更多组件详细介绍请点这里
button按钮组件
<!-- 按钮组件 -->
<!-- https://ionicframework.com/docs/api/button -->
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>按钮</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<!-- 组件的风格, 默认会自动适配所在操作系统 -->
<ion-button>我是按钮</ion-button>
<!-- mode: 指定风格 -->
<ion-button mode="ios">ios</ion-button>
<ion-button mode="md">android</ion-button>
<!-- 官方自带很多主题 theme -->
<br />
<ion-button color="primary">primary</ion-button>
<ion-button color="secondary">secondary</ion-button>
<ion-button color="tertiary">tertiary</ion-button>
<ion-button color="success">success</ion-button>
<ion-button color="warning">warning</ion-button>
<ion-button color="danger">danger</ion-button>
<ion-button color="light">light</ion-button>
<ion-button color="medium">medium</ion-button>
<ion-button color="dark">dark</ion-button>
<!-- 大小 -->
<br />
<ion-button size="small">small</ion-button>
<ion-button size="default">default</ion-button>
<ion-button size="large">large</ion-button>
<!-- 填充方式 -->
<br />
<ion-button fill="outline">outline</ion-button>
<ion-button fill="solid">solid</ion-button>
<ion-button fill="clear">clear</ion-button>
<!-- 扩展方式 -->
<br />
<ion-button expand="block">block</ion-button>
<ion-button expand="full">full</ion-button>
<!-- 不可用 -->
<br />
<ion-button disabled>不可用</ion-button>
</ion-content>
</ion-app>
bdge徽章
<!-- 徽章组件 badge -->
<!-- https://ionicframework.com/docs/api/badge -->
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>badge徽章</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-badge>42</ion-badge>
<ion-badge color="success">success</ion-badge>
<ion-badge mode="ios" color="danger">ios</ion-badge>
</ion-content>
</ion-app>
icon图标
<!-- 图标库 -->
<!-- https://ionicons.com/ -->
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>icon</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-icon name="add" style="font-size: 30px"></ion-icon>
<ion-icon name="logo-alipay" style="font-size: 50px" color="secondary"></ion-icon>
<!-- 可以搭配按钮使用 -->
<ion-button>
<ion-icon name="add"></ion-icon>
</ion-button>
</ion-content>
</ion-app>
card卡片组件
<!-- 卡片组件 -->
<!-- https://ionicframework.com/docs/api/card -->
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>卡片</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<!-- i-card-full -->
<ion-card>
<img src="http://101.96.128.94:9999/mfresh/news_imgs/01.jpg" alt="" />
<ion-card-header>
<ion-card-subtitle>此净化器功能优秀, 节能减排, 价格优美.</ion-card-subtitle>
<ion-card-title>净美仕净化器</ion-card-title>
</ion-card-header>
<ion-card-content>
打掉的的阿达的看了看.马卡龙文件但大家拿健康的宽带连接我已大奖等你
</ion-card-content>
</ion-card>
</ion-content>
</ion-app>
<!-- 横向滚动展示 -->
<!-- https://ionicframework.com/docs/api/slides -->
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>横向滚动展示</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<!-- slides: 容器; slide: 项目 -->
<!-- pager: 页数指示 -->
<!-- loop: 循环滚动 -->
<!-- autoplay: 自动滚动 -->
<!-- disableOnInteraction: 用户触摸之后 让自动滚动失效; 默认为true -->
<ion-slides
pager
[options]="{ loop: true, autoplay: { disableOnInteraction: false } }"
>
<!-- 在ts文件先定义好图片路径的数组 -->
<ion-slide *ngFor="let item of images">
<img [src]="item" alt="" />
</ion-slide>
</ion-slides>
</ion-content>
</ion-app>
grid栅格
<!-- 栅格布局 -->
<!-- https://ionicframework.com/docs/api/grid -->
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>栅格布局</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<!-- i-grid -->
<!-- 在scss中定义样式grid-lianxi -->
<ion-grid fixed class="grid-lianxi">
<ion-row>
<!-- 每行最多12列, size代表列数 -->
<ion-col size="6">6</ion-col>
<ion-col size="6">6</ion-col>
</ion-row>
<ion-row>
<!-- 超出会换行 -->
<ion-col size="4">4</ion-col>
<ion-col size="5">5</ion-col>
<ion-col size="6">6</ion-col>
<ion-col size="7">7</ion-col>
</ion-row>
<ion-row>
<ion-col size="3">3</ion-col>
<!-- 不写会自动填充剩余份数 -->
<ion-col>不写份数</ion-col>
</ion-row>
</ion-grid>
</ion-content>
</ion-app>
综合这些组件的一个例子
html:
<!-- 新闻 -->
<ion-app>
<ion-header>
<ion-toolbar color="danger">
<ion-title>网易新闻</ion-title>
</ion-toolbar>
</ion-header>
<!-- scrollEvents: 允许监听滚动事件, 默认false -->
<!-- ionScroll: 滚动事件 -->
<ion-content
*ngIf="news"
#content
[scrollEvents]="true"
(ionScroll)="onScroll($event)"
>
<!-- 下拉刷新 -->
<ion-refresher slot="fixed" (ionRefresh)="doRefresh($event)">
<ion-refresher-content></ion-refresher-content>
</ion-refresher>
<ion-grid fixed class="news-grid">
<ion-row>
<ion-col size="6" *ngFor="let item of news.result">
<ion-card>
<img [src]="item.image" alt="" />
<div class="content">
<div>{{ item.title }}</div>
<div>{{ item.passtime }}</div>
</div>
</ion-card>
</ion-col>
</ion-row>
</ion-grid>
<!-- 加载更多 -->
<!-- threshold: 触底阈值. 10% 代表剩余未显示区域的高度 是 显示区域高度 10% 时触发触底操作 -->
<ion-infinite-scroll
threshold="10%"
position="bottom"
(ionInfinite)="loadData($event)"
>
<ion-infinite-scroll-content
loadingSpinner="dots"
loadingText="加载中..."
>
</ion-infinite-scroll-content>
</ion-infinite-scroll>
<!-- 悬浮按钮: 可以悬浮在已有内容的上方 -->
<!-- vertical: 竖向, 可选值 top/center/bottom 对应 上/中/下 -->
<!-- horizontal: 横向, 可选值 start/center/end -->
<ion-fab
[hidden]="!showGoTop"
vertical="bottom"
horizontal="end"
slot="fixed"
>
<ion-fab-button (click)="goTop()">
<ion-icon name="arrow-up-outline"></ion-icon>
</ion-fab-button>
</ion-fab>
</ion-content>
</ion-app>
scss:
.grid-lianxi {
border: 1px solid red;
ion-row {
border: 2px solid orange;
ion-col {
border: 1px solid blue;
}
}
}
.news-grid {
padding: 3px;
img {
width: 100%;
}
ion-col {
padding: 3px;
}
ion-card {
margin: 0;
.content {
padding: 4px;
div:first-child {
color: black;
text-overflow: -o-ellipsis-lastline;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
font-size: 1.1em;
margin-bottom: 3px;
}
}
}
}
ts:
import { HttpClient } from '@angular/common/http';
import { Component, ViewChild } from '@angular/core';
import { IonContent } from '@ionic/angular';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss'],
})
export class AppComponent {
// 掌控子元素, 绑定变量 content 到 ion-content 组件
@ViewChild('content') content: IonContent;
showGoTop = false; //是否要显示 回到顶部 按钮
// 绑定到 ion-conent 的滚动监听事件, 当滚动时自动触发
onScroll(event) {
console.log(event);
const y = event.detail.currentY; //当前竖向滚动的 y
//如果 y > 1200 就显示回到头部按钮
this.showGoTop = y > 1200;
}
goTop() {
// scrollToTop(动画时长); 单位毫秒
this.content.scrollToTop(250);
}
constructor(public http: HttpClient) {}
getUrl(pno: number) {
return 'https://api.apiopen.top/getWangYiNews?page=' + pno;
}
news: News;
ngOnInit(): void {
const url = this.getUrl(1);
this.http.get(url).subscribe((res: News) => {
console.log(res);
this.news = res;
});
}
page = 1;
//下拉刷新
doRefresh(event) {
console.log('下拉刷新触发!');
const url = this.getUrl(1);
this.http.get(url).subscribe((res: News) => {
console.log(res);
this.news = res;
this.page = 1; //重置为 第一页
event.target.complete(); //结束下拉刷新动画的显示
});
}
// 触底时触发
loadData(event) {
// console.log(event);
const url = this.getUrl(this.page + 1);
this.http.get(url).subscribe((res: News) => {
console.log(res);
// 合并新旧数据中的数组
res.result = this.news.result.concat(res.result);
this.news = res;
this.page++; //成功后, 页数+1
//通知组件此次加载操作已完成; 这样组件才会监听下一次
event.target.complete();
});
}
images = [
'http://101.96.128.94:9999/mfresh/images/banner_01.jpg',
'http://101.96.128.94:9999/mfresh/images/banner_02.jpg',
'http://101.96.128.94:9999/mfresh/images/banner_03.jpg',
'http://101.96.128.94:9999/mfresh/images/banner_04.jpg',
];
}
/////
///// 返回值结构声明
interface News {
code: number;
message: string;
result: NewsResult[];
}
interface NewsResult {
image: string;
passtime: string;
path: string;
title: string;
}
item组件
<!-- item组件 -->
<!-- https://ionicframework.com/docs/api/item -->
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>item</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-item>
<ion-label>Awesome Label</ion-label>
</ion-item>
<!-- button: 可以点击 -->
<!-- detail: 右侧出现 箭头图标 -->
<ion-item button detail>
<ion-label>Click me</ion-label>
</ion-item>
<ion-item>
<!-- slot: 插槽; item具有左右两个插槽, start 和 end -->
<ion-badge slot="end">42</ion-badge>
<ion-label>Item Avatar</ion-label>
</ion-item>
<ion-item>
<!-- avatar: 把内部图片变圆形 -->
<ion-avatar slot="start">
<img
src="http://www.gx8899.com/uploads/allimg/2017112908/pc2e3vft403.jpg"
/>
</ion-avatar>
<ion-label>
<h3>重庆天花板</h3>
<p>今天的面试题超级简单, 妥妥的~</p>
</ion-label>
<ion-badge color="danger" slot="end">42</ion-badge>
</ion-item>
<ion-item>
<ion-icon name="settings" slot="start"></ion-icon>
<ion-label>设置</ion-label>
</ion-item>
<ion-item lines="none">
<ion-label>没有分割线</ion-label>
</ion-item>
<ion-item lines="full">
<ion-label>分割线充满</ion-label>
</ion-item>
<ion-item>
<ion-label>用户名:</ion-label>
<ion-input placeholder="账号/手机号/邮箱"></ion-input>
</ion-item>
<ion-item>
<ion-label>密码:</ion-label>
<ion-input placeholder="密码" type="password"></ion-input>
</ion-item>
<!-- 分组 -->
<ion-item-group>
<ion-item-divider>
<ion-label>A</ion-label>
</ion-item-divider>
<ion-item>
<ion-label>阿宝</ion-label>
</ion-item>
<ion-item>
<ion-label>阿花</ion-label>
</ion-item>
<ion-item-divider>
<ion-label>B</ion-label>
</ion-item-divider>
<ion-item>
<ion-label>宝蓝</ion-label>
</ion-item>
<ion-item>
<ion-label>boner</ion-label>
</ion-item>
</ion-item-group>
<!-- i-item-sliding: 通过滑动 显示隐藏操作 -->
</ion-content>
</ion-app>
例:网络模块+组件+跨域
html:
<ion-app>
<ion-header>
<ion-toolbar color="warning">
<ion-title>直播列表</ion-title>
</ion-toolbar>
</ion-header>
<ion-content *ngIf="res" #content [scrollEvents]="true" (ionScroll)="onScroll($event)">
<ion-refresher slot="fixed" (ionRefresh)="doRefresh($event)">
<ion-refresher-content></ion-refresher-content>
</ion-refresher>
<ion-grid fixed class="douyu-grid">
<ion-row>
<ion-col size="6" *ngFor="let item of res.data.list">
<ion-card>
<div class="card-header">
<img [src]="item.roomSrc" alt="" />
<div class="hn">{{ item.hn }}</div>
<div class="nick">
<ion-avatar>
<img [src]="item.avatar" />
</ion-avatar>
<span>{{ item.nickname }}</span>
</div>
</div>
<div class="room-name">{{ item.roomName }}</div>
</ion-card>
</ion-col>
</ion-row>
</ion-grid>
<!-- i-infinite-scroll -->
<ion-infinite-scroll threshold="10%" position="bottom" (ionInfinite)="loadData($event)">
<ion-infinite-scroll-content loadingSpinner="bubbles" loadingText="加载中...">
</ion-infinite-scroll-content>
</ion-infinite-scroll>
<!-- 悬浮按钮 -->
<ion-fab [hidden]="!showGoTop" vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button (click)="goTop()">
<ion-icon name="arrow-up-outline"></ion-icon>
</ion-fab-button>
</ion-fab>
</ion-content>
</ion-app>
ts:(注意在app.module.ts中引入HttpClientModule模块)
import { HttpClient } from '@angular/common/http';
import { Component, ViewChild } from '@angular/core';
import { IonContent } from '@ionic/angular';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss'],
})
export class AppComponent {
@ViewChild('content') content: IonContent;
showGoTop = false;
onScroll(e) {
this.showGoTop = e.detail.currentY > 1000;
}
goTop() {
this.content.scrollToTop(200);
}
getUrl(page: number) {
// 请求地址以 /douyu 开头, 代理就会进行解析
return `/douyu/api/room/list?page=${page}&type=yz`;
}
// 依赖注入机制: 声明依赖
constructor(public http: HttpClient) { }
res: Douyu;
// 下拉刷新
doRefresh(e) {
const url = this.getUrl(1);
this.http.get(url).subscribe((res: Douyu) => {
this.res = res;
e.target.complete(); //终止下拉刷新动画
});
}
// 加载更多
loadData(e) {
const url = this.getUrl(this.res.data.nowPage + 1);
this.http.get(url).subscribe((res: Douyu) => {
//合并新旧数据数组, 然后把新的数据 替换旧的
res.data.list = this.res.data.list.concat(res.data.list);
this.res = res;
e.target.complete(); // 声明本次加载更多操作已完成, 准备下一次
});
}
ngOnInit(): void {
this.http.get(this.getUrl(1)).subscribe((res: Douyu) => {
console.log(res);
this.res = res;
// 默认数据量8条: 不足以充满整个页面, 就会导致没有触底操作
// 再请求第二页数据 与第一页合并在一起
this.http.get(this.getUrl(2)).subscribe((res: Douyu) => {
res.data.list = this.res.data.list.concat(res.data.list);
this.res = res;
});
});
}
}
/////
///// 返回值的数据结构声明
interface Douyu {
code: number;
data: DouyuData;
}
interface DouyuData {
cate2Id: number;
list: DouyuDataList[];
nowPage: number;
pageCount: number;
}
interface DouyuDataList {
avatar: string;
cate1Id: number;
cate2Id: number;
hn: string;
isLive: number;
isVertical: number;
liveCity: string;
nickname: string;
rid: number;
roomName: string;
roomSrc: string;
verticalSrc: string;
vipId: number;
}
scss:
.douyu-grid {
padding: 3px;
ion-col {
padding: 3px;
ion-card {
margin: 0;
img {
width: 100%;
}
}
}
}
.card-header {
position: relative;
.hn {
position: absolute;
top: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.5);
color: white;
padding: 4px;
}
.nick {
position: absolute;
bottom: 0;
left: 0;
display: flex;
background-color: rgba(0, 0, 0, 0.5);
align-items: center;
color: white;
right: 0;
ion-avatar {
width: 20px;
height: 20px;
margin: 3px;
}
}
img {
display: block;
}
}
.room-name {
padding: 4px;
color: black;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
创建src目录下proxy.config.json文件跨域 在angular.json中引入