From bc91395afcd4192ec8a51d635360b9c8396d5ca5 Mon Sep 17 00:00:00 2001 From: Ed Morales Date: Mon, 15 Aug 2016 22:08:23 -0700 Subject: [PATCH 1/8] added white background and shadow to loading circle --- src/platform/core/loading/loading.component.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/platform/core/loading/loading.component.scss b/src/platform/core/loading/loading.component.scss index f3c7b74a40..1362e0130d 100644 --- a/src/platform/core/loading/loading.component.scss +++ b/src/platform/core/loading/loading.component.scss @@ -15,4 +15,9 @@ right: 0; } } + md-progress-circle { + background-color: white; + border-radius: 40px; + box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.26); + } } From 3935c0797e2ae6affdc2f61f95d34100e66a43dc Mon Sep 17 00:00:00 2001 From: Ed Morales Date: Mon, 15 Aug 2016 22:09:22 -0700 Subject: [PATCH 2/8] added width to circle, to fix bug --- src/platform/core/loading/loading.component.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/platform/core/loading/loading.component.html b/src/platform/core/loading/loading.component.html index 1d41a38f5d..44c50a1e6d 100644 --- a/src/platform/core/loading/loading.component.html +++ b/src/platform/core/loading/loading.component.html @@ -11,7 +11,8 @@ + [style.height]="getCircleDiameter()" + [style.width]="getCircleDiameter()"> Date: Mon, 15 Aug 2016 22:17:39 -0700 Subject: [PATCH 3/8] removed depricated methods, made loading comp creationg sync, added onDestroy and remove comp methods --- src/app/app.component.ts | 3 +- src/platform/core/index.ts | 6 + .../loading/directives/loading.directive.ts | 16 ++- .../core/loading/services/loading.service.ts | 133 +++++++++++------- 4 files changed, 104 insertions(+), 54 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index f1c64c82e2..32417ea0ba 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -4,7 +4,7 @@ import { ROUTER_DIRECTIVES } from '@angular/router'; import { MdIcon } from '@angular2-material/icon'; import { MD_LIST_DIRECTIVES } from '@angular2-material/list'; -import { TdLayoutComponent } from '../platform/core'; +import { TdLayoutComponent, TD_LOADING_ENTRY_COMPONENTS } from '../platform/core'; @Component({ directives: [ @@ -17,6 +17,7 @@ import { TdLayoutComponent } from '../platform/core'; selector: 'td-docs-app', styleUrls: ['app.component.css'], templateUrl: 'app.component.html', + precompile: [ TD_LOADING_ENTRY_COMPONENTS ], }) export class DocsAppComponent { diff --git a/src/platform/core/index.ts b/src/platform/core/index.ts index 222b54d7f7..9f109eaf77 100644 --- a/src/platform/core/index.ts +++ b/src/platform/core/index.ts @@ -43,6 +43,12 @@ export { TdStepComponent, StepState } from './steps/step.component'; export { TdStepsComponent, IStepChangeEvent } from './steps/steps.component'; // Loading +import { TdLoadingComponent } from './loading/loading.component'; + +export const TD_LOADING_ENTRY_COMPONENTS: Type[] = [ + TdLoadingComponent, +]; + export { LoadingType } from './loading/loading.component'; export { TdLoadingService, ILoadingOptions } from './loading/services/loading.service'; export { TdLoadingDirective } from './loading/directives/loading.directive'; diff --git a/src/platform/core/loading/directives/loading.directive.ts b/src/platform/core/loading/directives/loading.directive.ts index 93dcab60f4..547752296a 100644 --- a/src/platform/core/loading/directives/loading.directive.ts +++ b/src/platform/core/loading/directives/loading.directive.ts @@ -1,4 +1,4 @@ -import { Directive, Input, OnInit } from '@angular/core'; +import { Directive, Input, OnInit, OnDestroy } from '@angular/core'; import { ViewContainerRef, TemplateRef } from '@angular/core'; import { LoadingType } from '../loading.component'; @@ -7,7 +7,7 @@ import { TdLoadingService, ILoadingOptions } from '../services/loading.service'; @Directive({ selector: '[tdLoading]', }) -export class TdLoadingDirective implements OnInit { +export class TdLoadingDirective implements OnInit, OnDestroy { private _type: LoadingType; private _name: string; @@ -38,15 +38,19 @@ export class TdLoadingDirective implements OnInit { } } - constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef, - private _loadingService: TdLoadingService) { - this._viewContainer.createEmbeddedView(this._templateRef); - } + constructor(private _viewContainer: ViewContainerRef, + private _templateRef: TemplateRef, + private _loadingService: TdLoadingService) {} ngOnInit(): void { + this._viewContainer.createEmbeddedView(this._templateRef); this._registerComponent(); } + ngOnDestroy(): void { + this._loadingService.removeComponent(this._name); + } + /** * Creates [TdLoadingComponent] and attaches it to this directive's [ViewContainerRef]. * Passes this directive's [TemplateRef] to detach/attach it from DOM when loading mask is on. diff --git a/src/platform/core/loading/services/loading.service.ts b/src/platform/core/loading/services/loading.service.ts index 401a312b65..9cac1dab06 100644 --- a/src/platform/core/loading/services/loading.service.ts +++ b/src/platform/core/loading/services/loading.service.ts @@ -1,4 +1,4 @@ -import { Injectable, ComponentResolver, ComponentFactory } from '@angular/core'; +import { Injectable, ComponentFactoryResolver, NgZone } from '@angular/core'; import { Injector, ComponentRef, ViewContainerRef, TemplateRef } from '@angular/core'; import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; @@ -6,6 +6,10 @@ import { Subscription } from 'rxjs/Subscription'; import { TdLoadingComponent, LoadingType } from '../loading.component'; +const noop: () => void = () => { + // empty function +}; + export interface ILoadingOptions { name: string; type?: LoadingType; @@ -28,8 +32,9 @@ export class TdLoadingService { private _loadingSources: {[key: string]: Subject} = {}; private _loadingObservables: {[key: string]: Observable} = {}; - constructor(private _componentResolver: ComponentResolver, - private _injector: Injector) { + constructor(private _componentFactoryResolver: ComponentFactoryResolver, + private _injector: Injector, + private _ngZone: NgZone) { } /** @@ -46,24 +51,28 @@ export class TdLoadingService { public createOverlayComponent(options: ILoadingOptions, viewContainerRef: ViewContainerRef): void { (options).height = undefined; (options).overlay = true; - this._createComponent(options) - .then((loadingRef: ILoadingRef) => { - let loading: boolean = false; - loadingRef.observable - .subscribe((registered: number) => { - let instance: TdLoadingComponent = loadingRef.ref.instance; - if (registered > 0 && !loading) { - loading = true; + let loadingRef: ILoadingRef = this._createComponent(options); + let loading: boolean = false; + loadingRef.observable + .subscribe((registered: number) => { + let instance: TdLoadingComponent = loadingRef.ref.instance; + if (registered > 0 && !loading) { + loading = true; + this._ngZone.runOutsideAngular(() => { viewContainerRef.insert(loadingRef.ref.hostView, 0); instance.startInAnimation(); - } else if (registered <= 0 && loading) { - loading = false; + this._ngZone.run(noop); + }); + } else if (registered <= 0 && loading) { + loading = false; + this._ngZone.runOutsideAngular(() => { let subs: Subscription = instance.startOutAnimation().subscribe(() => { subs.unsubscribe(); viewContainerRef.detach(viewContainerRef.indexOf(loadingRef.ref.hostView)); + this._ngZone.run(noop); }); - } - }); + }); + } }); } @@ -84,29 +93,33 @@ export class TdLoadingService { let nativeElement: HTMLElement = templateRef.elementRef.nativeElement; (options).height = nativeElement.nextElementSibling.scrollHeight; (options).overlay = false; - this._createComponent(options) - .then((loadingRef: ILoadingRef) => { - let loading: boolean = false; - loadingRef.observable - .subscribe((registered: number) => { - let instance: TdLoadingComponent = loadingRef.ref.instance; - if (registered > 0 && !loading) { - loading = true; + let loadingRef: ILoadingRef = this._createComponent(options); + let loading: boolean = false; + loadingRef.observable + .subscribe((registered: number) => { + let instance: TdLoadingComponent = loadingRef.ref.instance; + if (registered > 0 && !loading) { + loading = true; + this._ngZone.runOutsideAngular(() => { let index: number = viewContainerRef.indexOf(loadingRef.ref.hostView); if (index < 0) { viewContainerRef.clear(); viewContainerRef.insert(loadingRef.ref.hostView, 0); } instance.startInAnimation(); - } else if (registered <= 0 && loading) { - loading = false; + this._ngZone.run(noop); + }); + } else if (registered <= 0 && loading) { + loading = false; + this._ngZone.runOutsideAngular(() => { let subs: Subscription = instance.startOutAnimation().subscribe(() => { subs.unsubscribe(); viewContainerRef.createEmbeddedView(templateRef); viewContainerRef.detach(viewContainerRef.indexOf(loadingRef.ref.hostView)); + this._ngZone.run(noop); }); - } - }); + }); + } }); } @@ -114,35 +127,65 @@ export class TdLoadingService { * params: * - name: string * + * Removes loading mask from service context. + */ + public removeComponent(name: string): void { + if (this._context[name]) { + this._loadingSources[name] = undefined; + delete this._loadingSources[name]; + this._context[name].loadingRef.destroy(); + this._context[name] = undefined; + delete this._context[name]; + } + } + + /** + * params: + * - name: string + * - registers?: number + * returns: true if successful + * * Resolves a request for the loading mask referenced by the name parameter. + * Can optionally pass registers argument to set a number of register calls. */ - public register(name: string): void { + public register(name: string, registers: number = 1): boolean { if (this._loadingSources[name]) { - this._loadingSources[name].next(++this._context[name].times); + registers = registers < 1 ? 1 : registers; + this._context[name].times += registers; + this._loadingSources[name].next(this._context[name].times); + return true; } + return false; } /** * params: * - name: string + * - resolves?: number + * returns: true if successful * * Registers a request for the loading mask referenced by the name parameter. + * Can optionally pass resolves argument to set a number of resolve calls. */ - public resolve(name: string): void { + public resolve(name: string, resolves: number = 1): boolean { if (this._loadingSources[name]) { - let times: number = 0; + resolves = resolves < 1 ? 1 : resolves; if (this._context[name].times > 0) { - times = --this._context[name].times; + let times: number = this._context[name].times; + times -= resolves; + this._context[name].times = times < 0 ? 0 : times; } - this._loadingSources[name].next(times); + this._loadingSources[name].next(this._context[name].times); + return true; } + return false; } /** * Creates a generic [TdLoadingComponent] and its context. * Returns a promise that resolves to a [ILoadingRef] with the created [ComponentRef] and its referenced [Observable]. */ - private _createComponent(options: IInternalLoadingOptions): Promise { + private _createComponent(options: IInternalLoadingOptions): ILoadingRef { let name: string = options.name; if (!name) { throw 'Name is required for Loading Component.'; @@ -152,19 +195,15 @@ export class TdLoadingService { } else { throw 'Name duplication: Loading Component name conflict.'; } - return new Promise((resolve: Function) => { - this._componentResolver.resolveComponent(TdLoadingComponent) - .then((cf: ComponentFactory) => { - this._context[name].loadingRef = cf.create(this._injector); - this._context[name].times = 0; - this._mapOptions(options, this._context[name].loadingRef.instance); - let compRef: ILoadingRef = { - observable: this._registerLoadingComponent(name), - ref: this._context[name].loadingRef, - }; - resolve(compRef); - }); - }); + this._context[name].loadingRef = this._componentFactoryResolver + .resolveComponentFactory(TdLoadingComponent).create(this._injector); + this._context[name].times = 0; + this._mapOptions(options, this._context[name].loadingRef.instance); + let compRef: ILoadingRef = { + observable: this._registerLoadingComponent(name), + ref: this._context[name].loadingRef, + }; + return compRef; } /** From a149004bfb5d91531795e0a6094c97da9ff801f9 Mon Sep 17 00:00:00 2001 From: Ed Morales Date: Mon, 15 Aug 2016 22:50:21 -0700 Subject: [PATCH 4/8] updated loading docs --- .../components/loading/loading.component.html | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/app/components/components/loading/loading.component.html b/src/app/components/components/loading/loading.component.html index 1babdfbd8b..66cb1f643f 100644 --- a/src/app/components/components/loading/loading.component.html +++ b/src/app/components/components/loading/loading.component.html @@ -38,6 +38,7 @@

tdLoading

Simply add the tdLoading attibute with a "name" value to the element you want to mask.

Dont forget to add the asterisk syntax before the tdLoading directive if its not used in a ]]> element.

