diff --git a/components/list/nz-list.component.ts b/components/list/nz-list.component.ts index 63ebb1b7c86..0226a209842 100644 --- a/components/list/nz-list.component.ts +++ b/components/list/nz-list.component.ts @@ -27,11 +27,7 @@ import { NzListGrid } from './interface'; encapsulation : ViewEncapsulation.None, changeDetection : ChangeDetectionStrategy.OnPush, styles : [ ` - :host { - display: block; - } - - nz-spin { + nz-list, nz-list nz-spin { display: block; } ` ] diff --git a/components/upload/demo/avatar.ts b/components/upload/demo/avatar.ts index 0d5515f4cf6..789e5352b6f 100644 --- a/components/upload/demo/avatar.ts +++ b/components/upload/demo/avatar.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; import { NzMessageService, UploadFile } from 'ng-zorro-antd'; +import { Observable, Observer } from 'rxjs'; @Component({ selector: 'nz-demo-upload-avatar', @@ -20,7 +21,7 @@ import { NzMessageService, UploadFile } from 'ng-zorro-antd'; `, styles: [ ` - :host ::ng-deep .avatar-uploader > .ant-upload { + :host ::ng-deep .avatar-uploader > .ant-upload, :host ::ng-deep .avatar { width: 128px; height: 128px; } @@ -39,18 +40,34 @@ export class NzDemoUploadAvatarComponent { loading = false; avatarUrl: string; - constructor(private msg: NzMessageService) {} + constructor(private msg: NzMessageService) { } beforeUpload = (file: File) => { - const isJPG = file.type === 'image/jpeg'; - if (!isJPG) { - this.msg.error('You can only upload JPG file!'); - } - const isLt2M = file.size / 1024 / 1024 < 2; - if (!isLt2M) { - this.msg.error('Image must smaller than 2MB!'); - } - return isJPG && isLt2M; + return new Observable((observer: Observer) => { + const isJPG = file.type === 'image/jpeg'; + if (!isJPG) { + this.msg.error('You can only upload JPG file!'); + observer.complete(); + return; + } + const isLt2M = file.size / 1024 / 1024 < 2; + if (!isLt2M) { + this.msg.error('Image must smaller than 2MB!'); + observer.complete(); + return; + } + // check height + this.checkImageDimension(file).then(dimensionRes => { + if (!dimensionRes) { + this.msg.error('Image only 300x300 above'); + observer.complete(); + return; + } + + observer.next(isJPG && isLt2M && dimensionRes); + observer.complete(); + }); + }); } private getBase64(img: File, callback: (img: string) => void): void { @@ -59,6 +76,19 @@ export class NzDemoUploadAvatarComponent { reader.readAsDataURL(img); } + private checkImageDimension(file: File): Promise { + return new Promise(resolve => { + const img = new Image(); // create image + img.src = window.URL.createObjectURL(file); + img.onload = () => { + const width = img.naturalWidth; + const height = img.naturalHeight; + window.URL.revokeObjectURL(img.src); + resolve(width === height && width >= 300); + }; + }); + } + handleChange(info: { file: UploadFile }): void { if (info.file.status === 'uploading') { this.loading = true; diff --git a/components/upload/demo/filter.ts b/components/upload/demo/filter.ts index 266a64863c9..1dbb5d39c6a 100644 --- a/components/upload/demo/filter.ts +++ b/components/upload/demo/filter.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; -import { UploadFile, UploadFilter, NzMessageService } from 'ng-zorro-antd'; +import { NzMessageService, UploadFile, UploadFilter } from 'ng-zorro-antd'; +import { Observable, Observer } from 'rxjs'; @Component({ selector: 'nz-demo-upload-filter', @@ -32,6 +33,16 @@ export class NzDemoUploadFilterComponent { } return fileList; } + }, + { + name: 'async', + fn: (fileList: UploadFile[]) => { + return new Observable((observer: Observer) => { + // doing + observer.next(fileList); + observer.complete(); + }); + } } ]; diff --git a/components/upload/interface.ts b/components/upload/interface.ts index b3cd865ac3d..2c6317bc23b 100644 --- a/components/upload/interface.ts +++ b/components/upload/interface.ts @@ -66,7 +66,7 @@ export interface ZipButtonOptions { export interface UploadFilter { name: string; - fn: (fileList: UploadFile[]) => UploadFile[]; + fn: (fileList: UploadFile[]) => UploadFile[] | Observable; } export interface UploadXHRArgs { diff --git a/components/upload/nz-upload-btn.component.ts b/components/upload/nz-upload-btn.component.ts index fbf7a67f448..87b7ade233f 100644 --- a/components/upload/nz-upload-btn.component.ts +++ b/components/upload/nz-upload-btn.component.ts @@ -12,7 +12,8 @@ import { ViewChild, ViewEncapsulation } from '@angular/core'; -import { Observable, Subscription } from 'rxjs'; +import { of, Observable, Subscription } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; import { NzUpdateHostClassService } from '../core/services/update-host-class.service'; @@ -143,11 +144,20 @@ export class NzUploadBtnComponent implements OnInit, OnChanges, OnDestroy { } uploadFiles(fileList: FileList | File[]): void { - let postFiles: UploadFile[] = Array.prototype.slice.call(fileList); - this.options.filters.forEach(f => postFiles = f.fn(postFiles)); - postFiles.forEach((file: UploadFile) => { - this.attachUid(file); - this.upload(file, postFiles); + let filters$: Observable = of(Array.prototype.slice.call(fileList)); + this.options.filters.forEach(f => { + filters$ = filters$.pipe(switchMap(list => { + const fnRes = f.fn(list); + return fnRes instanceof Observable ? fnRes : of(fnRes); + })); + }); + filters$.subscribe(list => { + list.forEach((file: UploadFile) => { + this.attachUid(file); + this.upload(file, list); + }); + }, e => { + console.warn(`Unhandled upload filter error`, e); }); } @@ -165,6 +175,8 @@ export class NzUploadBtnComponent implements OnInit, OnChanges, OnDestroy { } else if (typeof processedFile === 'boolean' && processedFile !== false) { this.post(file); } + }, e => { + console.warn(`Unhandled upload beforeUpload error`, e); }); } else if (before !== false) { return this.post(file); diff --git a/components/upload/upload.spec.ts b/components/upload/upload.spec.ts index b593f0d44a5..03c33689531 100644 --- a/components/upload/upload.spec.ts +++ b/components/upload/upload.spec.ts @@ -6,7 +6,7 @@ import { fakeAsync, tick, ComponentFixture, TestBed } from '@angular/core/testin import { FormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { of, Observable } from 'rxjs'; +import { of, throwError, Observable, Observer } from 'rxjs'; import { delay } from 'rxjs/operators'; import { NzI18nModule, NzI18nService } from '../i18n'; @@ -332,6 +332,17 @@ describe('upload', () => { pageObject.postSmall(); expect(instance._nzChange).toBeUndefined(); }); + it('should be console.warn error', () => { + let warnMsg = ''; + console.warn = jasmine.createSpy().and.callFake(res => warnMsg = res); + expect(instance._nzChange).toBeUndefined(); + instance.beforeUpload = (file: UploadFile, fileList: UploadFile[]): Observable => { + return throwError(''); + }; + fixture.detectChanges(); + pageObject.postSmall(); + expect(warnMsg).toContain(`Unhandled upload beforeUpload error`); + }); }); }); @@ -380,6 +391,55 @@ describe('upload', () => { pageObject.postFile(JPGSMALL.target.files); expect(instance._beforeUploadList.length).toBe(0); }); + describe('with Observable', () => { + it('shoule working', () => { + instance.nzFilter = [ + { + name: 'f1', + fn: (fileList: UploadFile[]) => { + return new Observable((observer: Observer) => { + observer.next(fileList.slice(1)); + observer.complete(); + }); + } + }, + { + name: 'f2', + fn: (fileList: UploadFile[]) => { + return new Observable((observer: Observer) => { + observer.next(fileList.slice(1)); + observer.complete(); + }); + } + } + ]; + fixture.detectChanges(); + expect(instance._beforeUploadList.length).toBe(0); + pageObject.postFile([ + ...PNGSMALL.target.files, + ...PNGSMALL.target.files, + ...PNGSMALL.target.files + ]); + expect(instance._beforeUploadList.length).toBe(1); + }); + it('should be console.warn error', () => { + let warnMsg = ''; + console.warn = jasmine.createSpy().and.callFake(res => warnMsg = res); + instance.nzFilter = [ + { + name: 'f1', + fn: (fileList: UploadFile[]) => { + return new Observable((observer: Observer) => { + observer.error('filter error'); + }); + } + } + ]; + fixture.detectChanges(); + pageObject.postFile(PNGSMALL.target.files); + expect(warnMsg).toContain(`Unhandled upload filter error`); + }); + }); }); it('#nzFileList, should be allow empty', () => {