nak
nak

Reputation: 3137

JavaScript: Order of defining functions and instantiating objects

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.

Upvotes: 3

Views: 115

Answers (4)

wxactly
wxactly

Reputation: 2470

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),
}

Upvotes: 0

nak
nak

Reputation: 3137

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

Upvotes: 1

wxactly
wxactly

Reputation: 2470

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.

Upvotes: 1

daleyjem
daleyjem

Reputation: 2674

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.

Upvotes: 1

Related Questions