More info on the asterisk (*) syntax here

+

Note: when used on load, should be registered in [TdLoadingService] after 'AfterViewInit#ngAfterViewInit()' component hook cycle.

Properties:

The tdLoading component has {{loadingAttrs.length}} properties:

@@ -69,10 +70,11 @@

Example:

Typescript:

Example: Date: Mon, 15 Aug 2016 23:02:59 -0700 Subject: [PATCH 5/8] added max diameter and height for loading --- src/platform/core/loading/loading.component.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/platform/core/loading/loading.component.ts b/src/platform/core/loading/loading.component.ts index a5d4d1d30a..32915378ee 100644 --- a/src/platform/core/loading/loading.component.ts +++ b/src/platform/core/loading/loading.component.ts @@ -69,14 +69,17 @@ export class TdLoadingComponent { if (this.height) { return `${this.height}px`; } - return 'auto'; + return '150px'; } getCircleDiameter(): string { if (this.height) { - return `${this.height / 2}px`; + let diameter: number = this.height * (2 / 3); + if (diameter < 80) { + return `${diameter}px`; + } } - return 'auto'; + return '80px'; } isCircular(): boolean { From d225a16712f82be93df2e6e9b283538f78806f16 Mon Sep 17 00:00:00 2001 From: Ed Morales Date: Mon, 15 Aug 2016 23:04:12 -0700 Subject: [PATCH 6/8] called loading and change detection from start, added more docs --- .../components/loading/loading.component.ts | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/app/components/components/loading/loading.component.ts b/src/app/components/components/loading/loading.component.ts index db7ec5c0c4..09304fa039 100644 --- a/src/app/components/components/loading/loading.component.ts +++ b/src/app/components/components/loading/loading.component.ts @@ -1,5 +1,4 @@ -import { Component, ViewContainerRef } from '@angular/core'; -import { TimerWrapper } from '@angular/core/src/facade/async'; +import { Component, ViewContainerRef, AfterViewInit, ChangeDetectorRef } from '@angular/core'; import { MD_CARD_DIRECTIVES } from '@angular2-material/card'; import { MD_LIST_DIRECTIVES } from '@angular2-material/list'; @@ -24,7 +23,7 @@ import { TdHighlightComponent } from '../../../../platform/highlight'; styleUrls: [ 'loading.component.css' ], templateUrl: 'loading.component.html', }) -export class LoadingDemoComponent { +export class LoadingDemoComponent implements AfterViewInit { demo: {name?: string, description?: string} = {}; demo2: {name?: string, description?: string} = {}; @@ -41,13 +40,15 @@ export class LoadingDemoComponent { }]; loadingServiceMethods: Object[] = [{ - description: 'Registers a request for the loading mask referenced by the name parameter.', + description: `Registers a request for the loading mask referenced by the name parameter. + Can optionally pass registers argument to set a number of register calls.`, name: 'register', - type: 'function(name: string)', + type: 'function(name: string, registers: number = 1)', }, { - description: 'Resolves a request for the loading mask referenced by the name parameter.', + description: `Resolves a request for the loading mask referenced by the name parameter. + Can optionally pass resolves argument to set a number of resolve calls.`, name: 'resolve', - type: 'function(name: string)', + type: 'function(name: string, resolves: number = 1)', }, { description: `Creates a fullscreen loading mask and attaches it to the viewContainerRef. Only displayed when the mask has a request registered on it.`, @@ -55,7 +56,9 @@ export class LoadingDemoComponent { type: 'function(options: ILoadingOptions, viewContainerRef: ViewContainerRef)', }]; - constructor(viewContainer: ViewContainerRef, private _loadingService: TdLoadingService) { + constructor(viewContainer: ViewContainerRef, + private _changeDetectorRef: ChangeDetectorRef, + private _loadingService: TdLoadingService) { let options: ILoadingOptions = { name: 'test.overlay', type: LoadingType.Circular, @@ -68,28 +71,29 @@ export class LoadingDemoComponent { this._loadingService.createOverlayComponent(options2, viewContainer); } + ngAfterViewInit(): void { + this.registerLoadingReplace(); + this._changeDetectorRef.detectChanges(); + } + registerCircleLoadingOverlay(): void { this._loadingService.register('test.overlay'); - TimerWrapper.setTimeout( - () => { - this._loadingService.resolve('test.overlay'); - }, - 3000); + setTimeout(() => { + this._loadingService.resolve('test.overlay'); + }, 3000); } registerLinearLoadingOverlay(): void { this._loadingService.register('test.overlay2'); - TimerWrapper.setTimeout( - () => { - this._loadingService.resolve('test.overlay2'); - }, - 3000); + setTimeout(() => { + this._loadingService.resolve('test.overlay2'); + }, 3000); } registerLoadingReplace(): void { - this.replaceRegistered++; this._loadingService.register('test'); this._loadingService.register('test2'); + this.replaceRegistered++; } resolveLoadingReplace(): void { From 13007e30a12510b73188ca7ef28ea0c90333cf6b Mon Sep 17 00:00:00 2001 From: Ed Morales Date: Mon, 15 Aug 2016 23:39:01 -0700 Subject: [PATCH 7/8] fixed unit tests --- config/karma-test-shim.js | 7 +++++-- .../components/loading/loading.component.spec.ts | 10 ++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/config/karma-test-shim.js b/config/karma-test-shim.js index a2fb099eb4..9adc817cfd 100644 --- a/config/karma-test-shim.js +++ b/config/karma-test-shim.js @@ -38,14 +38,16 @@ System.import('system-config.js').then(function() { System.import('@angular/router'), System.import('@angular/http'), System.import('@angular/forms'), - System.import('@angular2-material/icon') + System.import('@angular/core'), + System.import('@angular2-material/icon'), ]).then(function (providers) { var testing = providers[0]; var testingBrowser = providers[1]; var testingRouter = providers[2]; var testingHttp = providers[3]; var testingForms = providers[4]; - var testingIcon = providers[5]; + var testingCore = providers[5]; + var testingIcon = providers[6]; testing.setBaseTestProviders(testingBrowser.TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS, testingBrowser.TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS ); @@ -59,6 +61,7 @@ System.import('system-config.js').then(function() { testingIcon.MdIconRegistry, { provide: testingRouter.Router, useValue: {} }, { provide: testingRouter.ActivatedRoute, useValue: {} }, + testingCore.ChangeDetectorRef, ]); }); }); diff --git a/src/app/components/components/loading/loading.component.spec.ts b/src/app/components/components/loading/loading.component.spec.ts index a36678f5a7..2136f266b5 100644 --- a/src/app/components/components/loading/loading.component.spec.ts +++ b/src/app/components/components/loading/loading.component.spec.ts @@ -7,10 +7,10 @@ import { inject, } from '@angular/core/testing'; import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing'; -import { Component, DebugElement, ViewContainerRef, Injector } from '@angular/core'; +import { Component, DebugElement, ViewContainerRef, Injector, ComponentFactoryResolver } from '@angular/core'; import { By } from '@angular/platform-browser'; import { LoadingDemoComponent } from './loading.component'; -import { TdLoadingService } from '../../../../platform/core'; +import { TdLoadingService, TD_LOADING_ENTRY_COMPONENTS } from '../../../../platform/core'; describe('Component: LoadingDemo', () => { let builder: TestComponentBuilder; @@ -21,6 +21,11 @@ describe('Component: LoadingDemo', () => { Injector, ViewContainerRef, TdLoadingService, + { provide: ComponentFactoryResolver, useValue: {resolveComponentFactory: function(): any{ + return {create: function(): any{ + return {instance: {}}; + }}; + }}}, ]); }); @@ -48,6 +53,7 @@ describe('Component: LoadingDemo', () => { template: ` `, + precompile: [ TD_LOADING_ENTRY_COMPONENTS ], }) class LoadingDemoTestControllerComponent { } From ef2e5db03115fb1f9932e1f92a6d77455c62022c Mon Sep 17 00:00:00 2001 From: Ed Morales Date: Tue, 16 Aug 2016 10:06:20 -0700 Subject: [PATCH 8/8] removed white brackground (will be added later as optional) --- src/platform/core/loading/loading.component.scss | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/platform/core/loading/loading.component.scss b/src/platform/core/loading/loading.component.scss index 1362e0130d..f3c7b74a40 100644 --- a/src/platform/core/loading/loading.component.scss +++ b/src/platform/core/loading/loading.component.scss @@ -15,9 +15,4 @@ right: 0; } } - md-progress-circle { - background-color: white; - border-radius: 40px; - box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.26); - } }