Javascript-форум (https://javascript.ru/forum/)
-   Angular.js (https://javascript.ru/forum/angular/)
-   -   ngx-monaco-editor компонент сбрасывает form.pristine (https://javascript.ru/forum/angular/79055-ngx-monaco-editor-komponent-sbrasyvaet-form-pristine.html)

shtangen 11.12.2019 11:28

ngx-monaco-editor компонент сбрасывает form.pristine
 
Доброго времени суток.
Столкнулся со следующего рода проблемой.

Я использую в проекте ngx-monaco-editor и присоединяю его к компоненту редактора в качестве FormControl-a в реактивной форме.
Потом в onInit я загружаю данные и выставляю значения formControl-лов Я ожидал бы, что форма теперь будет нетронутой pristine: true, хотя на самом деле она помечена как pristine: false.
Pristin-ом она становится из за компонента monaco, который после загрузки данных из форм-контрола сбрасывает это свойство.
Даже сделав попытку пометить форму как pristine ничего не получилось, т.к. monaco работает асинхронно.

Есть дурацкий вариант поместить markAsPristine в функцию setTimeout, но это как-то костыльно.

На Гитлабе есть один opened issue с такой же проблемой.
https://github.com/atularen/ngx-mona...tor/issues/101

Но он у меня не заработал. Событий изменений содержимого компонента он не отлавливает.
<div>
		<ngx-monaco-editor
			#editorComponent
			[options]="editorOptions" 
			formControlName="template"
			(onInit)="initMonaco($event)"
		></ngx-monaco-editor>
	</div>

export class PostTemplateEditComponent implements ControlValueAccessor {
	componentForm: FormGroup;
        componentData: PostTemplate;
        currId: string;

	@ViewChild(EditorComponent, { static: false }) editorComponent: EditorComponent;

	private _justSetValue: any;
	private _onChange: any;
  
	registerOnChange (fn: any): void {
	  this._onChange = fn;
  
	  this.editorComponent.registerOnChange((val: any) => {
		// skip value if it is the just set value. relevant values are strings and `null`, so `===` is ok.
		console.log("catch");
		
		if(this._justSetValue === val) {
		  this._justSetValue = undefined;
		  return;
		}
  
		fn(val);
	  });
	}
  
	registerOnTouched (fn: any): void {
	  this.editorComponent.registerOnTouched(fn);
	}
  
	writeValue (val: any): void {
	  if(this._onChange) {
		this._onChange(val);
		this._justSetValue = val;
	  }
	  this.editorComponent.writeValue(val);
	}

	editorOptions = { 
		automaticLayout: true, 
		theme: 'vs-light', 
		language: 'handlebars',
		minimap: {
			enabled: false
		}
	};
}

initMonaco(monacoEditor: any) {
		this.componentForm.controls.template.markAsPristine();
                // не помогает
	}

	initState() {
		this.componentForm = new FormGroup({
			code: new FormControl(""),
			name: new FormControl(""),
			template: new FormControl(""),
			is_active: new FormControl(null),
			date_create: new FormControl({ value: "", disabled: true }),
			date_modify: new FormControl({ value: "", disabled: true }),
		});
	}

ngOnInit() {
		this.initState();
		this.dataService.getItem<PostTemplate>(this.currId)
			.subscribe((data: PostTemplate) => {
				this.componentData = new PostTemplate(data);
				this.componentForm.patchValue({
					code: data.code,
					name: data.name,
					is_active: data.is_active,
					template: data.template,
					date_create: data.date_create,
					date_modify: data.date_modify,
				});
				this.isReady = true;
				});
		} else {
			this.isReady = true;
		}
		
		this.$formSubscr = this.componentForm.valueChanges
			.subscribe(value => {
				this.applyFormValues(value);
			}
		);
		this.componentForm.markAsPristine();
	}


Не подскажите почему не работает приведенный вариант кода, либо предложите вариант получше.

destus 11.12.2019 11:47

shtangen,
ну вообще нужно ещё это https://github.com/atularen/ngx-mona...mponent.ts#L23, только в useExisting заюзать PostTemplateEditComponent.
И formControlName="template" для ngx-monaco-editor не надо указывать. Вся обработка идет в обёртке. Соответственно ей и надо снаружи передавать эту директиву.

shtangen 11.12.2019 13:08

Цитата:

Сообщение от destus (Сообщение 517304)
shtangen,
ну вообще нужно ещё это https://github.com/atularen/ngx-mona...mponent.ts#L23, только в useExisting заюзать PostTemplateEditComponent.
И formControlName="template" для ngx-monaco-editor не надо указывать. Вся обработка идет в обёртке. Соответственно ей и надо снаружи передавать эту директиву.

formControlName="template" использовал потому, что такой подход реализован в примерах на github.
Ваше решение не понял. До этого был вариант получить экземпляр редактора, завернутого в компонент и работать с ним напрямую без привязки к formControlName. Но такой вариант показался неудобным, так как реактивные формы позволяли упростить код.

Получалось что то типа такого:
<div class="scrolled-tab-content">
	<ngx-monaco-editor
	#editorComponent
	class="template-editor"
	[options]="editorOptions" 
	(onInit)="initMonaco($event)"
	></ngx-monaco-editor>
</div>


export class PostTemplateEditComponent {
	editor: any;

	initMonaco(monacoEditor: any) {
		this.editor = monacoEditor;
	}

	ngOnInit() {
		this.dataService.getItem<PostTemplate>(this.currId)
			.subscribe((data: PostTemplate) => {
				this.componentData = new PostTemplate(data);
				this.componentForm.patchValue({
					code: data.code,
					name: data.name,
					is_active: data.is_active,
					date_create: data.date_create,
					date_modify: data.date_modify,
				});
				// Инициализируем редактор
				this.editor.setValue(data.template);
			});

		this.$formSubscr = this.componentForm.valueChanges
			.subscribe(value => {
				this.applyFormValues(value);
			}
		);
	}


Или вы имели в виду что-то другое? Если есть возможность, приведите небольшой пример. Спасибо.

destus 11.12.2019 13:59

shtangen,
именно это я и имел ввиду.
а теперь снаружи где рисуется компонент PostTemplateEditComponent, ему и надо прокидывать formControlName, т.е. все ваши строчки с 9 по 27 должны быть на уровне выше по иерархии компонентов.
PostTemplateEditComponent используется просто как прокси.

app.component.ts
export class AppComponent {

	ngOnInit() {
        this.initState();
		this.dataService.getItem<PostTemplate>(this.currId)
			.subscribe((data: PostTemplate) => {
				this.componentData = new PostTemplate(data);
				this.componentForm.patchValue({
					code: data.code,
					name: data.name,
					is_active: data.is_active,
					date_create: data.date_create,
					date_modify: data.date_modify,
				});
				// Инициализируем редактор
				this.editor.setValue(data.template);
			});

		this.$formSubscr = this.componentForm.valueChanges
			.subscribe(value => {
				this.applyFormValues(value);
			}
		);
	}

	initState() {
		this.componentForm = new FormGroup({
			code: new FormControl(""),
			name: new FormControl(""),
			template: new FormControl(""),
			is_active: new FormControl(null),
			date_create: new FormControl({ value: "", disabled: true }),
			date_modify: new FormControl({ value: "", disabled: true }),
		});
	}

app.component.html
<div [formGroup]="componentForm">
  <post-template-edit-component formControlName="template">
  </post-template-edit-component>
</div>

shtangen 11.12.2019 15:42

Не слишком сложный оборот? Может проще обрабатывать monaco самостоятельно?
Поможет ли такая обертка избежать изменений внутри компонента?

destus 12.12.2019 06:23

shtangen,
Всё должно быть нормально. Иногда самому приходилось делать похожие обёртки для различных компонентов. То есть мы создаем свой кастомный форм контрол, и в методах реализации интерфейса ControlValueAccessor: registerOnTouched, registerOnChange, writeValue - вызываем точно такие же методы оборачиваемого компонента.


Часовой пояс GMT +3, время: 01:47.