1

When my angular app first loads, I need to make a couple requests for some fairly large data. This data is then used in controllers and services to determine how to display the page. I'm getting all sorts of JavaScript errors because the controllers and services are trying to access this data before the request has returned.

I know in the HTML pages, you can bind to this asynchronous data, and angular will populate it for you when the request returns. However, I have some pretty complex logic happening in my controllers and services based on this data. So, I guess I have 2 questions.

  1. Is there anyway to somehow work with asynchronous data in the controllers and services?
  2. Is there a way to prevent angular from trying to render the page until after the data has returned? This basically makes the data requests synchronous, and I am ok with that. This would only be on the initial full HTML page load.

My attempt at a jsFiddle showing this can be found here: http://jsfiddle.net/sES9T/

2 Answers 2

3

The basic flow should be like this: Inside your controller function, make the $http request, and in the success() callback, set your $scope variables as needed. Is your flow different from that?

Also, use the ng-show directive to hide your DOM elements until the scope has the data you need.

Your controller will look something like this:

MyCtrl = ( $scope, $http ) ->
  promise = $http.get "/url"
  promise.success ( data ) ->
    $scope.foo = data

And your view will look like this:

div(ng-show="foo")
  p Here is my data, and some other stuff
  p {{ foo }}

EDIT

I've modified your jsfiddle here:

http://jsfiddle.net/sES9T/4/

It works as expected, the problem was that you were trying to access properties on undefined variables, which was throwing an error. Also, on the third test, the variable was named wrong, but still needs to be checked when named right.

I'd recommend coffeescript, since those if statements can be made a lot more terse with the existential (?) operator, like so:

myLargeData?.data

That code will check if myLargeData is defined and non-null before trying to access it's properties, like I had done manually above. If it IS undefined or null, it will return undefined, instead of causing an error.

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

7 Comments

In my top-level controller, I am doing something like this $scope.myLargeData = $http.get("/foo/bar"); Would doing $http.get("/foo/bar").success(function(data) {$scope.myLargeData = data;}); be any different? I would think I'd still have the problem of $scope.myLargeData being accessed before it is populated.
Yes, definitely use the callback method. The $http.get() method returns a promise object (with the success and failure methods to bind with), not the actual data you need. Also, keep in mind that whatever code comes after that line will execute before you actually get the data. So all your logic that relies on that data should be inside that success callback function.
Right, I understand that. I guess I should have been more clear. I do the above and then do some bindings in the HTML page and then pass that data into lower level controllers. I did change my code to set the data in the success(), but I'm still getting the same JS errors because the other controllers are trying to access it before it's populated. I'll try putting a jsfiddle together to make what I'm doing more clear.
Yeah, a jsfiddle would be good to see the specific problem you're facing.
So, if I just check to see if it's undefined to prevent the javascript errors, all that stuff will be executed again once the request has returned and populate everything correctly? Or I guess if it doesn't happen automatically, I could always put a $scope.$apply() in the success() method.
|
0

I would suggest you to use router's resolve in order to load and process all required data. The controller will be executed only when all resolves are actually resolved. So the view will not be rendered at all, until your requests are completed and data is processed. I do believe this is a more elegant and recommended approach.

Also, you should extract all your data loading and processing logic into a separate service and relay the work to your service from the resolve. Your service should return a promise object that will be resolved when all processing is complete.

Please see the documentation for $routeProvider (the "resolve" property).

Comments

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.