1

I have an ajax call that fetches some JSON by hitting my controller:

class @Team
  constructor: () ->
    @players = null
    @getTeamInfo()

getTeamInfo:(teamId) ->
  request = $.ajax
  dataType: 'json',
  type: 'GET'
  url: "http://localhost:4000/teams/#{teamId}",
  async: false
  success : (data) =>
    _.each(data.players, (value) ->
      name = _.pluck(value, 'name')
      @players.push(new Player(name)))

The problem is that I need the players from the very start, and, seeing how JS runs asynchronously, the variables are null. Is there a way I can actually initialize variables with an ajax call?

UPDATE: After getting a new perspective on the issue from @mu_is_too_short and understanding the real importance of callbacks when doing ajax calls from @Rich Peck, I was able to get my game initializing by ensuring that all the code that depended on the data returned from the Ajax call was included in the Ajax success callback. I think I may have to include all the data that I will need to build the game, not just the team and player data. But that's later. Thanks, guys.

class @Game

  constructor: (homeId, awayId) ->
    @homeTeam = new Team()
    @awayTeam = new Team()
    @display  = new GameDisplay()
    @pitcher  = new Pitching()
    @contact  = new Contact()
    @baseRunners = new BaseRunners()
    @gameEngine  = new GameEngine(@homeTeam, @awayTeam, @display, @pitcher, @contact, @baseRunners)
    @initializeHomeBattingOrder(1, 3)

  pitch: ->
    @gameEngine.makePitch()

  initializeHomeBattingOrder: (homeId, awayId) ->
    $.ajax
      dataType: 'json',
      type: 'GET',
      url: "http://localhost:4000/teams/#{homeId}",
      success : (data) =>
        @populateHomePlayers(data)
        @initializeAwayBattingOrder(awayId)

  initializeAwayBattingOrder: (awayId) ->
    $.ajax
      dataType: 'json',
      type: 'GET',
      url: "http://localhost:4000/teams/#{awayId}",
      success : (data) =>
        @populateAwayPlayers(data)
        @display.battingOrder(@awayTeam.players, @homeTeam.players)
        @display.teamsPlaying(@awayTeam.name, @homeTeam.name)

  populateHomePlayers: (data) ->
    @homeTeam.players = _.map(_.pluck(data.players, "name"), (name) -> new Player(name))
    @homeTeam.name = data.name

  populateAwayPlayers: (data) ->
    @awayTeam.players = _.map(_.pluck(data.players, "name"), (name) -> new Player(name))
    @awayTeam.name = data.name
steve_gallagher
  • 3,778
  • 8
  • 33
  • 51
  • Is that your real whitespace or is `getTeamInfo` really indented to the same level as `constructor`? And once you're in AJAX land, you're pretty much stuck in callback hell unless you hate and despise your users enough to use synchronous calls. – mu is too short Nov 07 '13 at 05:43
  • @muistooshort my whitespace was off, sorry, fixed it. Thanks for looking. How the hell (I've been banging away at this one little issue for about three hours) do I just get the data in my models (as JSON) into the JS side so that I can initialize some objects. I mean, I have a team model with players, I can get the JSON back okay, but the timing is off, somewhat new to JS land, wrapping my head around asynch is going to take awhile, I've looked at a ton of stuff on the web but they are for more "normal" web dev operations, I guess initializing a game is non-standard, sorry for the long comment – steve_gallagher Nov 07 '13 at 05:54
  • This might make interesting reading for you: http://jfire.io/blog/2012/04/30/how-to-securely-bootstrap-json-in-a-rails-view/ – mu is too short Nov 07 '13 at 06:03
  • @muistooshort That article was interesting. This is a different approach than I was expecting, embedding the JSON directly into the page. I'm using HAML templates for the rendering, do I need something like js.erb views? – steve_gallagher Nov 07 '13 at 06:17
  • 1
    That's a pretty standard way to bootstrap data and you do want to bootstrap everything you need to get things started. I don't know HAML but presumably it can produce ``. Then make sure your code is wrapped in a `$ ->` document-ready wrapper and grab `window.bootstrap_data` when you need it. – mu is too short Nov 07 '13 at 06:29
  • @muistooshort: there's `:javascript` filter in HAML: http://haml.info/docs/yardoc/file.REFERENCE.html#javascript-filter – Sergio Tulentsev Nov 07 '13 at 06:31

1 Answers1

4

I was going to write this as a comment, but I figured it would be easier to read in an answer. I'll delete if inappropriate, as I don't think I can provide a direct solution, rather some more info about working with Ajax


Asychronous Javascript And XML

Ajax is designed to run asynchronously by default, and actually freezes your browser if it's forced to run synchronously (by using async: false)

We found this out the hard way, as we were trying to find a way to use it to call some variables & discovered you cannot "just use ajax" -- you have to design around it


Using Callbacks With Ajax

We wanted to render a form "on the fly", which required data to be called by Ajax. The problem was that instead of just fitting Ajax into the exiting code, we had to refactor it so that the Ajax part of the code ran independently to the "static" parts. We fixed it by using ajax callbacks

Here's our code:

 function create_modal(o){

                //o.ajax = the link to use. If present, it means we use Ajax :)

            if(o.ajax){
            //Loading

            fetch_modal(o.ajax, function(data){

                var modal = document.createElement("div");
                modal.setAttribute("id", o.modal_id.substring(1));
                modal.className = 'modal ajax'; 
                modal.innerHTML = data;

                $("body").append(modal);
                overlay();
                show_modal(o);

            }, function(data){
                //error
            });
        }else{
            overlay();
            show_modal(o);
        }
}

function fetch_modal(link, success, error) {
    $.ajax({
        url: link,
        success: function(data) { success(data); },
        error: function(data)   { error(data); }
    });
}

You can see a demo of this working at http://emailsystem.herokuapp.com (register & then click the "New List" / "New Broadcast" / "New Subscriber" button) -- that form is rendered through ajax


CoffeeScript formatting aside, I think the best thing you can do is to employ a callback function for Ajax, and make Ajax the central component in your function, like this:

class @Team
  constructor: () ->
    @players = null
    @getTeamInfo(teamId, ->

      _.each(data.players, (value) ->
      name = _.pluck(value, 'name')
      @players.push(new Player(name)))
    , -> 
      //error


getTeamInfo:(teamId, success, error) ->
  request = $.ajax
  dataType: 'json'
  type: 'GET'
  url: "teams/#{teamId}"
  success : (data) => success(data)
  error : (data) => error(data)
Community
  • 1
  • 1
Richard Peck
  • 76,116
  • 9
  • 93
  • 147