Closed shoover closed 9 years ago
You can definitely do that. I advocate separating render functions completely into their own files because
2 and 3 are just my opinion. Raynos does seem to lean more componentwards in some of the examples, for example how most render methods are attached to an object, not completely free.
@shoover is not a requirement of the library just a way in which Raynos do the examples, because he likes more functional programming, in your case I see that you want to use and OO paradigm, a option then is create local state in each component, and attach them to his parent component state, and the state of the parent to the parent-parent state, and soon on till the root State (making you State tree in the process) when the class are loaded, using observ-struct, observ-array, or observ-varhash. if you are careful and understand the code that is really small and easy to comprehend, you will archive the same using the OO paradigm.
this approximation is good for me because In my case when I think about the model of the app, I relate it more with the db data (users, tasks, messages, etc.) very similar to what I should have in my db. the ui for me can represent that data in different ways, so the state of db like data should not be tied to a UI component. and the Ui component should has its own state. (is just my way of thinking)
I archive those things in this way (Warning
: this is pseudo code can contains many errors)
// Create global variable that you will use ref the db models
GLOBAL_VARIABLE = {};
// load your model here, you can define all your model structure like
// you do when use db-schemes using for example an observ-struct (I prefer this way),
// or in scheme-less approximation using observ-varhash
// and reference to the db model in the global variable
GLOBAL_VARIABLE.model = mercury.varhash({});
// load the ui root component, you should create the UI state tree in the process
var ui = new UI()
// Create the app state
var appState = mercury.struct({
"model": GLOBAL_VARIABLE.model,
"ui": ui.state
});
//start mercury
mercury.app(document.body, appState, ui.render);
// Ui class implementation
class UI {
constructor() {
// we create ui root state
let state = {
show: mercury.value("c1");
}
// load your component 1
this.c1 = new Component1();
state["c1"] = c1.state
.
.
.
.
// load your component n
this.cn = new Component_n();
state["cn"] = cn.state
// the magic occurs here
this.state = mercury.struct(state);
// subscribe to child events
this.c1.onClose(this.closeView);
.
.
.
this.cn.onClose(this.closeView);
}
closeView(event, data) {
this.state.show.set("c1");
}
changeView(component = "c1") {
this.state.show.set(component);
}
//=============================================================
// An important note is that the UI.render should has the appState as argument,
// but this is ignored because each component has access to its own state
// and already know where to look for render itself.
//=============================================================
render(state) {
// just ignore the state argument
if (this.state.show() == "c1") {
return c1.render();
}
.
.
.
.
if(this.state.show() == "cn") {
return cn.render();
}
}
// =======================================================
// in case that you want to use the immutable state
// (more mercury way also more safest)
// this will avoid to accidentally or by bad practices change the state
// in the render procedure, if you know what are you doing
// just pick the method that you are comfortable.
// =======================================================
render(state) {
if (state.show == "c1") {
return c1.render(state.c1);
}
.
.
.
.
if(this.state.show == "cn") {
return cn.render(state.cn);
}
}
}
this.state.form_has_errors = true
(in the c1 component), "the global re-render" will be fired. tips1: use typescript is of great help in this kind of cases
Sorry for my broken English I hope that this example can help you.
Thank you, @eightyeight and @criloz, for the helpful explanations. I can see there is room for changing designs to suit the application but the examples seem to do it one particular way that ensures a functional perspective. I see this separation enforced in many Elm designs, as well.
Just curious why mercury component render methods are static since naively it would be convenient for a parent component to call foo.render() as an instance method. Is that mainly to enforce separation from the state object and discourage mutating local state?
Thanks very much for the excellent docs and examples. Mercury is an enlightening project.