VBAHole
VBAHole

Reputation: 1518

angularjs passing variables into controller

I have my angular controller setup like most of the examples shown in the docs such that it is a global function. I assume that the controller class is being called when the angular engine sees the controller tag in the html.

My issue is that i want to pass in a parameter to my controller and i don't know how to do that because I'm not initializing it. I see some answers suggesting the use of ng-init. But my parameter is not a trivial string - it is a complex object that is being loaded by another (non-angular) part of my js. It is also not available right on load but takes a while to come along.

So i need a way to pass this object, when it finally finishes loading, into the controller (or scope) so that the controller can interact with it. Is this possible?

Upvotes: 2

Views: 6994

Answers (2)

satchmorun
satchmorun

Reputation: 12477

You can use a service or a factory for this, combined with promises:

You can setup a factory that returns a promise, and create a global function (accessible from 3rd-party JS) to resolve the promise.

Note the $rootScope.$apply() call. Angular won't call the then function of a promise until an $apply cycle. See the $q docs.

app.factory('fromoutside', function($window, $q, $rootScope) {
    var deferred = $q.defer();

    $window.injectIntoAngularWorld = function(obj) {
        deferred.resolve(obj);
        $rootScope.$apply();
    };

    return deferred.promise;
});

And then in your controller, you can ask for the fromoutside service and bind to the data when it arrives:

app.controller('main', function($scope, fromoutside) {
    fromoutside.then(function(obj) {
        $scope.data = obj;
    });
});

And then somewhere outside of Angular:

setTimeout(function() {
    window.injectIntoAngularWorld({
        A: 1,
        B: 2,
        C: 3
    });
}, 2000);

Here's a fiddle of this.

Personally, I feel this is a little bit cleaner than reaching into an Angular controller via the DOM.

EDIT: Another approach

Mark Rajcok asked in a comment if this could be modified to allow getting data more than once.

Now, getting data more than once could mean incremental updates, or changing the object itself, or other things. But the main things that need to happen are getting the data into the Angular world and then getting the right angular scopes to run their $digests.

In this fiddle, I've shown one way, when you might just be getting updates to an Array from outside of angular.

It uses a similar trick as the promise example above.

Here's the main factory:

app.factory('data', function($window) {
  var items = [];
  var scopes = [];

  $window.addItem = function(item) {
    items.push(item);
    angular.forEach(scopes, function(scope) {
        scope.$digest();
    });
  };

  return {
    items: items,
    register: function(scope) { scopes.push(scope); }
};

Like the previous example, we attach a function to the $window service (exposing it globally). The new bit is exposing a register function, which controllers that want updates to data should use to register themselves.

When the external JS calls into angular, we just loop over all the registered scopes and run a digest on each to make sure they're updated.

Upvotes: 8

Mark Rajcok
Mark Rajcok

Reputation: 364697

In your non-angular JavaScript, you can get access to the scope associated with a DOM element as follows:

angular.element(someDomElement).scope().someControllerFunction(delayedData);

I assume you can find someDomElement with a jQuery selector or something.

Upvotes: 5

Related Questions