3

I am having issues with the order of defining functions and instantiating objects, see: JSFiddle

I'm just playing around with an idea right now, but I hit this wall and I don't know if there's any simple solution to the problem. Basically I have a an object with some methods on another object, but this other object contains references to the first object and so no matter what order I instantiate/define I'll get an error because one or the other hasn't been loaded:

  var router = {
    update: function(event, from, to) {
      window.location.hash = "#/" + to;
      $("back-btn").disabled = fsm.can("back");  // *** And here I am referencing fsm
      $("next-btn").disabled = fsm.can("next");
    },
    location: window.location.hash.substring(2),
  }

  var fsm = StateMachine.create({
    initial: "intro",
    events: [

      // Next events and where to route based on our page
      { name: "next", from: "intro", to: "getname" },
      { name: "next", from: "getname", to: "welcome" },
      { name: "next", from: "welcome", to: "why" },

      // We can't go "back" from the initial route
      { name: "back", from: "getname", to: "intro" },
      { name: "back", from: "welcome", to: "getname" },
      { name: "back", from: "why", to: "welcome" } ],

    callbacks: {
      onintro  : router.update, //*** Here I am referencing the router object
      ongetname: router.update,
      onwelcome: router.update,
      onwhy    : router.update 
    }
  });

Thanks for any help.

nak
  • 3,077
  • 3
  • 27
  • 35
  • 1
    Possible duplicate http://stackoverflow.com/questions/1450997/resolving-circular-dependencies-with-dependency-injection Also, here's a good question to check out regarding require.js http://stackoverflow.com/questions/12639772/solving-circular-dependency-in-node-using-requirejs – mrk Feb 10 '13 at 01:06
  • Are you doing this all within the scope of JSFiddle, or are you testing somewhere else as well? – daleyjem Feb 10 '13 at 01:23
  • I'm testing in my browser without JSFiddle scope – nak Feb 10 '13 at 01:55

4 Answers4

1

It looks like the timing issue occurs because one of the callbacks you are specifying is onintro, which presumably runs right away. Is it practical to refactor your implementation for the onintro callback? You might be able to get away with something like this:

var router = {
    update: function(event, from, to) {
        window.location.hash = "#/" + to;
        $("back-btn").disabled = fsm.can("back");
        $("next-btn").disabled = fsm.can("next");
    },
    location: window.location.hash.substring(2),
}

var fsm = StateMachine.create({
    //...

    callbacks: {
        //onintro  : router.update, // Don't call this in the constructor...
        ongetname: router.update,
        onwelcome: router.update,
        onwhy    : router.update 
    }
});

router.update(); // Call it just after construct.
wxactly
  • 2,400
  • 1
  • 26
  • 42
  • it actually throws the error before it even tries to callback, so when I comment it out it gives an error, here's a more JSFiddle friendly JSFiddle with the new version that works: http://jsfiddle.net/Yqdj5/1/ – nak Feb 10 '13 at 05:34
1

You could use a try/catch to avoid the first undefined:

try {
    $("back-btn").disabled = fsm.can("back");
    $("next-btn").disabled = fsm.can("next");
} catch(e){}

Additionally, if you're testing all within JSFiddle, it's going to wrap your JS into a window.onload function. So when you click the buttons, they'll be trying to call fsm.back() or fsm.next(), where fsm was defined within the scope of that window.onload function. Not within the scope those buttons have access to.

daleyjem
  • 2,335
  • 1
  • 23
  • 34
1

I had to assign the callbacks to the state machine object after the fact, and then defer initialization until after my router object was defined:

var fsm = StateMachine.create({

  //*** Here we set defer to true
  initial: { state: "intro", event: "init", defer: true },
  events: [

    // Next events and where to route based on our page
    { name: "next", from: "intro",   to: "getname" },
    { name: "next", from: "getname", to: "welcome" },
    { name: "next", from: "welcome", to: "why" },

    // We can't go "back" from the initial route
    { name: "back", from: "getname", to: "intro" },
    { name: "back", from: "welcome", to: "getname" },
    { name: "back", from: "why",     to: "welcome" } ],
});

window.onload = function() {
  var router = {
    update: function(event, from, to) {
      window.location.hash = "#/" + to;
      $("back-btn").disabled = fsm.cannot("back");
      $("next-btn").disabled = fsm.cannot("next");
    },
    location: window.location.hash.substring(2),
  }

  //*** And now we attach the callbacks since we have created the router object
  fsm.onintro = router.update, fsm.ongetname = router.update,
  fsm.ongetname = router.update, fsm.onwelcome = router.update,
  fsm.onwhy = router.update;

  //*** And call the init event!
  fsm.init();
}

and the fiddle

nak
  • 3,077
  • 3
  • 27
  • 35
0

The dependency fix could be as simple as:

var router = {
    update: function(event, from, to) {
        window.location.hash = "#/" + to;
        if(window.fsm) {
            $("back-btn").disabled = fsm.can("back");
            $("next-btn").disabled = fsm.can("next");
        }
    },
    location: window.location.hash.substring(2),
}
wxactly
  • 2,400
  • 1
  • 26
  • 42
  • Ah, if I did this though, the button would be enabled and allow the user to access the back button initially when they shouldn't. Thanks for all the help/ideas but it seems like I'm going to have to defer initializing the FSM, assign the callbacks, and then initialize the FSM, it's the only way I can see so far without having bugs. It's just to test an idea out anyway, nothing that's hitting a wide audience (I don't think!) – nak Feb 11 '13 at 22:07