前置:
ckeditor5/utils:emittermixin(事件监听机制)
ckeditor5官网observables讲解
ckeditor5/utils:ObservableMixin
可观察的对象(Observables)是CKEditor 5 Framework的常见组成要素。
绑定到可观察对象的模板使得用户界面具有动态和交互式特质。一些基本类如Editor、Command可也是可观察对象。
- 如何设置类为observable
通过混入ObservableMixin
,可以使任何类变成observable,如[View基类](https://github.com/ckeditor/ckeditor5/blob/master/packages/ckeditor5-ui/src/view.js;关于ObservableMixin
,详见ckeditor5/utils:ObservableMixin。 -
set
:如何设置单个属性为observable
通过从View基类获得set
得方法设置属性为observable,如buttonview。
export default class ButtonView extends View {
constructor( locale ) {
super( locale );
// ......
this.set( 'isEnabled', true );
this.set( 'isOn', false );
this.set( 'label' );
this.set( 'isVisible', true );
this.set( 'isToggleable', false );
// ......
}
}
如下代码,每当label属性改变后,change:label事件会被触发,相应的回调函数会被执行:
const view = new ButtonView();
view.on( 'change:label', ( evt, propertyName, newValue, oldValue ) => {
console.log(
`#${ propertyName } has changed from "${ oldValue }" to "${ newValue }"`
);
} )
view.label = 'Hello world!'; // -> #label has changed from "undefined" to "Hello world!"
view.label = 'Bold'; // -> #label has changed from "Hello world!" to "Bold"
view.type = 'submit'; // Changing a regular property fires no event.
-
delegate
:除了set
方法外,还有其他方法如delegate
等也可以用于设置属性为observable:
buttonFoo.delegate( 'execute' ).to( toolbar );
buttonBar.delegate( 'execute' ).to( toolbar );
toolbar.on( 'execute', evt => {
console.log( `The "${ evt.source.label }" button was clicked!` );
} );
-
bind
:
// editor.commands.isEnabled必须使用set方法定义,才能保证bind方法的有效性
const button = new Button();
const command = editor.commands.get( 'bold' );
button.bind( 'isEnabled' ).to( command, 'isEnabled' );
- decorate(装饰):
通过decorate
方法,可以将object methods转换为事件驱动性质的方法。当某个方法被decorate后,那么当方法被执行时,一个同名的事件将会被创建和触发。源码很简单,如下:
decorate( methodName ) {
const originalMethod = this[ methodName ];
if ( !originalMethod ) {
throw new CKEditorError(
'observablemixin-cannot-decorate-undefined',
this,
{ object: this, methodName }
);
}
this.on( methodName, ( evt, args ) => {
evt.return = originalMethod.apply( this, args );
} );
this[ methodName ] = function( ...args ) {
return this.fire( methodName, args );
};
}
实例如下:
class Button extends View {
constructor() {
// ...
this.decorate( 'focus' ); // 注意这里!!!
}
focus( force ) {
console.log( `Focusing button, force argument="${ force }"` );
// Unless forced, the button will only focus when not already focused.
if ( force || document.activeElement != this.element ) {
this.element.focus();
return true;
}
return false;
}
}
// Cancelling the execution
const button = new Button();
// Render the button to create its #element.
button.render();
// The logic controlling the behavior of the button.
button.on( 'focus', ( evt, [ isForced ] ) => {
// Disallow forcing the focus of this button.
if ( isForced === true ) {
// 参见:[ckeditor5/utils:emittermixin(事件监听机制)](https://www.jianshu.com/p/c6222dbf157d)
evt.stop();
}
}, { priority: 'high' } );
button.focus(); // -> 'Focusing button, force argument="undefined"'
button.focus( true ); // Nothing is logged, the execution has been stopped.
上面有两个逻辑需要注意:
1.priority:'high'
,用于设置回调函数执行的优先级(顺序),不设置的话,默认为'normal',低于'high':
const priorities = {
get( priority ) {
if ( typeof priority != 'number' ) {
return this[ priority ] || this.normal;
} else {
return priority;
}
},
highest: 100000,
high: 1000,
normal: 0,
low: -1000,
lowest: -100000
}
2.evt.stop();
会终止执行后续的回调函数,见ckeditor5/utils:emittermixin(事件监听机制)第五节fire。
- 更多用法见ui-library#views。