Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to provide initial data to Angular's controller/$scope?

Tags:

angularjs

There seems to be no way to provide data to an Angular controller other than through attributes in the DOM handled by directives (of which ngInit is a handy example).

I'd like to provide other "constructor" data, e.g. objects with functions to my $scope.

Background: We have an existing dashboard-style single page application, where each widget manages a <div>, and widget-instance-specific data is provided as an object along with support functions, etc.. This object data doesn't fit nicely into DOM attributes or ngInit calls.

I can't really come up with a better way to it than to have a global hash, and use an instance-specific unique key. Before calling angular.bootstrap(domElement, ['myApp']), we set up all "constructor" parameters in this global hash under the key and then use

<div ng-init='readInitialValuesFromHash("uniqueKey")'>...</div>

where readInitialValuesFromHash gets all its data from globalHash["uniqueKey"] and stores what it needs it in $scope (possibly just the "uniqueKey").

(What seems like an alternative is to use a directive and jQuery.data(), but jQuery.data uses a global hash behind the scenes)

Of course I can hide the global data in a function, but fundamentally still use a singleton/global variable. This "global hash and pass key as param to ng init trick" just seems like such a hack...

Am I missing something? Is there a better way, given that the widget-instance-specific data is actually more complicated than suitable for inserting in the DOM via directives/attributes due to the legacy dashboard framework?

Are there dangers when putting complicated objects in the $scope as long as they aren't referenced by directives, {{}} or $scope.$watch() calls?

Angular's front page says:

Add as much or as little of AngularJS to an existing page as you like

So in light of that, how should I proceed?


EDIT: Comments have asked to make my question more clear. As an example of a non-trivial constructor parameter, assume I want to give this myObj to the controller, prototypical inheritance, function and all:

var proto = {
    p1: "p1",
    pf: function() {
        return "proto"
    }
};
function MyObj(ost) {
    this.ost = ost;
}
MyObj.prototype=proto;
var myObj = new MyObj("OST");

So I have myObj, and I have a string:

<div ng-app='myApp' ng-controller="MyCtrl">....</div>

I put the string in the DOM, and call angular.bootstrap().

How to I get the real myObj object into MyCtrl's $scope for this <div>, not a serialized/deserialized version/copy of it?

like image 259
Peter V. Mørch Avatar asked Sep 04 '26 18:09

Peter V. Mørch


1 Answers

Services is what you are looking for.

You can create your own services and then specify them as dependencies to your components (controllers, directives, filters, services), so Angular's dependency injection will take care of the rest.


Points to keep in mind:

  • Services are application singletons. This means that there is only one instance of a given service per injector. Since Angular is lethally allergic to global state, it is possible to create multiple injectors, each with its own instance of a given service, but that is rarely needed, except in tests where this property is crucially important.

  • Services are instantiated lazily. This means that a service will be created only when it is needed for instantiation of a service or an application component that depends on it. In other words, Angular won't instantiate services unless they are requested directly or indirectly by the application.

  • Services (which are injectable through DI) are strongly preferred to global state (what isn't), because they are much more testable (e.g. easily mocked etc) and "safer" (e.g. against accidental conflicts).


Relevant links:

  • Understanding Angular Services
  • Managing Service Dependencies
  • Creating Angular Services
  • Injecting Services into Controllers
  • Testing Angular Services
  • About Angular Dependency Injection

Example:

Depending on your exact requirements, it might be better to create one service to hold all configuration data or create one service per widget. In the latter case, it would probably be a good idea to include all services in a module of their own and specify it as a dependency of your main module.

var services = angular.module('myApp.services', []);
services.factory('widget01Srv', function () {
    var service = {};
    service.config = {...};

    /* Other widget01-specific data could go here,
     * e.g. functionality (but not presentation-related stuff) */
    service.doSomeSuperCoolStuff = function (someValue) {
        /* Send `someValue` to the server, receive data, process data */
        return somePrettyInterestingStuff;
    }
    ...
    return service;
}
services.factory('widget02Srv', function () {...}
...

var app = angular.module('myApp', ['myApp.services']);
app.directive('widget01', function ('widget01Srv') {
    return function postLink(scope, elem, attrs) {
        attrs.$set(someKey, widget01Srv.config.someKey);
        elem.bind('click', function () {
            widget01Srv.doSomeSuperCoolStuff(elem.val());
        });
        ...
    };
});
like image 124
gkalpak Avatar answered Sep 07 '26 14:09

gkalpak



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!