Продолжая мое путешествие на ночное путешествие в Angular2 (beta 1), я пытаюсь понять, как правильно реализовать двустороннюю привязку без моего приложения, застрявшего в бесконечный цикл.Angular2: как правильно реализовать двухстороннюю привязку данных, не влетая в бесконечный цикл
Пожалуйста, найдите образец, воспроизводимый в этом Plunker: http://plnkr.co/edit/83NeiUCEpPvYvUXIkl0g. Просто запустите и нажмите кнопку.
Мой сценарий:
- директива оборачивает редактор кода Ace и выставляет
text
собственности иtextChanged
события. - компонент редактора (XML) использует эту директиву. Он должен иметь возможность реагировать на изменения в базовом редакторе для обновления своего XML-кода и устанавливать новый текст в базовом редакторе из его XML-кода.
Моей проблема заключается в том, что всякий раз, когда компонент редактора программно устанавливает его свойство xml
, это вызывает изменение базового редактора Ace, который, в свою очередь, запускает текстовое событие изменения, которые, в свою очередь, обрабатываются обратным вызовом из редактора компоненты , и так далее. Это создает бесконечный цикл, и вы можете видеть мерцание текста редактора. Что я здесь делаю неправильно?
Вот код для директивы:
import {Component,Directive,EventEmitter,ElementRef} from 'angular2/core';
declare var ace: any;
@Directive({
selector: "ace-editor",
inputs: [
"text"
],
outputs: [
"textChanged"
]
})
export class AceDirective {
private editor : any;
public textChanged: EventEmitter<string>;
set text(s: string) {
if (s === undefined) return;
let sOld = this.editor.getValue();
if (sOld === s) return;
this.editor.setValue(s);
this.editor.clearSelection();
this.editor.focus();
}
get text() {
return this.editor.getValue();
}
constructor(elementRef: ElementRef) {
var dir = this;
this.textChanged = new EventEmitter<string>();
let el = elementRef.nativeElement;
this.editor = ace.edit(el);
let session = this.editor.getSession();
session.setMode("ace/mode/xml");
session.setUseWrapMode(true);
this.editor.on("change", (e) => {
let s = dir.editor.getValue();
dir.textChanged.next(s);
});
}
}
А вот компонент редактор:
import {Component,EventEmitter} from "angular2/core";
import {AceDirective} from "./ace.directive";
@Component({
selector: "my-editor",
directives: [AceDirective],
template: `<div style="border:1px solid red">
<ace-editor id="editor" [text]="xml" (textChanged)="onXmlChanged($event)"></ace-editor>
</div>
<div><button (click)="changeXml()">set xml</button></div>`,
inputs: [
"xml"
]
})
export class EditorComponent {
private _xml: string;
// using a property here so that I can set a breakpoint
public set xml(s: string) {
this._xml = s;
}
public get xml() : string {
return this._xml;
}
constructor() {
this._xml = "";
}
public onXmlChanged(xml: string) {
this._xml = xml;
}
// an action which somehow changes the XML content
public changeXml() {
this._xml = "<x>abc</x>";
}
}
Спасибо, я просто попробовал это, но, похоже, это не сработало, если, конечно, мое изменение не было прав ( ). При нажатии на кнопку set ничего не происходит. Вместо этого при загрузке страницы я получаю «EXCEPTION: no value accessor for» в [xml в EditorComponent @ 1: 28] ». Я обновил свой Plunker соответственно, вы можете проверить его. –
Naftis
Я должен добавить, что, что касается нечеткого сообщения об ошибке, я нашел эту подсказку: https://github.com/angular/angular/issues/4733, то есть мне пришлось явно импортировать директиву NgModel (в то время как другие, подобные NgFor, уже инкапсулированный). Я обновил Plunker, но он по-прежнему ведет себя одинаково. Есть идеи? – Naftis
Использовать текст, а не ngModel: ' ' –