ngx-promise-buttons 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/.editorconfig +13 -0
  2. package/.github/FUNDING.yml +12 -0
  3. package/.travis.yml +21 -0
  4. package/CHANGELOG.md +0 -0
  5. package/CONTRIBUTING.md +36 -0
  6. package/README.md +12 -30
  7. package/angular.json +180 -0
  8. package/e2e/protractor.conf.js +32 -0
  9. package/e2e/src/app.e2e-spec.ts +23 -0
  10. package/e2e/src/app.po.ts +11 -0
  11. package/e2e/tsconfig.json +13 -0
  12. package/logo.png +0 -0
  13. package/package.json +87 -38
  14. package/projects/ngx-promise-buttons/karma.conf.js +32 -0
  15. package/projects/ngx-promise-buttons/ng-package.json +10 -0
  16. package/projects/ngx-promise-buttons/package.json +26 -0
  17. package/projects/ngx-promise-buttons/src/default-promise-btn-config.ts +10 -0
  18. package/projects/ngx-promise-buttons/src/index.ts +2 -0
  19. package/projects/ngx-promise-buttons/src/promise-btn-config.ts +7 -0
  20. package/projects/ngx-promise-buttons/src/promise-btn.directive.spec.ts +597 -0
  21. package/projects/ngx-promise-buttons/src/promise-btn.directive.ts +247 -0
  22. package/projects/ngx-promise-buttons/src/provider.ts +14 -0
  23. package/projects/ngx-promise-buttons/src/test.ts +16 -0
  24. package/projects/ngx-promise-buttons/src/user-cfg.ts +3 -0
  25. package/projects/ngx-promise-buttons/tsconfig.lib.json +27 -0
  26. package/projects/ngx-promise-buttons/tsconfig.lib.prod.json +10 -0
  27. package/projects/ngx-promise-buttons/tsconfig.spec.json +17 -0
  28. package/projects/ngx-promise-buttons/tslint.json +25 -0
  29. package/projects/ngx-promise-buttons/yarn.lock +9 -0
  30. package/projects/ngx-promise-buttons-demo/e2e/protractor.conf.js +30 -0
  31. package/projects/ngx-promise-buttons-demo/e2e/tsconfig.json +14 -0
  32. package/projects/ngx-promise-buttons-demo/karma.conf.js +56 -0
  33. package/projects/ngx-promise-buttons-demo/src/app/app.component.css +6 -0
  34. package/projects/ngx-promise-buttons-demo/src/app/app.component.html +109 -0
  35. package/projects/ngx-promise-buttons-demo/src/app/app.component.spec.ts +29 -0
  36. package/projects/ngx-promise-buttons-demo/src/app/app.component.ts +208 -0
  37. package/projects/ngx-promise-buttons-demo/src/assets/.gitkeep +0 -0
  38. package/projects/ngx-promise-buttons-demo/src/environments/environment.prod.ts +3 -0
  39. package/projects/ngx-promise-buttons-demo/src/environments/environment.ts +8 -0
  40. package/projects/ngx-promise-buttons-demo/src/favicon.ico +0 -0
  41. package/projects/ngx-promise-buttons-demo/src/index.html +72 -0
  42. package/projects/ngx-promise-buttons-demo/src/main.ts +11 -0
  43. package/projects/ngx-promise-buttons-demo/src/polyfills.ts +63 -0
  44. package/projects/ngx-promise-buttons-demo/src/styles.scss +135 -0
  45. package/projects/ngx-promise-buttons-demo/tsconfig.app.json +14 -0
  46. package/projects/ngx-promise-buttons-demo/tsconfig.spec.json +18 -0
  47. package/projects/ngx-promise-buttons-demo/tslint.json +156 -0
  48. package/scripts/copy-readme-to-demo.js +15 -0
  49. package/tsconfig.build-lib.json +32 -0
  50. package/tsconfig.json +41 -0
  51. package/wallaby.js +90 -0
  52. package/fesm2022/ngx-promise-buttons.mjs +0 -261
  53. package/fesm2022/ngx-promise-buttons.mjs.map +0 -1
  54. package/types/ngx-promise-buttons.d.ts +0 -79
@@ -0,0 +1,247 @@
1
+ import {AfterContentInit, Directive, ElementRef, HostListener, Inject, Input, OnDestroy, Optional} from '@angular/core';
2
+ import {Observable, Subscription} from 'rxjs';
3
+ import {DEFAULT_CFG} from './default-promise-btn-config';
4
+ import {PromiseBtnConfig} from './promise-btn-config';
5
+ import {USER_CFG} from "./provider";
6
+
7
+ @Directive({
8
+ selector: '[promiseBtn]',
9
+ })
10
+
11
+ export class PromiseBtnDirective implements OnDestroy, AfterContentInit {
12
+ cfg: PromiseBtnConfig;
13
+ // the timeout used for min duration display
14
+ minDurationTimeout: number;
15
+ // boolean to determine minDurationTimeout state
16
+ isMinDurationTimeoutDone: boolean;
17
+ // boolean to determine if promise was resolved
18
+ isPromiseDone: boolean;
19
+ // the promise button button element
20
+ btnEl: HTMLElement;
21
+ // the promise itself or a function expression
22
+ // NOTE: we need the type any here as we might deal with custom promises like bluebird
23
+ promise: any;
24
+
25
+ // this is added to fix the overriding of the disabled state by the loading indicator button.
26
+ @Input('disabled')
27
+ set isDisabledFromTheOutsideSetter(v: boolean) {
28
+ this.isDisabledFromTheOutside = v;
29
+ if (v) {
30
+ // disabled means always disabled
31
+ this.btnEl.setAttribute('disabled', 'disabled');
32
+ } else if (this.isPromiseDone || this.isPromiseDone === undefined) {
33
+ this.btnEl.removeAttribute('disabled');
34
+ }
35
+ // else the button is loading, so do not change the disabled loading state.
36
+ }
37
+
38
+ isDisabledFromTheOutside: boolean;
39
+
40
+ private _fakePromiseResolve: (value: void) => void;
41
+
42
+ constructor(el: ElementRef,
43
+ @Optional() @Inject(USER_CFG) cfg?: PromiseBtnConfig) {
44
+ // provide configuration
45
+ this.cfg = Object.assign({}, DEFAULT_CFG, cfg);
46
+
47
+ // save element
48
+ this.btnEl = el.nativeElement;
49
+ }
50
+
51
+ @Input()
52
+ set promiseBtn(passedValue: any) {
53
+ const isObservable: boolean = passedValue instanceof Observable;
54
+ const isSubscription: boolean = passedValue instanceof Subscription;
55
+ const isBoolean: boolean = typeof passedValue === 'boolean';
56
+ const isPromise: boolean = passedValue instanceof Promise || (
57
+ passedValue !== null &&
58
+ typeof passedValue === 'object' &&
59
+ typeof passedValue.then === 'function' &&
60
+ typeof passedValue.catch === 'function'
61
+ );
62
+
63
+ if (isObservable) {
64
+ throw new TypeError('promiseBtn must be an instance of Subscription, instance of Observable given');
65
+ } else if (isSubscription) {
66
+ const sub: Subscription = passedValue;
67
+ if (!sub.closed) {
68
+ this.promise = new Promise((resolve) => {
69
+ sub.add(resolve);
70
+ });
71
+ }
72
+ } else if (isPromise) {
73
+ this.promise = passedValue;
74
+ } else if (isBoolean) {
75
+ this.promise = this.createPromiseFromBoolean(passedValue);
76
+ }
77
+
78
+ this.checkAndInitPromiseHandler(this.btnEl);
79
+ }
80
+
81
+ ngAfterContentInit() {
82
+ this.prepareBtnEl(this.btnEl);
83
+ // trigger changes once to handle initial promises
84
+ this.checkAndInitPromiseHandler(this.btnEl);
85
+ }
86
+
87
+ ngOnDestroy() {
88
+ // cleanup
89
+ if (this.minDurationTimeout) {
90
+ clearTimeout(this.minDurationTimeout);
91
+ }
92
+ }
93
+
94
+ createPromiseFromBoolean(val: boolean): Promise<any> {
95
+ if (val) {
96
+ return new Promise((resolve) => {
97
+ this._fakePromiseResolve = resolve;
98
+ });
99
+ } else {
100
+ if (this._fakePromiseResolve) {
101
+ this._fakePromiseResolve();
102
+ }
103
+ return this.promise;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Initializes all html and event handlers
109
+ */
110
+ prepareBtnEl(btnEl: HTMLElement) {
111
+ // handle promises passed via promiseBtn attribute
112
+ this.appendSpinnerTpl(btnEl);
113
+ }
114
+
115
+ /**
116
+ * Checks if all required parameters are there and inits the promise handler
117
+ */
118
+ checkAndInitPromiseHandler(btnEl: HTMLElement) {
119
+ // check if element and promise is set
120
+ if (btnEl && this.promise) {
121
+ this.initPromiseHandler(btnEl);
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Helper FN to add class
127
+ */
128
+ addLoadingClass(el: any) {
129
+ if (typeof this.cfg.btnLoadingClass === 'string') {
130
+ el.classList.add(this.cfg.btnLoadingClass);
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Helper FN to remove classes
136
+ */
137
+ removeLoadingClass(el: any) {
138
+ if (typeof this.cfg.btnLoadingClass === 'string') {
139
+ el.classList.remove(this.cfg.btnLoadingClass);
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Handles everything to be triggered when the button is set
145
+ * to loading state.
146
+ */
147
+ initLoadingState(btnEl: HTMLElement) {
148
+ this.addLoadingClass(btnEl);
149
+ this.disableBtn(btnEl);
150
+ }
151
+
152
+ /**
153
+ * Handles everything to be triggered when loading is finished
154
+ */
155
+ cancelLoadingStateIfPromiseAndMinDurationDone(btnEl: HTMLElement) {
156
+ if ((!this.cfg.minDuration || this.isMinDurationTimeoutDone) && this.isPromiseDone) {
157
+ this.removeLoadingClass(btnEl);
158
+ this.enableBtn(btnEl);
159
+ }
160
+ }
161
+
162
+ disableBtn(btnEl: HTMLElement) {
163
+ if (this.cfg.disableBtn) {
164
+ btnEl.setAttribute('disabled', 'disabled');
165
+ }
166
+ }
167
+
168
+ enableBtn(btnEl: HTMLElement) {
169
+ if (this.cfg.disableBtn) {
170
+ if (this.isDisabledFromTheOutside) {
171
+ btnEl.setAttribute('disabled', 'disabled');
172
+ } else {
173
+ btnEl.removeAttribute('disabled');
174
+ }
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Initializes a watcher for the promise. Also takes
180
+ * this.cfg.minDuration into account if given.
181
+ */
182
+
183
+ initPromiseHandler(btnEl: HTMLElement) {
184
+ const promise = this.promise;
185
+
186
+ // watch promise to resolve or fail
187
+ this.isMinDurationTimeoutDone = false;
188
+ this.isPromiseDone = false;
189
+
190
+ // create timeout if option is set
191
+ if (this.cfg.minDuration) {
192
+ this.minDurationTimeout = window.setTimeout(() => {
193
+ this.isMinDurationTimeoutDone = true;
194
+ this.cancelLoadingStateIfPromiseAndMinDurationDone(btnEl);
195
+ }, this.cfg.minDuration);
196
+ }
197
+
198
+ const resolveLoadingState = () => {
199
+ this.isPromiseDone = true;
200
+ this.cancelLoadingStateIfPromiseAndMinDurationDone(btnEl);
201
+ };
202
+
203
+ if (!this.cfg.handleCurrentBtnOnly) {
204
+ this.initLoadingState(btnEl);
205
+ }
206
+ // native Promise doesn't have finally
207
+ if (promise.finally) {
208
+ promise.finally(resolveLoadingState);
209
+ } else {
210
+ promise
211
+ .then(resolveLoadingState)
212
+ .catch(resolveLoadingState);
213
+ }
214
+
215
+ }
216
+
217
+
218
+ /**
219
+ * $compile and append the spinner template to the button.
220
+ */
221
+ appendSpinnerTpl(btnEl: HTMLElement) {
222
+ // TODO add some kind of compilation later on
223
+ btnEl.insertAdjacentHTML('beforeend', this.cfg.spinnerTpl as string);
224
+ }
225
+
226
+ /**
227
+ * Limit loading state to show only for the currently clicked button.
228
+ * Executed only if this.cfg.handleCurrentBtnOnly is set
229
+ */
230
+ @HostListener('click')
231
+ handleCurrentBtnOnly() {
232
+ if (!this.cfg.handleCurrentBtnOnly) {
233
+ return true; // return true for testing
234
+ }
235
+
236
+ // Click triggers @Input update
237
+ // We need to use timeout to wait for @Input to update
238
+ window.setTimeout(() => {
239
+ // return if something else than a promise is passed
240
+ if (!this.promise) {
241
+ return;
242
+ }
243
+
244
+ this.initLoadingState(this.btnEl);
245
+ }, 0);
246
+ }
247
+ }
@@ -0,0 +1,14 @@
1
+ import { InjectionToken, EnvironmentProviders, makeEnvironmentProviders, Optional, SkipSelf, inject } from '@angular/core';
2
+ import { PromiseBtnConfig } from './promise-btn-config';
3
+
4
+ export const USER_CFG = new InjectionToken<PromiseBtnConfig>('Promise Button Config');
5
+
6
+ export const DEFAULT_CONFIG: PromiseBtnConfig = {
7
+ // default values
8
+ };
9
+
10
+ export function provideNgxPromiseButtons(config: PromiseBtnConfig = DEFAULT_CONFIG): EnvironmentProviders {
11
+ return makeEnvironmentProviders([
12
+ { provide: USER_CFG, useValue: { ...DEFAULT_CONFIG, ...config } }
13
+ ]);
14
+ }
@@ -0,0 +1,16 @@
1
+ // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2
+
3
+ import 'zone.js';
4
+ import 'zone.js/dist/async-test.js';
5
+ import 'zone.js/dist/proxy.js';
6
+ import 'zone.js/dist/sync-test';
7
+ import 'zone.js/dist/jasmine-patch';
8
+
9
+ import {getTestBed} from '@angular/core/testing';
10
+ import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing';
11
+
12
+ // First, initialize the Angular testing environment.
13
+ getTestBed().initTestEnvironment(
14
+ BrowserDynamicTestingModule,
15
+ platformBrowserDynamicTesting()
16
+ );
@@ -0,0 +1,3 @@
1
+ import {InjectionToken} from '@angular/core';
2
+
3
+ export const userCfg = new InjectionToken('cfg');
@@ -0,0 +1,27 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../out-tsc/lib",
5
+ "declaration": true,
6
+ "inlineSources": true,
7
+ "types": [],
8
+ "lib": [
9
+ "dom",
10
+ "es2018"
11
+ ]
12
+ },
13
+ "angularCompilerOptions": {
14
+ "annotateForClosureCompiler": false,
15
+ "skipTemplateCodegen": true,
16
+ "strictMetadataEmit": true,
17
+ "fullTemplateTypeCheck": true,
18
+ "strictInjectionParameters": true,
19
+ "enableResourceInlining": true,
20
+ "enableIvy": true,
21
+ "allowEmptyCodegenFiles": true,
22
+ },
23
+ "exclude": [
24
+ "src/test.ts",
25
+ "**/*.spec.ts"
26
+ ]
27
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "./tsconfig.lib.json",
3
+ "compilerOptions": {
4
+ "declarationMap": false
5
+ },
6
+ "angularCompilerOptions": {
7
+ "allowEmptyCodegenFiles": true,
8
+ "compilationMode": "partial"
9
+ }
10
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../out-tsc/spec",
5
+ "types": [
6
+ "jasmine",
7
+ "node"
8
+ ]
9
+ },
10
+ "files": [
11
+ "src/test.ts"
12
+ ],
13
+ "include": [
14
+ "**/*.spec.ts",
15
+ "**/*.d.ts"
16
+ ]
17
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "extends": "../ngx-promise-buttons-demo/tslint.json",
3
+ "rules": {
4
+ "directive-selector": [
5
+ false,
6
+ "attribute",
7
+ "lib",
8
+ "camelCase"
9
+ ],
10
+ "component-selector": [
11
+ false,
12
+ "element",
13
+ "lib",
14
+ "kebab-case"
15
+ ],
16
+ "variable-name": [
17
+ true,
18
+ "allow-pascal-case",
19
+ "allow-snake-case",
20
+ "ban-keywords",
21
+ "check-format",
22
+ "allow-leading-underscore"
23
+ ]
24
+ }
25
+ }
@@ -0,0 +1,9 @@
1
+ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2
+ # yarn lockfile v1
3
+
4
+
5
+ "@ctrl/tinycolor@^2.6.0":
6
+ version "2.6.0"
7
+ resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-2.6.0.tgz#c269def5ed1f871913f299a475c01bdb39119eee"
8
+ integrity sha512-bvkszNAcbmR2zrjjkaHbTVbEj07Id44HsBWf57mugPcvJNIPaWLqxWV/GUJVJuXXayqFP2X09cZRqKrCy/v10Q==
9
+
@@ -0,0 +1,30 @@
1
+ // Protractor configuration file, see link for more information
2
+ // https://github.com/angular/protractor/blob/master/lib/config.ts
3
+
4
+ const { SpecReporter } = require('jasmine-spec-reporter');
5
+
6
+ exports.config = {
7
+ allScriptsTimeout: 11000,
8
+ specs: [
9
+ './e2e/**/*.e2e-spec.ts'
10
+ ],
11
+ capabilities: {
12
+ 'browserName': 'chrome'
13
+ },
14
+ directConnect: true,
15
+ baseUrl: 'http://localhost:4200/',
16
+ framework: 'jasmine',
17
+ jasmineNodeOpts: {
18
+ showColors: true,
19
+ defaultTimeoutInterval: 30000,
20
+ print: function() {}
21
+ },
22
+ beforeLaunch: function() {
23
+ require('ts-node').register({
24
+ project: 'e2e/tsconfig.e2e.json'
25
+ });
26
+ },
27
+ onPrepare() {
28
+ jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
29
+ }
30
+ };
@@ -0,0 +1,14 @@
1
+ /* To learn more about this file see: https://angular.io/config/tsconfig. */
2
+ {
3
+ "extends": "../../../tsconfig.json",
4
+ "compilerOptions": {
5
+ "outDir": "../../../out-tsc/e2e",
6
+ "module": "commonjs",
7
+ "target": "es2018",
8
+ "types": [
9
+ "jasmine",
10
+ "jasminewd2",
11
+ "node"
12
+ ]
13
+ }
14
+ }
@@ -0,0 +1,56 @@
1
+ // Karma configuration file, see link for more information
2
+ // https://karma-runner.github.io/1.0/config/configuration-file.html
3
+
4
+ module.exports = function (config) {
5
+ config.set({
6
+ basePath: '../..',
7
+ frameworks: ['jasmine', '@angular-devkit/build-angular'],
8
+ plugins: [
9
+ require('karma-jasmine'),
10
+ require('karma-chrome-launcher'),
11
+ require('karma-jasmine-html-reporter'),
12
+ require('karma-coverage-istanbul-reporter'),
13
+ require('@angular-devkit/build-angular/plugins/karma')
14
+ ],
15
+ client: {
16
+ clearContext: false // leave Jasmine Spec Runner output visible in browser
17
+ },
18
+ coverageIstanbulReporter: {
19
+ dir: require('path').join(__dirname, '../coverage'),
20
+ reports: ['html', 'lcovonly'],
21
+ fixWebpackSourcePaths: true
22
+ },
23
+ reporters: ['progress', 'kjhtml'],
24
+ port: 9876,
25
+ colors: true,
26
+ logLevel: config.LOG_INFO,
27
+ autoWatch: false,
28
+ browsers: ['ChromeHeadless'],
29
+ singleRun: true,
30
+ customLaunchers: {
31
+ 'ChromeHeadless': {
32
+ base: 'Chrome',
33
+ flags: [
34
+ // We must disable the Chrome sandbox when running Chrome inside Docker
35
+ // (Chrome's sandbox needs more permissions than Docker allows by default)
36
+ '--headless',
37
+ '--no-sandbox',
38
+ '--disable-gpu',
39
+ '--no-default-browser-check',
40
+ '--no-first-run',
41
+ '--disable-default-apps',
42
+ '--disable-popup-blocking',
43
+ '--disable-translate',
44
+ '--disable-background-timer-throttling',
45
+ '--disable-renderer-backgrounding',
46
+ '--disable-device-discovery-notifications',
47
+ // Without a remote debugging port, Google Chrome exits immediately.
48
+ '--remote-debugging-port=9222',
49
+ '--disable-web-security',
50
+ ],
51
+ debug: true
52
+ }
53
+ },
54
+ browserNoActivityTimeout: 120000,
55
+ });
56
+ };
@@ -0,0 +1,6 @@
1
+ h2 {
2
+ margin-top: 16px;
3
+ }
4
+ h3 {
5
+ margin-top: 16px;
6
+ }
@@ -0,0 +1,109 @@
1
+ <button class="btn btn-raised"
2
+ (click)="success($event)"
3
+ [promiseBtn]="successPromise">Success after delay
4
+ </button>
5
+ <button class="btn btn-raised"
6
+ (click)="error()"
7
+ [promiseBtn]="errorPromise">Error after delay
8
+ </button>
9
+ <br>
10
+ <br>
11
+ <button class="btn btn-raised"
12
+ (click)="endless()"
13
+ [promiseBtn]="endlessPromise">Never resolving promise
14
+ </button>
15
+ <button class="btn btn-raised"
16
+ [promiseBtn]="endlessInitialPromise">Loading initially and forever
17
+ </button>
18
+
19
+ <h3>Same promise buttons</h3>
20
+ <button class="btn btn-raised"
21
+ (click)="success($event)"
22
+ [promiseBtn]="successPromise">We
23
+ </button>
24
+ <button class="btn btn-raised"
25
+ (click)="success($event)"
26
+ [promiseBtn]="successPromise">share
27
+ </button>
28
+
29
+ <h3>Chained promise buttons</h3>
30
+ <button class="btn btn-raised"
31
+ (click)="chain()"
32
+ [promiseBtn]="chainedPromises">Load promise chain {{ promiseIndex }}
33
+ </button>
34
+
35
+
36
+ <h3>Inside a form</h3>
37
+ <form (submit)="submit()"
38
+ novalidate>
39
+ <button class="btn btn-raised"
40
+ type="submit"
41
+ [promiseBtn]="submitPromise">Submit
42
+ </button>
43
+ </form>
44
+
45
+ <div>
46
+ <h2>Observable</h2>
47
+
48
+ <button class="btn btn-raised"
49
+ (click)="initSuccessObservable()"
50
+ [promiseBtn]="successObservable">Success after delay
51
+ </button>
52
+ <button class="btn btn-raised"
53
+ (click)="initErrorObservable()"
54
+ [promiseBtn]="errorObservable">Error after delay
55
+ </button>
56
+ <br>
57
+ <br>
58
+ <button class="btn btn-raised"
59
+ (click)="initEndlessObservable()"
60
+ [promiseBtn]="endlessObservable">Never resolving Observable
61
+ </button>
62
+ <button class="btn btn-raised"
63
+ [promiseBtn]="endlessInitialObservable">Loading initially and forever
64
+ </button>
65
+
66
+ <h3>Same observable buttons</h3>
67
+ <button class="btn btn-raised"
68
+ (click)="initSuccessObservable()"
69
+ [promiseBtn]="successObservable">We
70
+ </button>
71
+ <button class="btn btn-raised"
72
+ (click)="initSuccessObservable()"
73
+ [promiseBtn]="successObservable">share
74
+ </button>
75
+
76
+ <h3>Chained observable button</h3>
77
+ <button class="btn btn-raised"
78
+ (click)="initChainedObservable()"
79
+ [promiseBtn]="chainedObservable">Observable chain {{ chainedObservableValue }}
80
+ </button>
81
+ </div>
82
+
83
+ <div>
84
+ <h2>Simple Boolean</h2>
85
+ <button class="btn btn-raised"
86
+ (click)="myBool=!myBool">Toggle loading ({{myBool}})
87
+ </button>
88
+
89
+ <button class="btn btn-raised"
90
+ [promiseBtn]="myBool">My Boolean Btn {{myBool}}
91
+ </button>
92
+ </div>
93
+
94
+
95
+ <div>
96
+ <h2>Dynamic [disabled] </h2>
97
+
98
+ <button class="btn btn-raised"
99
+ (click)="isOutsideDisabled=!isOutsideDisabled">Toggle disabled via [disabled]={{isOutsideDisabled}}
100
+ </button>
101
+
102
+ <button class="btn btn-raised"
103
+ (click)="myBoolWithCustomDisabled=!myBoolWithCustomDisabled">Toggle promise button
104
+ </button>
105
+
106
+ <button class="btn btn-raised"
107
+ [disabled]="isOutsideDisabled"
108
+ [promiseBtn]="myBoolWithCustomDisabled">This is the button</button>
109
+ </div>
@@ -0,0 +1,29 @@
1
+ import {async, TestBed} from '@angular/core/testing';
2
+ import {AppComponent} from './app.component';
3
+ import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
4
+
5
+ // @Directive({
6
+ // selector: '[promiseBtn]'
7
+ // })
8
+ // class MockPromiseBtnDirective {
9
+ // @Input('promiseBtn') promise: Promise<any>;
10
+ // }
11
+
12
+ describe('AppComponent', () => {
13
+ beforeEach(async(() => {
14
+ TestBed.configureTestingModule({
15
+ declarations: [
16
+ AppComponent,
17
+ // MockPromiseBtnDirective
18
+ ],
19
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
20
+ }).compileComponents();
21
+ }));
22
+
23
+ it('should create the app', async(() => {
24
+ const fixture = TestBed.createComponent(AppComponent);
25
+ const app = fixture.debugElement.componentInstance;
26
+ expect(app).toBeTruthy();
27
+ }));
28
+ });
29
+