-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
View: An utility to extend existing templates #5209
Comments
TL;DR: I decided to propose some general changes to Template–View interaction. First of all, I think the decision we made that event listener declaration should be as brief as possible like
is oversimplification and mixes Template with View logic. I think it's dirty. It silently says that The same thing happened to attribute bindings. Using Then, once separated // IMO importing Template class in each View is not a huge cost.
import Template from './template.js';
class ButtonView extends View {
constructor( model ) {
super( model );
this.template = new Template( {
tag: 'button',
attributes: {
class: [
'ck-button',
// Move binding to Templates. It should be View-independent.
// It should allow binding to any model, not View#model only.
Template.bind.to( this.model, 'isEnabled', () => ... ) ),
Template.bind.if( any model, 'isOn', () => ... )
// Alt syntax?
// [ 'to', this.model, 'isEnabled', () => { ... } ]
// [ 'if', any model, 'isOn', () => { ... } ]
]
},
on: {
click: [
// Dispatching works for this View...
Template.dispatch( this, 'clicked' ),
// ...but also for any instance of EmitterMixin.
Template.dispatch( any emitter, 'execute' ),
() => { ... }
// Alt syntax?
// [ any emitter, 'clicked' ],
]
}
} );
// I think this method name is confusing. Why not
// this.region( 'children', el => el );
// or
// this.setRegion( 'children', el => el );
// or
// this.registerRegion( 'children', el => el );
// ?
this.register( 'children', el => el );
}
}
class MyButtonView extends ButtonView {
constructor( model ) {
super( model );
// Extending works via existing Template instance created by the parent class.
this.template.extend( {
attributes: {
class: [
Template.bind.to( any model, 'foo', () => { ... } )
],
bar: 'value'
},
on: {
click: [
() => { ... }
]
}
} );
}
} Template application to the existing DOM Element could look like this: class NoConstructorTemplateView extends View {
constructor( model ) {
super( model );
}
init() {
new Template( {
attributes: {
foo: Template.bind.to( this.model, 'isEnabled', () => ... ) )
bar: 'value'
},
on: {
click: Template.dispatch( this, 'clicked' ),
}
} ).apply( someDOMElement );
}
} |
Questions:
|
As for regions, maybe |
And generally, I like the idea about separating templates and views. I think that they should be totally separated up to a point when a template is being applied to a view. The related changes like moving binding definitions to |
It has one model. But it may spawn sub-views in simple cases when controller is not necessary. Like
By binding View events to model events you make VM mandatory for event system to work. You cannot have a model–less View firing events, you got to pass an empty Model to do this. Not a good idea, IMO. |
But, for the sake of proper separation every sub-view should have its own view model. View's view model should be its only point of concern. So the reason to restrict us is to keep the code clean. By forcing binding view's template to its model only (in both directions), we'll have a cleaner API (because you don't have to pass the model each time) and a more predictable code.
As I wrote above – I see this as a big advantage. Most likely views will be creating their view models (as mentioned in https://github.com/ckeditor/ckeditor5-ui/issues/26) and those models should be the only entry point to the views. |
But if you use foo: Template.bind.to( 'isEnabled', () => ... ) ) instead of foo: Template.bind.to( this.model, 'isEnabled', () => ... ) ) then the I suppose you'd like to see something like that: // In View#constructor()
this.template = {
attributes: {
foo: view.bind.to( 'isEnabled', () => ... ) )
}
} which then internally is processed into a // In View#init()
const tpl = new Template( this.template );
for ( let a in tpl.attributes ) {
Template.bind.to( view.model, tpl.attributes[ a ] );
} but it's only a syntax sugar. It creates additional layer of abstraction which I think is unnecessary. |
I'm not sure I agree, so I'll write generally how I see it. Curious if that's doable. So, I totally understand that template should be totally detached from the view. I support that. However, template must know about the view model, right? You propose to pass the model explicitly to each constructor() {
const bind = new Binding( this.model );
// later in some template:
foo: bind.to( 'foo' )
} What's the difference between passing model once to the whole templating mechanism and passing to each of its methods? (*) I don't like this name... Can't we also bind events? Like... property change trigger other property change (and we call this a binding) and an event fired on the view triggers event on a model (why not calling this a binding as well?). |
Moved from ckeditor/ckeditor5-core#219
The text was updated successfully, but these errors were encountered: