Using a State Machine to keep my UI Simple and Flexible? Good Idea? How do I do it in Knockout JS?

481 views
Skip to first unread message

EricNewYork

unread,
Nov 8, 2012, 10:03:58 AM11/8/12
to knock...@googlegroups.com
I’m new to Knockout JS


I am building a Knockout JS App (single page) where as I move from state to state, exactly what sub panels are visible and what they do changes. For example:


* pre-login, I have one big panel that invites the user to login.


* Right after login, I have a bunch of tabs, a main area for searching and an advertisement at the bottom.


* If the user does a search, then I end up in a state where we see tabs, a search area and a search results area.


I imagine doing this via having a state machine that moves from state to state as the user clicks, sort of the way a parser would move from state to state.


I imagine delegating to the current state, an object hanging off of the ViewModel (perhaps) so that my 20 or so different states are almost like 20 very simple different UIs each one separate from the others and simple and clean and easy to understand.


1 - How would I do this, have the UI delegate a sub UI to a sub object hanging off the ViewModel?

2 – Is there a better way to keep my UI simple even though it has lots of states and different states often look very different from one another in terms of what elements (sub panels) are visible?

3 – Does anyone have an example were the code repeatedly delegates to a sub user interface?


Am I making sense?


Is there a better way to think about this?

Eric

Ralph Becket

unread,
Nov 8, 2012, 6:50:56 PM11/8/12
to knock...@googlegroups.com
Hi,

we are (re)building UIs with very complex logic of this sort.  I think you need to move away from the state machine idea, since it's almost antithetical to the declarative MVVM style encouraged by Knockout.  Instead, simply control the visibility of your UI components in terms of the current state of the UI observables.

For example, say I have the following observables:

LoggedIn (initially false)
CurrentTab
InSearchMode (initially false)

I can then specify my view :

[if !LoggedIn]
  ... login screen ...
[else]
  [if CurrentTab == 1] ... tab 1 ...
  [else if CurrentTab == 2] ... tab 2 ...
  ...
  [else if CurentTab == n] ... tab n ...
  [if !InSearchMode]
    ... search text box bound to some observable ...
    ... "Search" button triggering search action and setting InSearchMode = true ...
  [else]
    ... search results ...
    ... "Search Again" button setting InSearchMode = false ...

We've found this approach leads to much more manageable view logic.

Hope this helps,
-- Ralph

EricNewYork

unread,
Nov 8, 2012, 7:58:41 PM11/8/12
to knock...@googlegroups.com
Interesting - are you recommending one big biz rule? No matter how many screens and sub-screens with complex rules for what is visible when? I was thinking we should have a sub VM that would be selected by the state machine and delete to that state to do the stuff for the screen when it is in a particular state. Still does not make sense to you?

Ralph Becket

unread,
Nov 8, 2012, 8:38:56 PM11/8/12
to knock...@googlegroups.com
Why would you go to all that trouble when you have the 'if' and 'visible' bindings in Knockout?  What I'm proposing is that each "container component" in your UI (and these can be hierarchical) simply include a computed observable indicating when it should be visible.  I'm not sure if this is what you mean by "one big biz rule", though, so we may be talking at cross purposes.  When you talk about a state machine, it suggests to me you're thinking imperatively ("on this transition, hide X and show Y") rather than declaratively ("X is visible when ...; Y is visible when ...").  Declarative schemes are usually much more concise, easier to debug, and more easily composable.


EricNewYork

unread,
Nov 8, 2012, 11:49:56 PM11/8/12
to knock...@googlegroups.com
Ralph

Thanks for thinking about this with me!

My application has lots of screens in it and each one has tabs with other screens on them. I'm envisioning being able to have a sub-VM for each of the major areas of the UI. Once in place, that sub-VM can make lots of decisions declaratively. 
  • Which sub-views to display
  • What to enable or disable under what logic. 
  • What biz objects to make available for display / editing.
I don't want these rules to be global or have them have lots of complex conditions about the state of the UI in them.

I'm just thinking this out now so it is great to talk to you about it!

Eric

Ralph Becket

unread,
Nov 11, 2012, 5:36:26 PM11/11/12
to knock...@googlegroups.com
Hi Eric,

glad to be able to help.  I think I mentioned I was looking into just this problem a few months ago.  The structure we arrived at has the following properties:

(1) each "view" (think: page on a UI) has a separate view model, an input observable (this is where it can share data provided by its parent), and an output observable (this is where it can return results -- e.g., for an address editor "wizard");

(2) views can be nested (forming a tree);

(3) each view is responsible for controlling the visibility of its immediate children;

(4) within a view, different mark-up and controls can specify visibility conditions in terms of the related view model state.

The idea here is that each view can be implemented as an independent entity; there should be no cross-view dependencies other than the parent-child relationship.  This structure greatly limits the complexity of the visibility and navigation rules.  All the mark-up is static, we don't generate anything at run-time except insofar as we use Knockout's "foreach" binding.

Cheers,
-- Ralph

jgoo...@radsoft.com

unread,
May 3, 2013, 3:33:52 PM5/3/13
to knock...@googlegroups.com
I'm using a scheme where I simply have two separate state observables - one for the total application state and one for the current "page" (tab, or whatever a "page means to you).  In coffeescript, as an example:

    @currentState('logged-in')
    @currentPage('page-foo')
    @applicationState = ko.computed => "#{@currentState()}:#{@currentPage()}"

then in your knockout visible bindings, you can match on a specific state or substate:

    <div data-bind='visible: applicationState() == "logged-in:page-foo">

or you can do partial matchs:

    <div data-bind='visible: applicationState().endsWith("page-foo")'>

It's a simple strategy and you don't end up with nasty, nested-if statements.
Reply all
Reply to author
Forward
0 new messages