Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Progress bar stays loading forever (until completed is called) and an… #62

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export class SharedModule {
#### 3. Use the `SlimLoadingBarService` for your application
- Import `SlimLoadingBarService` from `ng2-slim-loading-bar` in your application code:

```js
```typescript
import {Component} from '@angular/core';
import {SlimLoadingBarService} from 'ng2-slim-loading-bar';

Expand All @@ -98,9 +98,7 @@ export class AppComponent {
constructor(private slimLoadingBarService: SlimLoadingBarService) { }

startLoading() {
this.slimLoadingBarService.start(() => {
console.log('Loading complete');
});
this.slimLoadingBarService.start();
}

stopLoading() {
Expand All @@ -118,6 +116,7 @@ You can use the following properties to customize the `ng2-slim-loading-bar` com
- `color` - The color of loading bar. Default is `firebrick`. It can be any CSS compatible value.
- `height` - The height of loading bar. Default value is `2px`.
- `show` - The flag helps hide and show the loading bar. Default value is `true`.
- `transitionTimingFunction` - The transition timing function the bar will use when incrementing the progress in the bar. Default value is `'linear'`.

Example:
`<ng2-slim-loading-bar color="blue" height="4px"></ng2-slim-loading-bar>`
Expand All @@ -127,6 +126,7 @@ You can use the following properties to customize the SlimLoadingBar via instanc
- `color` - The color of loading bar.
- `height` - The height of loading bar.
- `visible` - The flag helps hide and show the loading bar, false for hidden and true for visible.
- `growSpeed` - The speed of the progress bar when start is called. It can be any number grater than 0, but. It is 0.1 by default.

You can use the following methods to control the SlimLoadingBar via instance of SlimLoadingBarService:
- `start` - Start the loading progress. Use the callback function as an parameter to listed the complete event.
Expand All @@ -141,8 +141,8 @@ You can hook up with our different types of events thrown.
- `SlimLoadingBarEventType.COLOR`
- `SlimLoadingBarEventType.VISIBLE`

you can subscribe to these events types by simplying doing this
```js
you can subscribe to these events types simply by doing this
```typescript
constructor(private _loadingBar: SlimLoadingBarService) {
this._loadingBar.events.subscribe((item:SlimLoadingBarEvent) => console.log(item));
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"@angular/core": "^2.4.7 || ^4.3.0"
},
"devDependencies": {
"@angular/common": "^2.4.7 || ^4.3.0",
"@angular/common": "^2.4.7",
"@angular/compiler": "^2.4.7",
"@angular/compiler-cli": "^2.4.7",
"@angular/core": "^2.4.7",
Expand Down
9 changes: 6 additions & 3 deletions src/slim-loading-bar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { Component, Input, OnInit, AfterViewInit, ChangeDetectorRef, ChangeDetectionStrategy, ElementRef } from '@angular/core';

import { SlimLoadingBarService, SlimLoadingBarEvent, SlimLoadingBarEventType } from './slim-loading-bar.service';
import { SlimLoadingBarService, SlimLoadingBarEvent, SlimLoadingBarEventType, TransitionTimingFunction } from './slim-loading-bar.service';
import { isPresent } from './slim-loading-bar.utils';

/**
Expand All @@ -25,7 +25,7 @@ export class SlimLoadingBarComponent implements OnInit, AfterViewInit {

private _progress: string = '0';
@Input() set progress(progress: string) {
this.isTransition = progress >= this._progress ? 'all 0.5s ease-in-out' : 'none';
this.isTransition = progress >= this._progress ? `all ${this.service.interval}ms ${this.transitionTimingFunction}` : 'none';
this._progress = progress;
}

Expand All @@ -36,6 +36,7 @@ export class SlimLoadingBarComponent implements OnInit, AfterViewInit {
@Input() color: string = 'firebrick';
@Input() height: string = '2px';
@Input() show: boolean = true;
@Input() transitionTimingFunction: TransitionTimingFunction = 'linear';

constructor(public service: SlimLoadingBarService, private _elmRef: ElementRef, private _changeDetectorRef: ChangeDetectorRef) { }

Expand All @@ -49,6 +50,8 @@ export class SlimLoadingBarComponent implements OnInit, AfterViewInit {
this.height = event.value;
} else if (event.type === SlimLoadingBarEventType.VISIBLE) {
this.show = event.value;
} else if (event.type === SlimLoadingBarEventType.TRANSITION_TIMING_FUNCTION) {
this.transitionTimingFunction = event.value;
}
});
}
Expand All @@ -58,5 +61,5 @@ export class SlimLoadingBarComponent implements OnInit, AfterViewInit {
this._elmRef.nativeElement.visible = event.type === SlimLoadingBarEventType.VISIBLE ? event.value : true;
this._changeDetectorRef.detectChanges();
});
}
}
}
60 changes: 43 additions & 17 deletions src/slim-loading-bar.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ export enum SlimLoadingBarEventType {
PROGRESS,
HEIGHT,
COLOR,
VISIBLE
VISIBLE,
TRANSITION_TIMING_FUNCTION
}

export type TransitionTimingFunction = 'linear' | 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | string;

export class SlimLoadingBarEvent {
constructor(public type:SlimLoadingBarEventType, public value:any) {}
}
Expand All @@ -25,13 +28,17 @@ export class SlimLoadingBarEvent {
@Injectable()
export class SlimLoadingBarService {

private _progress:number = 0;
private _height:string = '2px';
private _color:string = 'firebrick';
private _visible:boolean = true;
private _progress: number = 0;
private _height: string = '2px';
private _color: string = 'firebrick';
private _visible: boolean = true;
private _transitionTimingFunction: TransitionTimingFunction = 'linear';

private _intervalCounterId: any = 0;
public interval: number = 500; // in milliseconds

private _intervalCounterId:any = 0;
public interval:number = 500; // in milliseconds
private growMagnitude: number = 0;
public growSpeed: number = 1 / 10;

private eventSource: Subject<SlimLoadingBarEvent> = new Subject<SlimLoadingBarEvent>();
public events: Observable<SlimLoadingBarEvent> = this.eventSource.asObservable();
Expand Down Expand Up @@ -86,6 +93,17 @@ export class SlimLoadingBarService {
return this._visible;
}

set transitionTimingFunction(value: TransitionTimingFunction) {
if (isPresent(value)) {
this._transitionTimingFunction = value;
this.emitEvent(new SlimLoadingBarEvent(SlimLoadingBarEventType.TRANSITION_TIMING_FUNCTION, this._transitionTimingFunction));
}
}

get transitionTimingFunction(): TransitionTimingFunction {
return this._transitionTimingFunction;
}

private emitEvent(event: SlimLoadingBarEvent) {
if (this.eventSource) {
// Push up a new event
Expand All @@ -94,20 +112,13 @@ export class SlimLoadingBarService {
}


start(onCompleted:Function = null) {
start() {
// Stop current timer
this.stop();
// Make it visible for sure
this.visible = true;
// Run the timer with milliseconds iterval
this._intervalCounterId = setInterval(() => {
// Increment the progress and update view component
this.progress++;
// If the progress is 100% - call complete
if (this.progress === 100) {
this.complete(onCompleted);
}
}, this.interval);
// Run the timer with milliseconds interval
this._intervalCounterId = setInterval(this.growProgress.bind(this), this.interval);
}

stop() {
Expand All @@ -124,6 +135,7 @@ export class SlimLoadingBarService {

complete(onCompleted: Function = null) {
this.progress = 100;
this.growMagnitude = 0;
this.stop();
setTimeout(() => {
// Hide it away
Expand All @@ -138,6 +150,20 @@ export class SlimLoadingBarService {
}, 250);
}

protected growProgress() {
this.growMagnitude += 1;
// for all n >= 1
// progress = sum ((n / (n + 1))^k ) / n , k=1 to m) * 100%
// let m = Infinity
// progress = 100%
// Math ....
this.progress += this.getIncrement(this.growMagnitude) * 100;
}

protected getIncrement(growMagnitude: number) {
const growRateInverse = 1 / this.growSpeed;
// (n / (n + 1))^k ) / n, where k controls how big is the increment ans ensures the infinite sum produces 1 as result
return ((growRateInverse / (growRateInverse + 1)) ** growMagnitude) / growRateInverse;
}
}

11 changes: 9 additions & 2 deletions tests/slim-loading-bar.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { TestBed, ComponentFixture }
from '@angular/core/testing';

import {SlimLoadingBarService}
import {SlimLoadingBarService}
from '../src/slim-loading-bar.service';
import {SlimLoadingBarComponent}
import {SlimLoadingBarComponent}
from '../src/slim-loading-bar.component';

describe('SlimLoadingBar', () => {
Expand Down Expand Up @@ -78,4 +78,11 @@ describe('SlimLoadingBar', () => {
componentFixture.detectChanges();
expect(progressDiv.style.opacity).toBe('1');
});

it ('should change the transition timing function when calling set transitionTimingFunction', () => {
component.progress = '30';
component.transitionTimingFunction = 'ease-in-out';
componentFixture.detectChanges();
expect(progressDiv.style.transition).toContain('ease-in-out');
});
});
58 changes: 56 additions & 2 deletions tests/slim-loading-bar.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,59 @@ describe('SlimLoadingBarService', () => {
expect(service.progress).toBe(30);
});

it('increaments over time after calling start()', <any>fakeAsync((): void => {
it('increments over time after calling start()', <any>fakeAsync((): void => {
// var value, flag;
expect(service.progress).toBe(0);
service.start();

tick(500);
expect(service.progress).toBe(1);
expect(service.progress).toBeGreaterThan(0);
service.stop();
}));

it('is not completed never after calling start() if no other action is taken', <any>fakeAsync((): void => {
// var value, flag;
expect(service.progress).toBe(0);
service.start();

tick(10 * 60 * 1000); // 10 minutes
expect(service.progress).toBeLessThan(100);
service.stop();
}));

it('grows in an infinite sum fashion (0.5, 0.75, 0.875, ...) after calling start().', <any>fakeAsync((): void => {
// var value, flag;
expect(service.progress).toBe(0);
service.growSpeed = 1 / 10;
service.start();

tick(500);
expect(service.progress).toBeCloseTo(100 * (1 / 11), 1 / 1000);

tick(500);
expect(service.progress).toBeCloseTo(100 * (21 / 121), 1 / 1000);

tick(38 * 500);
expect(service.progress).toBeCloseTo(97.7905, 1 / 1000);

service.stop();
}));

it('grow rate after calling calling start() is based in growSpeed.', <any>fakeAsync((): void => {
// var value, flag;
expect(service.progress).toBe(0);
service.growSpeed = 1;
service.start();

tick(500);
expect(service.progress).toBeCloseTo(100 * (1 / 2), 1 / 1000);

tick(500);
expect(service.progress).toBeCloseTo(100 * (3 / 4), 1 / 1000);

tick(8 * 500);
expect(service.progress).toBeCloseTo(100 * (1023 / 1024), 1 / 1000);

service.stop();
}));

Expand Down Expand Up @@ -78,4 +124,12 @@ describe('SlimLoadingBarService', () => {
expect(service.color).toBe('green');
});

it('return current transition timing function ', () => {
expect(service.transitionTimingFunction).toBe('linear');
});
it('set the transition timing function', () => {
service.transitionTimingFunction = 'ease-in-out';
expect(service.transitionTimingFunction).toBe('ease-in-out');
});

});