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.

3
  • 1
    Possible duplicate stackoverflow.com/questions/1450997/… Also, here's a good question to check out regarding require.js stackoverflow.com/questions/12639772/… Commented Feb 10, 2013 at 1:06
  • Are you doing this all within the scope of JSFiddle, or are you testing somewhere else as well? Commented Feb 10, 2013 at 1:23
  • I'm testing in my browser without JSFiddle scope Commented Feb 10, 2013 at 1:55

4 Answers 4

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.

Sign up to request clarification or add additional context in comments.

Comments

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.

1 Comment

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: jsfiddle.net/Yqdj5/1
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

1 Comment

Hm... Seems like a hack, doesn't it. =/ Does my recent edit not work for you?
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),
}

1 Comment

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!)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.