Manage views with css or scopes in the Magiont Master
I am working on a page with many input controls and related sections. There are use cases on this page where I supposedly have to show / hide the div depending on how the user clicks on the input controls on different subsequent screens.
Now all the divs are on first load and by showing / hiding the screen changes for the user. Now to show / hide I can use css and add a class like * to the .main content div depending on the business logic.
eg:.
.main div{
display: none;
}
.main.view1 div.a,.main.view1 div.b,.main.view1 div.f{
display:block;
}
.main.view2 div.c,.main.view2 div.f {
display:block;
}
.main.view3 div.c,.main.view3 div.f {
display:block;
}
....etc
But in this way, no. css classes become unmanaged.
Please suggest if there is a better method I can use when it becomes easier to manage user flows. I think there are regions in the puppet that can help me deal with this. Please suggest a better way and clarify if the answer is marionette.regions
source to share
You can model your application as a state machine for modeling complex workflows.
To define a state machine:
- Determine all the states your application might be in.
- Determine the set of actions allowed in each state. Each action transfers the state of the application from one state to another.
- Write business logic for each action that includes both permanent changes to the server and changing view state.
This schema is similar to creating a DFA , but you can add additional behavior according to your needs.
If this sounds too abstract, here's an example of a simple finite machine.
Let's say you are building a simple login application.
Constructing states and actions
-
INITIAL_STATE: The user visits the page for the first time and both fields are blank. Let's say you only want to make it
username
visible, but notpassword
in this state. (Similar to the new Gmail workflow) -
USERNAME_ENTRY_STATE: When the user enters the username and shows
return
, in this state, you want to display the username and hide the password. You can haveonUsernameEntered
as an action in this state. -
PASSWORD_ENTRY_STATE: The username will now be hidden and the password view will be shown. When the user clicks
return
, you have to check if the usernames and passwords match. Let's call this actiononPasswordEntered
-
AUTHENTICATED_STATE: When the server checks the username and password combination, let's say you want to show the home page. Let's call this action
onAuthenticated
I just skipped the "Authentication Failed" operation.
Create views:
In this case, we have UsernameView
andPasswordView
Model design:
For our example, one model is sufficient Auth
.
Creating routes:
Check out the tips for managing routes with Marionette. The state machine must be initialized in the login path.
Example pseudocode:
I just showed the code related to state machine control. Rendering and event handling can be done as usual
var UsernameView = Backbone.View.extend({
initialize: function(options) {
this.stateMachine = options.stateMachine;
},
onUserNameEntered: function() {
username = //get username from DOM;
this.stateMachine.handleAction('onUserNameEntered', username)
},
show: function() {
//write logic to show the view
},
hide: function() {
//write logic to hide the view
}
});
var PasswordView = Backbone.View.extend({
initialize: function(options) {
this.stateMachine = options.stateMachine;
},
onPasswordEntered: function() {
password = //get password from DOM;
this.stateMachine.handleAction('onPasswordEntered', password)
},
show: function() {
//write logic to show the view
},
hide: function() {
//write logic to hide the view
}
});
Each state will have a function entry
that initializes the views and and a exit
function that will clean up the views. Each state will also have functions corresponding to the actual actions in that state. For example:
var BaseState = function(options) {
this.stateMachine = options.stateMachine;
this.authModel = options.authModel;
};
var InitialState = BaseState.extend({
entry: function() {
//show the username view
// hide the password view
},
exit: function() {
//hide the username view
},
onUsernameEntered: function(attrs) {
this.authModel.set('username', attrs.username');
this.stateMachine.setState('PASSWORD_ENTRY_STATE');
}
});
Likewise, you can write code for other states.
Finally, the state machine:
var StateMachine = function() {
this.authModel = new AuthModel;
this.usernameView = new UserNameView({stateMachine: this});
//and all the views
this.initialState = new InitialState({authModel: this.authModel, usernameView: this.usernameView});
//and similarly, all the states
this.currentState = this.initialState;
};
StateMachine.prototype = {
setState: function(stateCode) {
this.currentState.exit(); //exit from currentState;
this.currentState = this.getStateFromStateCode(stateCode);
this.currentState.entry();
},
handleAction: function(action, attrs) {
//check if action is valid for current state
if(actionValid) {
//call appropriate event handler in currentState
}
}
};
StateMachine.prototype.constructor = StateMachine;
For a simple application, this seems to be overkill. For complex business logic, it's well worth the effort. This design pattern automatically prevents cases such as double-clicking on a button since you have already moved to the next state and the new state does not recognize the previous state action.
Once you've created a state machine, other members of your team can simply plug in their states and views, and see the big picture in one place.
Libraries like Redux show some of the heavy workloads shown here. As such, you might want to consider React + Redux + Immutable.js.
source to share