2016-01-14 2 views
2

Продолжая мое путешествие на ночное путешествие в 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>"; 
    } 
} 

ответ

3

Просто сделайте [(ngModel)] = синтаксис "свойство". Несмотря на то, что он выглядит как старый двусторонний привязку в реальности, он разбивает его на два разных привязки в одном направлении: один для ввода и один для вывода, способ, которым угловой 2 обрабатывает изменения, а его грязные контрольные циклы под капотом - это то, что изменено и не позволит вам выполнять бесконечный цикл.

+0

Спасибо, я просто попробовал это, но, похоже, это не сработало, если, конечно, мое изменение не было прав (). При нажатии на кнопку set ничего не происходит. Вместо этого при загрузке страницы я получаю «EXCEPTION: no value accessor for» в [xml в EditorComponent @ 1: 28] ». Я обновил свой Plunker соответственно, вы можете проверить его. – Naftis

+0

Я должен добавить, что, что касается нечеткого сообщения об ошибке, я нашел эту подсказку: https://github.com/angular/angular/issues/4733, то есть мне пришлось явно импортировать директиву NgModel (в то время как другие, подобные NgFor, уже инкапсулированный). Я обновил Plunker, но он по-прежнему ведет себя одинаково. Есть идеи? – Naftis

+0

Использовать текст, а не ngModel: '' –

Смежные вопросы