Model-Centric View Composition
This is one of the ideas that are neither constructed a-priori nor abstracted from accumulated experiences of solving different but similar problems; this emerged naturally from only one single project. I'm not saying that this is better than what people are used to or even this is any good at all, but I believe it's worth writing down.
What is this "model-centric view composition"?
It's exactly what its name says: a GUI application written in this way would be a composition (or a combination of multiple compositions) of views with their actual content determined by models. The reason why it is "model-centric" is that views, instead of being a relatively-standalone thing like in common implementations of MVC and MVVM, are only a part (or avatars, to be more exact) of models. "Composition", most of the time, merely means "combination" and roughly corresponds to a single web page in a Multi-Page Application.
Figure 1: A graph demonstrating the structure of a possible MCVC app.
How this idea came into being
So I was trying to make a WYSIWYG UI editor that's like a mix of Microsoft Powerpoint and VB6; in this editor there are a lot of case of changes coming from different places and need to be synced to different places. Since it was still just a mock-up I decided to do everything in vanilla JavaScript; I ended up making something like this:
class Attribute { public static name = 'width'; public static description = 'The width of the component.'; private _value = new Observable(); public setValue(newValue) { this._value.update(newValue); } public getValue() { return this._value.currentValue(); } public makeEditorView() { let res = document.createElement('div'); // adding name & description & text field to res & setting up 2-way data binding return res } } function MakeAttributeTable(...) { // ... some code used to simplify the making of a table of attributes. } class Button { private _text = new Observable(''); // separating publicly-available & private attributes, should you want some attributes to be hidden... private _attributes = MakeAttributetable(...); public attributes = { ... }; // public makeMainView() { let res = document.createElement('button'); // setting up 2-way data binding return res; } public makeEditorView() { let res = document.createElement('button'); // setting up 2-way data binding // then add things specific to the UI editor, e.g. drag to move/resize, double-click to rename, etc. return res; } } class CurrentEditorState { private static _componentList = new Observable([]); private static _focusedComponent = new Observable(); private static _attribute = CurrentEditorState._focusedComponent.derive((x) => x.getAllAttributes()); public static makeAttributeEditorView() { let res = document.createElement('div'); // setting up attribute editor return res; } public static makeEditorView() { let res = document.createElement('div'); // setting up editor return res; } public static makeMainView() { let res = document.createElement('div'); // setting up main view res.appendChild(this.makeEditorView()); res.appendChild(this.makeAttributeEditorView()); // ... return res; } }
The motive behind this is that: if I set up the data binding properly in all the makeView
methods, I can always get a view that's guaranteed to be able to push & receive updates through these methods anywhere anytime I want.
Addendum: Command-Driven Model
The phrase "command-driven" means that the update of the model (or models) is not conceptually a singular thing but as a part of a "command" or "transaction". This idea is to solve a very specific problem; it requires the following to make sense:
- The changes that could happen for your models can indeed be abstracted in a handful of commands;
- These commands could and would be occuring in a non-predetermined fashion, e.g. it is expected to be configured with an external source.
Last update: 2024.6.6