Skip to content
This repository was archived by the owner on Nov 3, 2024. It is now read-only.

Commit e5f3793

Browse files
fix(command): initial flicker (#75)
1 parent 0875386 commit e5f3793

File tree

7 files changed

+67
-17
lines changed

7 files changed

+67
-17
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
<!-- ## [vNext](https://github.com/sktch7/ngx.command/compare/2.0.0...3.0.0) (2020-x-x) -->
22

3+
## [2.2.0](https://github.com/sketch7/ngx.command/compare/2.1.0...2.1.1) (2022-11-02)
4+
5+
### Bug Fixes
6+
7+
- **command:** avoid set disabled initially + remove delay when setting in order to avoid flickers - by default, can be optionally changed via `hasDisabledDelay` when needed for example with material
8+
39
## [2.1.1](https://github.com/sketch7/ngx.command/compare/2.1.0...2.1.1) (2022-09-26)
410

511
### Bug Fixes

examples/projects/example-app/src/app/app.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ const materialModules = [
2929
BrowserAnimationsModule,
3030

3131
SsvCommandModule.forRoot({
32-
executingCssClass: "is-busy"
32+
executingCssClass: "is-busy",
33+
hasDisabledDelay: false
3334
}),
3435
materialModules,
3536
],

examples/projects/example-app/src/app/command/example-command.component.html

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,41 @@ <h1>Command</h1>
8686

8787
<button mat-raised-button
8888
color="primary"
89+
disabled
8990
[ssvCommand]="saveCmd">
9091
<i *ngIf="saveCmd.isExecuting"
9192
class="ai-circled ai-indicator ai-dark-spin small"></i>
9293
Save
9394
<small>with [ssvCommand]</small>
9495
</button>
9596

97+
<button color="primary"
98+
[ssvCommand]="saveCmd">
99+
<i *ngIf="saveCmd.isExecuting"
100+
class="ai-circled ai-indicator ai-dark-spin small"></i>
101+
Save
102+
<small>with [ssvCommand]</small>
103+
</button>
104+
105+
<button color="primary"
106+
[ssvCommand]="saveCmdNoValidation">
107+
<i *ngIf="saveCmdNoValidation.isExecuting"
108+
class="ai-circled ai-indicator ai-dark-spin small"></i>
109+
Save
110+
<small>with [ssvCommand] - no canExecute</small>
111+
</button>
112+
113+
<button mat-raised-button
114+
color="primary"
115+
disabled
116+
[ssvCommand]="saveCmdNoValidation"
117+
[ssvCommandOptions]="{hasDisabledDelay: true}">
118+
<i *ngIf="saveCmdNoValidation.isExecuting"
119+
class="ai-circled ai-indicator ai-dark-spin small"></i>
120+
Save
121+
<small>with [ssvCommand] - no canExecute</small>
122+
</button>
123+
96124
<button mat-raised-button
97125
color="primary"
98126
[ssvCommand]="saveCmd"

examples/projects/example-app/src/app/command/example-command.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export class ExampleCommandComponent {
4242
isValidHeroRemove$ = new BehaviorSubject(true);
4343

4444
saveCmd = new CommandAsync(this.save$.bind(this), this.isValid$);
45+
saveCmdNoValidation = new CommandAsync(this.save$.bind(this));
4546
removeHeroCmd = new CommandAsync(this.removeHero$.bind(this), this.isValidHeroRemove$);
4647
pauseHeroCmd = new CommandAsync(this.pauseHero$.bind(this), this.isValidHeroRemove$);
4748
saveReduxCmd = new CommandAsync(

src/command-ref.directive.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { ICommand, CommandCreator } from "./command.model";
55
import { isCommandCreator } from "./command.util";
66
import { Command } from "./command";
77

8+
const NAME_CAMEL = "ssvCommandRef";
9+
810
/**
911
* Command creator ref, directive which allows creating Command in the template
1012
* and associate it to a command (in order to share executions).
@@ -23,12 +25,12 @@ import { Command } from "./command";
2325
*
2426
*/
2527
@Directive({
26-
selector: "[ssvCommandRef]",
27-
exportAs: "ssvCommandRef"
28+
selector: `[${NAME_CAMEL}]`,
29+
exportAs: NAME_CAMEL
2830
})
2931
export class CommandRefDirective implements OnInit, OnDestroy {
3032

31-
@Input("ssvCommandRef") commandCreator: CommandCreator | undefined;
33+
@Input(NAME_CAMEL) commandCreator: CommandCreator | undefined;
3234

3335
get command(): ICommand { return this._command; }
3436
private _command!: ICommand;
@@ -40,7 +42,7 @@ export class CommandRefDirective implements OnInit, OnDestroy {
4042
const execFn = this.commandCreator.execute.bind(this.commandCreator.host);
4143
this._command = new Command(execFn, this.commandCreator.canExecute as Observable<boolean> | undefined, isAsync);
4244
} else {
43-
throw new Error("ssvCommandRef: [ssvCommandRef] is not defined properly!");
45+
throw new Error(`${NAME_CAMEL}: [${NAME_CAMEL}] is not defined properly!`);
4446
}
4547
}
4648

src/command.directive.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,21 @@ import { CommandCreator, ICommand } from "./command.model";
6060
*
6161
*/
6262

63-
const SELECTOR = "ssvCommand";
63+
const NAME_CAMEL = "ssvCommand";
64+
65+
// let nextUniqueId = 0;
6466

6567
@Directive({
66-
selector: `[${SELECTOR}]`,
67-
exportAs: "ssvCommand"
68+
selector: `[${NAME_CAMEL}]`,
69+
exportAs: NAME_CAMEL
6870
})
6971
export class CommandDirective implements OnInit, OnDestroy {
7072

71-
@Input(SELECTOR) commandOrCreator: ICommand | CommandCreator | undefined;
73+
// readonly id = `${NAME_CAMEL}-${nextUniqueId++}`;
74+
75+
@Input(NAME_CAMEL) commandOrCreator: ICommand | CommandCreator | undefined;
7276

73-
@Input(`${SELECTOR}Options`)
77+
@Input(`${NAME_CAMEL}Options`)
7478
get commandOptions(): CommandOptions { return this._commandOptions; }
7579
set commandOptions(value: CommandOptions) {
7680
if (value === this._commandOptions) {
@@ -82,7 +86,7 @@ export class CommandDirective implements OnInit, OnDestroy {
8286
};
8387
}
8488

85-
@Input(`${SELECTOR}Params`) commandParams: unknown | unknown[];
89+
@Input(`${NAME_CAMEL}Params`) commandParams: unknown | unknown[];
8690

8791
get command(): ICommand { return this._command; }
8892
private _command!: ICommand;
@@ -98,9 +102,8 @@ export class CommandDirective implements OnInit, OnDestroy {
98102

99103
ngOnInit(): void {
100104
// console.log("[ssvCommand::init]", this.config);
101-
this.trySetDisabled(true);
102105
if (!this.commandOrCreator) {
103-
throw new Error("ssvCommand: [ssvCommand] should be defined!");
106+
throw new Error(`${NAME_CAMEL}: [${NAME_CAMEL}] should be defined!`);
104107
} else if (isCommand(this.commandOrCreator)) {
105108
this._command = this.commandOrCreator;
106109
} else if (isCommandCreator(this.commandOrCreator)) {
@@ -122,12 +125,14 @@ export class CommandDirective implements OnInit, OnDestroy {
122125
// });
123126
this._command = new Command(execFn, canExec, isAsync);
124127
} else {
125-
throw new Error("ssvCommand: [ssvCommand] is not defined properly!");
128+
throw new Error(`${NAME_CAMEL}: [${NAME_CAMEL}] is not defined properly!`);
126129
}
127130

128131
this._command.subscribe();
129132
this._command.canExecute$.pipe(
130-
delay(1),
133+
this.commandOptions.hasDisabledDelay
134+
? delay(1)
135+
: tap(() => { /* stub */ }),
131136
tap(x => {
132137
this.trySetDisabled(!x);
133138
// console.log("[ssvCommand::canExecute$]", { canExecute: x });
@@ -178,6 +183,7 @@ export class CommandDirective implements OnInit, OnDestroy {
178183

179184
private trySetDisabled(disabled: boolean) {
180185
if (this.commandOptions.handleDisabled) {
186+
// console.warn(">>>> disabled", { id: this.id, disabled });
181187
this.renderer.setProperty(this.element.nativeElement, "disabled", disabled);
182188
}
183189
}

src/config.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,17 @@ export interface CommandOptions {
1111
* This disables the handling manually and need to pass explicitly `[disabled]="!saveCmd.canExecute"`.
1212
*/
1313
handleDisabled: boolean;
14+
15+
/** Determine whether to set a `delay(1)` when setting the disabled. Which might be needed when working with external
16+
* components/directives (such as material button)
17+
*/
18+
hasDisabledDelay: boolean;
1419
}
1520

16-
export const COMMAND_DEFAULT_CONFIG: Readonly<CommandOptions> = Object.freeze({
21+
export const COMMAND_DEFAULT_CONFIG = Object.freeze({
1722
executingCssClass: "executing",
1823
handleDisabled: true,
19-
});
24+
hasDisabledDelay: false,
25+
} as CommandOptions);
2026

2127
export const COMMAND_CONFIG = new InjectionToken<CommandOptions>("command-config");

0 commit comments

Comments
 (0)