http拦截器,参考:
官网:https://angular.cn/guide/http#intercepting-requests-and-responses
设置header:https://blog.csdn.net/wanglui1990/article/details/78796662
https://www.cnblogs.com/changyaoself/p/8539048.html
https://www.cnblogs.com/leejay6567/p/9462758.html
https://juejin.im/post/5b59c89de51d45190a4336d8#comment
https://www.jianshu.com/p/165c0b6d1475
Angular 2 AsyncPipe,参考:
https://segmentfault.com/a/1190000008759314
创建拦截器的service:
ng g service share/http-interceptor
在share.module中:
providers:[
{
provide:HTTP_INTERCEPTORS,
useClass:HttpInterceptorService, // HttpInterceptorService自己服务的名字,其他都是固定的。
multi:true
}
]
service:
import '../utils/base64.js';
declare var Base64: any;
const baseurl = '';
@Injectable({
providedIn: 'root'
})
export class HttpInterceptorService implements HttpInterceptor {
constructor(private nzMessageService: NzMessageService,) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log("req:",req);
console.log("req-params:",req.params);
// 处理request的参数:可以什么都不做;或者增加header里面的token。
req = req.clone({
url: `${baseurl}${req.url}`,
params: req.params.delete('__isNeedHeader'),
body: req.body instanceof Object ? this.deleteOwnProperty(req.body, '__isNeedHeader') : null,
})
// 回调函数:
return next.handle(req)
.pipe(
retry(2), /*失败时重试2次,可自由设置(共执行n+1次)*/
tap(res=>{ // 任意执行,打印res查看数据
console.log("tap:",res);
}),
filter(res => res instanceof HttpResponse), // 过滤HttpResponse,还会有一个其他的格式,没有用的,eg:{type: 0}
map((res: HttpResponse<any>) => this.handleSuccess(res)), // 没有发生异常,就执行success
// catchError(this.handleError.bind(this)),
catchError(error=>this.handleError(error)) // 接口发生异常了,那么走错误的方法
)
}
// 成功
handleSuccess(res:HttpResponse<any>):HttpEvent<any> {
const resHeaders =res.headers; // 头
console.log("resHeaders:",resHeaders);
const status_code = resHeaders.get('status_code')|| resHeaders.get('Status_code'); // 头status
const total =resHeaders.get('x-total-count')||resHeaders.get('x-Total-Count'); // 头 tatal
const data= res.body; // res.body
console.log("total:",total);
if(status_code==='200'||!status_code){ // 真正的成功(我们用headers里面的status,200或者不存在是成功)
if(!!total){
console.log("total:",total);
return res.clone({
body:{data:res.body,total:total},
// body:{data,total}
});
}else{
return res;
}
}else { // 错误:headers里面有加密的错误信息
console.log("Base64:",Base64);
const base64= new Base64();
let status_msg = resHeaders.get('status_msg')||resHeaders.get('Status_msg')||'服务器异常';
status_msg = base64.decode(status_msg); // 错误的写法,汉字无法解析的。
/*去掉了前后的双引号*/
if(status_msg[0]==='"'){
status_msg= status_msg.slice(1,status_msg.length); // 默认第二个参数不传
}
if(status_msg[status_msg.length-1]=='"'){
status_msg= status_msg.slice(0,status_msg.length-1); // 第二个参数传-1即可。
}
throw new HttpErrorResponse({ // 重新抛出一个http的错误异常(模拟500情况的数据结构)。
error:null,
headers:res.headers,
status:parseInt(status_code),
statusText:status_msg,
url:res.url,
})
}
console.log("成功res:",res);
// return res; // 最开始什么都没有的时候,必须需要一个返回。
}
// 失败:404,500,
handleError(error){
console.log("失败error:",error);
console.log("error里面的error-evnet:",error.error instanceof ErrorEvent);
if(error.error instanceof ErrorEvent){ //
console.log('error-if');
console.error('An error occurred:', error.error.message);
return throwError((error.error.message));
}else{ // 404,500,
console.log('error-else');
let status_msg = error.statusText||'服务器异常';
if(!!error&&error.status===1000){
// goToLogin:跳转到登录页
// this.authService.goToLoginPage(); // this.router.navigate(['/pages/login']);
return;
}
if(!!error&&error.status===404){
status_msg= '请求的接口不存在';
}
// 500的时候,没有取到明确的 错误信息(头部的错误信息)
this.nzMessageService.error(status_msg);
return throwError(status_msg); // 在浏览器中抛出一个异常
}
}
// 删除对象的属性:
private deleteOwnProperty(obj: Object, field: string) {
if (obj.hasOwnProperty(field)) {
delete obj[field];
}
return obj;
}
}
注:我觉得没有使用baseService好,因为错误的时候,并没有取到错误信息,且传参不舒服。
// 500时候的错误返回:
HttpErrorResponse {headers: HttpHeaders, status: 500, statusText: "Internal Server Error", url: "http://xxx.xxxx.com:4200/marketplace-web/api/product/get-productAll/aa?page=1&size=5", ok: false, …}
error: null
headers: HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, lazyInit: ƒ}
message: "Http failure response for http://xxx.xxx.com:4200/marketplace-web/api/product/get-productAll/aa?page=1&size=5: 500 Internal Server Error"
name: "HttpErrorResponse"
ok: false
status: 500
statusText: "Internal Server Error"
url: "http://xxx.xxx.com:4200/marketplace-web/api/product/get-productAll/aa?page=1&size=5"
// get请求拼接参数
return this.http.get(url, {params: {page: '1', size: '5'}})
树结构每次都调用,确实太浪费了,所以使用service-BehaviorSubject:
ng g service core/category-tree
ng g c product/category-subtree
ng g c product/product-list-filter
问题小结:
-
输入属性发生变化,如何监听???
1.get和set
2.生命周期钩子函数
3.订阅啊 -
Set的时候,如果add和delete后,值会变化么?是不是跟对象一样,是引用类型?
-
ChangeDetectorRef和changeDetection: ChangeDetectionStrategy.OnPush,
开始home页面:
ng g c home/product-list
ng g c home/product-categories
ng g c home/publish-section
ng g c home/product-list-item
详情页面:
ng g c product/product-head
// 获取用户评论
public commentData:{total:number,data:[],iscomment:number}={
total:0,data:[],iscomment:2,
}
flex布局的时候:
ul>li,li里面是文字:
注:
1.ul要flex-wrap换行
2.li要不缩,shrink:0;
input属性的时候,使用了get方法。
按钮的防重复点击:(跟搜索一样的)
public collectEmit$ = new Subject();
// 初始化这个
this.collectEmit$
.pipe(
debounceTime(300),
tap(res=>{
console.log("tap的res:",res);
}),
distinctUntilChanged(),
).subscribe(res=>{
this.collect.emit(res);
})
// 调用函数:
this.collectEmit$.next(data);
注:因为有distinctUntilChanged,所以只有值变化后才可以触发事件。data改变属性也是可以监听到的。
rxjs-finalize:
描述:Call a function when observable completes or errors。
成功和失败都会执行 的函数。
购买
一系列操作还没做!
轮播和用户评价:
ng g c product/product-screenshot
ng g c product/product-intro
ng g c product/product-other
ng g c product/user-evaluate // 评价
ng g c product/user-evaluate-list // 评价列表
set和get的使用:
正常:
@Input() data:any; // 评论列表
ngOnInit() {
console.log(this.data); // undefined
setTimeout(()=>{
console.log(this.data); // 正确的数据
},1000)
}
ngAfterViewInit(){
console.log("ngAfterViewInit:",this.data); // 也是获取不到数的。
}
get和set:
_data:any;
@Input()
set data(value){ // 设置
console.log("value %C%C",value);
if(!value) {
this._data={total:0,data:[]};
}
this._data = value;
}
get data(){ // 取值
return this._data;
}
// 其他值的使用:
get total(){
// console.log('aaaa');
return this.data&&this.data.total;
}
get commentList(){
return this.data&&this.data.data;
}
管道:slice:eg:
<li *ngFor="let i of collection | slice:1:3">{{i}}</li>
collection: string[] = ['a', 'b', 'c', 'd'];
注:看下管道那个,已经忘光了。
报错:eg:
ERROR Error:
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
Previous value: 'ngForOf: [object Object]'. Current value: 'ngForOf: [object Object],[object Object]'.
参考:
https://blog.csdn.net/qq_33242126/article/details/79734150
https://www.cnblogs.com/xudengwei/p/9214959.html
https://blog.csdn.net/friend_ship/article/details/81773057
案例分析:
<li class="mp-user-evaluate-list__item"
*ngFor="let item of (commentList | slice:0:count)">
</li>
count:any=1;
ngAfterViewInit(){
console.log("ngAfterViewInit:",this.data);
this.count=2;
// Promise.resolve().then(() => this.count = 2);
}
angular的动画:官网:https://angular.cn/guide/animations
angular获取dom元素:
参考:
https://blog.csdn.net/idomyway/article/details/83656366
https://blog.csdn.net/qq_28004379/article/details/80801378
获取元素的样式:
getComputedStyle(elDom, null)
生命周期钩子:
https://www.colabug.com/2645773.html
computedIntroHeight:浏览器大小变化,也会执行的。
文本是输入属性传递进来的,盒子的h在输入属性渲染后才能获取;那么h用什么事件监听?
ngAfterViewChecked
,然后用this.cdRef.detectChanges();
(ChangeDetectorRef)强制刷新视图。
注:这个用法不太好,因为只要窗口大小有变化,就会执行;那么其他的操作,eg:NzNotificationService,弹框,也会执行,使用了ngAfterViewChecked的组件会重新渲染,效果有问题的。(鼠标移入NzNotificationService,页面h会变化的)
时间格式化:
npm install date-fns
1.30.1和2.0.1版本的使用不一样了!
eg:年月日的格式化:
import { format, compareAsc } from 'date-fns'
format(new Date(2014, 1, 11), 'MM/dd/yyyy') // 2.x
format(new Date(2014, 1, 11), 'MM/DD/YYYY') // 1.x
ng g service share/concat
新建模块和组件:文件名与文件夹名不一致:
ng g m carousel/mp-carousel --flat
ng g c carousel/mp-carousel --flat
npm install hammerjs
npm i ngx-image-gallery
ng-content
内容投影。
ngx-image-gallery:参数:
imageBorderRadius:'20px', // 图片的圆角
imageOffset: '40px', // add gap between image and it's container (default 20px)
showCloseControl: false, // 是否显示关闭按钮
showDeleteControl: false, // 是否显示删除按钮
showExtUrlControl: false,
imagePointer: true,
showImageTitle: true, // 是否显示描述
backdropColor: 'rgba(0, 0, 0, 0.5)', // 背景颜色
showThumbnails: true, // 是否显示缩略图
inline: true, // 弹框模式还是inline模式
showArrows: true, // 是否显示箭头
thumbnailSize:60, // 缩略图的大小。thumbnail:缩略图
reactToMouseWheel:true, // 是否鼠标滚动,好像方向是反着的!!!!
reactToKeyboard:false, // 键盘是否可以切换图片(弹框的时候,才可以使用键盘)
轮播图:
就是antd的轮播图源码!照着看一遍吧。
路径:/node_modules/ng-zorro-antd/fesm2015/ng-zorro-antd.js
引出的问题:
@HostBinding()和@HostListener():参考:
https://www.cnblogs.com/cme-kai/p/8352087.html
https://segmentfault.com/a/1190000008878888
https://blog.csdn.net/ligaoming_123/article/details/81531623
NgTemplateOutlet 参考:
https://segmentfault.com/a/1190000009530554
https://segmentfault.com/a/1190000015944548
ng-template, ng-container and ngTemplateOutlet:参考:
https://blog.csdn.net/m0_37529303/article/details/80690518
[https://blog.csdn.net/Create_mylife/article/details/85615585](https://blog.csdn.net
/Create_mylife/article/details/85615585)
https://www.jianshu.com/p/e5bed2678ab1