diff --git a/CHANGELOG.md b/CHANGELOG.md index ff2cafeb..000e07a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Usage of RxJS deprecated operators. #INT-3274 ### Added - Support for Angular 15, 16 & 17. #INT-3274 +- Support for the OnPush change detection strategy. #INT-2974 ### Improved - Updated Storybook to v8, as well as now using CSFv3 components. #INT-3274 diff --git a/stories/Editor.stories.ts b/stories/Editor.stories.ts index b01395cb..39146eb4 100644 --- a/stories/Editor.stories.ts +++ b/stories/Editor.stories.ts @@ -5,6 +5,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { EventBindingComponent } from './event-binding/EventBinding.component'; import { EventForwardingComponent } from './event-forwarding/EventForwarding.component'; import { FormControlComponent } from './form-control/FormControl.component'; +import { FormWithOnPushComponent } from './form-with-on-push/form-with-on-push.component'; import { BlogComponent } from './formvalidation/FormValidation.component'; import { DisablingComponent } from './disable/Disable.component'; import { ViewQueryComponent } from './viewquery/Viewquery.component'; @@ -97,6 +98,17 @@ export const FormControlStory: StoryObj = { } }; +export const FormStateStory: StoryObj = { + name: 'Form with on-push change detection', + render: () => ({ + moduleMetadata: { + imports: [ ReactiveFormsModule, FormsModule ], + declarations: [ FormWithOnPushComponent ], + }, + template: `` + }), +}; + export const FormValidationStory: StoryObj = { name: 'Form Validation', render: () => ({ diff --git a/stories/form-with-on-push/form-with-on-push.component.ts b/stories/form-with-on-push/form-with-on-push.component.ts new file mode 100644 index 00000000..42c02304 --- /dev/null +++ b/stories/form-with-on-push/form-with-on-push.component.ts @@ -0,0 +1,31 @@ +/* eslint-disable @typescript-eslint/unbound-method */ +/* eslint-disable @typescript-eslint/no-parameter-properties */ +import { + Component, + ChangeDetectionStrategy, + Input, +} from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import type { EditorComponent } from '../../tinymce-angular-component/src/main/ts/public_api'; + +@Component({ + selector: 'form-with-on-push', + templateUrl: './form-with-on-push.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class FormWithOnPushComponent { + @Input() public apiKey = ''; + public readonly initialValue = ''; + public readonly init: EditorComponent['init'] = { + plugins: [ 'help' ], + }; + public readonly form = new FormGroup({ + tiny: new FormControl('', { + validators: Validators.compose([ + Validators.required, + Validators.minLength(10) + ]), + }), + regular: new FormControl(''), + }); +} diff --git a/stories/form-with-on-push/form-with-on-push.html b/stories/form-with-on-push/form-with-on-push.html new file mode 100644 index 00000000..9c157427 --- /dev/null +++ b/stories/form-with-on-push/form-with-on-push.html @@ -0,0 +1,24 @@ +
+ +
+ +
+ + + +
+
+Pristine: {{ form.pristine }}
+Touched: {{ form.touched }}
+Dirty: {{ form.dirty }}
+Valid: {{ form.valid }}
+Data: {{ form.value | json }}
+
diff --git a/tinymce-angular-component/src/main/ts/editor/editor.component.ts b/tinymce-angular-component/src/main/ts/editor/editor.component.ts index 1b4c363c..557e287f 100644 --- a/tinymce-angular-component/src/main/ts/editor/editor.component.ts +++ b/tinymce-angular-component/src/main/ts/editor/editor.component.ts @@ -1,6 +1,20 @@ /* eslint-disable @typescript-eslint/no-parameter-properties */ import { isPlatformBrowser, CommonModule } from '@angular/common'; -import { AfterViewInit, Component, ElementRef, forwardRef, Inject, Input, NgZone, OnDestroy, PLATFORM_ID, InjectionToken, Optional } from '@angular/core'; +import { + AfterViewInit, + Component, + ElementRef, + forwardRef, + Inject, + Input, + NgZone, + OnDestroy, + PLATFORM_ID, + InjectionToken, + Optional, + ChangeDetectorRef, + ChangeDetectionStrategy +} from '@angular/core'; import { FormsModule, ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { Subject, takeUntil } from 'rxjs'; import { getTinymce } from '../TinyMCE'; @@ -25,7 +39,8 @@ const EDITOR_COMPONENT_VALUE_ACCESSOR = { styles: [ ':host { display: block; }' ], providers: [ EDITOR_COMPONENT_VALUE_ACCESSOR ], standalone: true, - imports: [ CommonModule, FormsModule ] + imports: [ CommonModule, FormsModule ], + changeDetection: ChangeDetectionStrategy.OnPush }) export class EditorComponent extends Events implements AfterViewInit, ControlValueAccessor, OnDestroy { @@ -77,6 +92,7 @@ export class EditorComponent extends Events implements AfterViewInit, ControlVal public constructor( elementRef: ElementRef, ngZone: NgZone, + private cdRef: ChangeDetectorRef, @Inject(PLATFORM_ID) private platformId: Object, @Optional() @Inject(TINYMCE_SCRIPT_SRC) private tinymceScriptSrc?: string ) { @@ -187,10 +203,12 @@ export class EditorComponent extends Events implements AfterViewInit, ControlVal private initEditor(editor: TinyMCEEditor) { listenTinyMCEEvent(editor, 'blur', this.destroy$).subscribe(() => { + this.cdRef.markForCheck(); this.ngZone.run(() => this.onTouchedCallback()); }); listenTinyMCEEvent(editor, this.modelEvents, this.destroy$).subscribe(() => { + this.cdRef.markForCheck(); this.ngZone.run(() => this.emitOnChange(editor)); });