284

I have a controller responsible for communicating with an API to update properties of a user, name, email, etc. Each user has an 'id' which is passed from the server when the profile page is viewed.

I would like to pass this value to the AngularJS controller so it knows what the API entry point is for the current user. I've tried passing the value in ng-controller. For example:

function UserCtrl(id, $scope, $filter) {

$scope.connection = $resource('api.com/user/' + id)

and in the HTML

<body ng-controller="UserCtrl({% id %})">

where {% id %} print the id sent from the server. but I get errors.

What is the correct way to pass a value into a controller on its creation?

nickponline
  • 25,354
  • 32
  • 99
  • 167
  • 6
    if you had the id as part of the url then you could just read the url – akonsu Jan 25 '13 at 14:47
  • I had very similar problem and I solved it as I have posted in my answer. Sometimes using the libraries we overlook simple fundamental concept of JavaScript function call. – Jigar Patel Jan 25 '13 at 22:59
  • @nickponline After 21+ you still think that its not possible? – om471987 Jun 16 '13 at 17:22
  • Possible duplicate of [Pass variables to AngularJS controller, best practice?](http://stackoverflow.com/questions/11703477/pass-variables-to-angularjs-controller-best-practice) – T.Todua Sep 20 '16 at 11:29

17 Answers17

367

Notes:

This answer is old. This is just a proof of concept on how the desired outcome can be achieved. However, it may not be the best solution as per some comments below. I don't have any documentation to support or reject the following approach. Please refer to some of the comments below for further discussion on this topic.

Original Answer:

I answered this to Yes you absolutely can do so using ng-init and a simple init function.

Here is the example of it on plunker

HTML

<!DOCTYPE html>
<html ng-app="angularjs-starter">
  <head lang="en">
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
    <script src="app.js"></script>
  </head>  
  <body ng-controller="MainCtrl" ng-init="init('James Bond','007')">
    <h1>I am  {{name}} {{id}}</h1>
  </body>
</html>

JavaScript

var app = angular.module('angularjs-starter', []);

app.controller('MainCtrl', function($scope) {

  $scope.init = function(name, id)
  {
    //This function is sort of private constructor for controller
    $scope.id = id;
    $scope.name = name; 
    //Based on passed argument you can make a call to resource
    //and initialize more objects
    //$resource.getMeBond(007)
  };


});
Josh
  • 334
  • 1
  • 17
Jigar Patel
  • 4,585
  • 1
  • 17
  • 20
  • 5
    Thanks for this -- saved me a lot of head-scratching, and it worked perfectly for my situation. I had to initialize my controller with an object ID, and this was just right. – Masonoise Apr 12 '13 at 17:25
  • Is there a way to pass an object to the init method? – Leon Revill Apr 16 '13 at 09:29
  • @LeonRevill sure you can pass an object. `{name: "Name", address: {street: "123 Main St", city: "My City", state: "NY", zip: 123456}}` – Jigar Patel Apr 18 '13 at 17:50
  • @Bastian Because the user already accepted the first answer as an accepted answer even before reading my post or it might have happened before I posted my answer. – Jigar Patel Jul 16 '13 at 18:38
  • 27
    [From docs](http://docs.angularjs.org/api/ng.directive:ngInit): `The only appropriate use of ngInit for aliasing special properties of ngRepeat, as seen in the demo below. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.` – Serhii Holinei Nov 18 '13 at 13:03
  • 1
    @goliney Thanks for the documentation link. However, when I answered this question the document wasn't there. May be this is a good note for the future visitors. – Jigar Patel Nov 20 '13 at 03:24
  • 1
    @JigarPatel I decided to write own directive for that. Here is my approach: http://jsfiddle.net/goliney/89bLj/ – Serhii Holinei Nov 20 '13 at 09:36
  • @goliney You can certainly do that. There is no right or wrong way of doing it. I posted what I came up with. Your solution is also very approachable. I am wondering, how you would initialize an object type with an unknown set of fields. Or get some resource based on the provided parameter. Thanks for putting additional thoughts on this problem. – Jigar Patel Nov 22 '13 at 16:47
  • @JigarPatel unfortunatly my approach works only with pre defined objects into controller. – Serhii Holinei Nov 25 '13 at 21:53
  • 1
    As noted above, the docs recommend against this approach as a general solution for initializing controller state. – Shyam Habarakada Apr 08 '14 at 22:18
  • 8
    The answer makes it clear that the doc's recommend against this approach, but can someone point me to where the docs provide the official solution? – Michael Pell Jul 23 '14 at 23:57
  • 1
    @MichaelPell I have updated my answer to reflect my stand. I don't have any documentation to support or reject my approach. It was something other commentators mentioned in the thread so I felt obliged to warn any future visitors. – Jigar Patel Jul 24 '14 at 20:52
  • 57
    Well I think the docs are poor in just recommending against this approach without giving reasons why. I think this is a brilliant approach. If the framework authors don't want the framework to be used in what is to them a "wrong" way, then they should not make it possible to be used in that "wrong" way...and provide guidance as to the "right" way! – Shawn de Wet Sep 10 '14 at 06:04
  • 3
    a word of warning -- if you're trying to bind to a scope value, or really do anything that expects the value to be present in the controller function, it's already run the controller function before `ng-init` occurs -- see http://plnkr.co/edit/donCm6FRBSX9oENXh9WJ?p=preview – drzaus Oct 16 '14 at 18:38
  • 1
    For a simple use case like this I use module values. app.value('id', id); app.value('name', name); – dipu Jan 05 '15 at 06:35
  • 1
    Maybe it's recommended against because the controller (function) is initialized before the html part is parsed or the init func is called, thus decreasing performance – penguinsource Jan 06 '15 at 19:25
  • Seems to work only with constants. I've tried to pass an angularjs scope object. I think the object will be preced later. Right? – MR.ABC Jul 06 '15 at 07:22
  • @JigarPatel. Seems to work only with constants. I've tried to pass an angularjs scope object. I think the object will be proceed later. Right? Any solution for this problem? This will be really great! – MR.ABC Jul 06 '15 at 07:46
  • The docs now state that this is a valid use case for ng-init [(from here)](https://docs.angularjs.org/api/ng/directive/ngInit). – gavxn Dec 05 '18 at 11:06
148

I'm very late to this and I have no idea if this is a good idea, but you can include the $attrs injectable in the controller function allowing the controller to be initialized using "arguments" provided on an element, e.g.

app.controller('modelController', function($scope, $attrs) {
    if (!$attrs.model) throw new Error("No model for modelController");

    // Initialize $scope using the value of the model attribute, e.g.,
    $scope.url = "http://example.com/fetch?model="+$attrs.model;
})

<div ng-controller="modelController" model="foobar">
  <a href="{{url}}">Click here</a>
</div>

Again, no idea if this is a good idea, but it seems to work and is another alternative.

Michael Tiller
  • 9,291
  • 3
  • 26
  • 41
  • 3
    How would I pass an object using this approach? var obj = {a: 1, b: 2}; – Neil Sep 04 '14 at 10:12
  • 3
    This is a very reasonable solution to the problem of producing a hybrid app. – superluminary Sep 30 '14 at 09:55
  • 2
    @Neil: you have to stringify your json object and then parse it inside the controller. Not the nicer solution, but it may work. The Michael's solution is fine for string-like parameters... – M'sieur Toph' Oct 06 '14 at 14:51
  • you could combine this method with $parse to obtain an object. No idea if that is really a good approach or not though. – deweydb Oct 08 '14 at 23:02
  • 1
    this is actually more reliable than using `ng-init` -- if you're trying to bind to a scope value, it's already run the controller function before `ng-init` occurs -- see http://plnkr.co/edit/donCm6FRBSX9oENXh9WJ?p=preview – drzaus Oct 16 '14 at 18:37
  • This is great. I now have the same controller for three product pages, and at the beginning I can pass each product from the view in this way and reuse the controller code. But I still wonder why angular don't cover this, I'd like to see an explanation of the pitfalls of ng-init. – mwarren Jan 14 '15 at 10:58
  • @neil if your object exists on the parent scope of your controller, you don't need to pass it all. It should already be on the scope. – A F Feb 24 '15 at 01:21
  • 2
    I don't get all the reinventing the wheel with directives etc. If you have a controller that you want to use in multiple views with a few different initialization parameters, this is *easiest* and most reliable solution. – Eric H. Mar 16 '15 at 23:55
  • 1
    This is the way to go. I would prefix the attribute with `data-` though: `data-model=...`. So this is [HTML5 compliant](http://caniuse.com/#feat=dataset) (`data` prefix is removed by Anuglar/jQuery when accessing the `$attrs` service). – eightyfive May 08 '15 at 07:02
  • Not good solution: you cannot send $attrs, $routeParams, $stateParams or any kind of these objects. A controller must be independent of an injection method. Instead better to inject parameters itself, like function($scope, param_1, param_2) – Ivan Rave Jun 13 '15 at 06:41
  • Couple of Notes. First the attribute name should be model only, so someAttribute="foobar" would not work. Next if you want to pass more than one value, you should use model='{"apiUri": "/api/PatientCategoryApi/PatCat" ,"someOtherProp":"someOtherValue"}'. And in the controller you can have var someObject = JSON.parse($attrs.model); alert("The path is: " + someObject.apiUri);alert("Other prop is: " + someObject.someOtherProp); – VivekDev Oct 31 '15 at 07:51
  • With nested controllers, you can use `$scope.eval($attrs.model))` to pass an object through using this method (which avoids JSON stringification etc). [This gist](https://gist.github.com/CMCDragonkai/6282750) has some more details (aimed at directives primarily) – Mike Wade Jan 22 '18 at 09:45
  • This is a great answer, but if the controller is initialized from routing, it throws an $injector error `Unknown provider : $attrsProvider`. See my solution which fixes that and expands on this answer https://stackoverflow.com/a/54701367/1843673 – Moses Machua Feb 15 '19 at 01:18
39

This also works.

Javascript:

var app = angular.module('angularApp', []);

app.controller('MainCtrl', function($scope, name, id) {
    $scope.id = id;
    $scope.name = name;
    // and more init
});

Html:

<!DOCTYPE html>
<html ng-app="angularApp">
  <head lang="en">
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
    <script src="app.js"></script>
    <script>
       app.value("name", "James").value("id", "007");
    </script>
  </head>
  <body ng-controller="MainCtrl">
    <h1>I am  {{name}} {{id}}</h1>
  </body>
</html>
Jonghee Park
  • 1,257
  • 15
  • 14
  • 4
    This is a good solution that works with the existing constructor injection mechanism. You're, in essence, creating two simple services called 'name' and 'id'. The injector takes care of matching them by name during construction. See: the Value Recipe section of https://docs.angularjs.org/guide/providers – Todd Jun 15 '14 at 20:08
  • 1
    Nice. Note this works with objects as well, not just primitive types like strings. – jbustamovej Nov 27 '14 at 10:38
  • I like this solution better. I was able to modularize my duplicated codes via parameter passing. Cheers! – agentpx Sep 07 '15 at 08:21
  • This looks like the most idiomatic way, but I am not sure it is recommended by the angular team – VinGarcia Aug 18 '17 at 19:39
  • Does this essentially set up global key/value pairs? Can these be scoped to a given controller instances so that it is only picked up on child controllers under the main one? If not it seems like this isn't really much different than just setting a regular global Javascript variable at the window level. – jpierson Sep 11 '17 at 15:01
17

The view should not dictate config

In Angular, the template should never dictate configuration, which is inherently what people desire when they want to pass arguments to controllers from a template file. This becomes a slippery slope. If config settings are hard-coded in templates (such as by a directive or controller argument attribute), you can no longer re-use that template for anything but that single use. Soon you'll want to re-use that template, but with different config and now in order to do so you'll either be pre-processing the templates to inject variables before it gets passed to angular or using massive directives to spit out giant blocks of HTML so you re-use all of the controller HTML except for the wrapper div and it's arguments. For small projects it's no big deal. For something big (what angular excels at), it gets ugly quick.

The Alternative: Modules

This type of configuration is what modules were designed to handle. In many angular tutorials people have a single module for their entire application, but really the system is designed and fully supports many small modules each which wrap small pieces of the total application. Ideally, controllers, modules etc would be declared in separate files and stitched together in specific re-usable chunks. When your application is designed this way, you get a lot of re-use in addition to easy controller arguments.

The example below has 2 modules, re-using the same controller, but each with their own config settings. That config settings are passed in via dependency injection using module.value. This adheres to the angular way because we have the following: constructor dependency injection, reusable controller code, reusable controller templates (the controller div could easily be included with ng-include), easily unit-testable system without HTML, and lastly re-usable modules as the vehicle for stitching the pieces together.

Here's an example:

<!-- index.html -->
<div id="module1">
    <div ng-controller="MyCtrl">
        <div>{{foo}}</div>
    </div>
</div>
<div id="module2">
    <div ng-controller="MyCtrl">
        <div>{{foo}}</div>
    </div>
</div>
<script>
    // part of this template, or a JS file designed to be used with this template
    angular.element(document).ready(function() {
        angular.bootstrap(document.getElementById("module1"), ["module1"]);
        angular.bootstrap(document.getElementById("module2"), ["module2"]);
    });
</script>

<!-- scripts which will likely in be in their seperate files -->
<script>
    // MyCtrl.js
    var MyCtrl = function($scope, foo) {
    $scope.foo = foo;
    }

    MyCtrl.$inject = ["$scope", "foo"];

    // Module1.js
    var module1 = angular.module('module1', []);
    module1.value("foo", "fooValue1");
    module1.controller("MyCtrl", MyCtrl);

    // Module2.js file
    var module2 = angular.module('module2', []);
    module2.value("foo", "fooValue2");
    module2.controller("MyCtrl", MyCtrl);
</script>

See it in action: jsFiddle.

Owen Allen
  • 11,348
  • 9
  • 51
  • 63
  • 1
    i already upvoted, but i want to voice my thanks for your contribution. this answer set me on a better path. stackoverflow is best when people share best practices and not just hacky snippets. this is brilliant: " the template should never dictate configuration, which is inherently what people desire when they want to pass arguments to controllers from a template file". thank you for having laser vision into the heart of the matter. – pestophagous May 16 '20 at 02:50
15

Like @akonsu and Nigel Findlater suggest, you can read the url where url is index.html#/user/:id with $routeParams.id and use it inside the controller.

your app:

var app = angular.module('myApp', [ 'ngResource' ]);

app.config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/:type/:id', {templateUrl: 'myView.html', controller: 'myCtrl'});
}]);

the resource service

app.factory('MyElements', ['$resource', function($resource) {
     return $resource('url/to/json/:type/:id', { type:'@type', id:'@id' });
}]);

the controller

app.controller('MyCtrl', ['$scope', '$routeParams', 'MyElements', function($scope, $routeParams, MyElements) {
    MyElements.get({'type': $routeParams.type, "id": $routeParams.id }, function(elm) {
        $scope.elm = elm;
    })
}]);

then, elm is accessible in the view depending on the id.

François Romain
  • 13,617
  • 17
  • 89
  • 123
8

If ng-init is not for passing objects into $scope, you can always write your own directive. So here is what I got:

http://jsfiddle.net/goliney/89bLj/

Javasript:

var app = angular.module('myApp', []);
app.directive('initData', function($parse) {
    return function(scope, element, attrs) {
        //modify scope
        var model = $parse(attrs.initData);
        model(scope);
    };
});

function Ctrl1($scope) {
    //should be defined
    $scope.inputdata = {foo:"east", bar:"west"};
}

Html:

<div ng-controller="Ctrl1">
    <div init-data="inputdata.foo=123; inputdata.bar=321"></div>
</div>

But my approach can only modify objects, which are already defined at controller.

Serhii Holinei
  • 5,758
  • 2
  • 32
  • 46
8

It looks like the best solution for you is actually a directive. This allows you to still have your controller, but define custom properties for it.

Use this if you need access to variables in the wrapping scope:

angular.module('myModule').directive('user', function ($filter) {
  return {
    link: function (scope, element, attrs) {
      $scope.connection = $resource('api.com/user/' + attrs.userId);
    }
  };
});

<user user-id="{% id %}"></user>

Use this if you don't need access to variables in the wrapping scope:

angular.module('myModule').directive('user', function ($filter) {
  return {
    scope: {
      userId: '@'
    },
    link: function (scope, element, attrs) {
      $scope.connection = $resource('api.com/user/' + scope.userId);
    }
  };
});

<user user-id="{% id %}"></user>
btesser
  • 181
  • 2
  • 2
7

I found passing variables from $routeProvider usefull.

For example, you use one controller MyController for multiple screens, passing some very important variable "mySuperConstant" inside.

Use that simple structure:

Router:

$routeProvider
            .when('/this-page', {
                templateUrl: 'common.html',
                controller: MyController,
                mySuperConstant: "123"
            })
            .when('/that-page', {
                templateUrl: 'common.html',
                controller: MyController,
                mySuperConstant: "456"
            })
            .when('/another-page', {
                templateUrl: 'common.html',
                controller: MyController,
                mySuperConstant: "789"
            })

MyController:

    MyController: function ($scope, $route) {
        var mySuperConstant: $route.current.mySuperConstant;
        alert(mySuperConstant);

    }
Dmitri Algazin
  • 3,332
  • 27
  • 30
6

You can do it when setting up the routes for e.g.

 .when('/newitem/:itemType', {
            templateUrl: 'scripts/components/items/newEditItem.html',
            controller: 'NewEditItemController as vm',
            resolve: {
              isEditMode: function () {
                return true;
              }
            },
        })

And later use it as

(function () {
  'use strict';

  angular
    .module('myApp')
    .controller('NewEditItemController', NewEditItemController);

  NewEditItemController.$inject = ['$http','isEditMode',$routeParams,];

  function NewEditItemController($http, isEditMode, $routeParams) {
    /* jshint validthis:true */

    var vm = this;
    vm.isEditMode = isEditMode;
    vm.itemType = $routeParams.itemType;
  }
})();

So here when we set up the route we sent :itemType and retrive it later from $routeParams.

ssaini
  • 71
  • 1
  • 6
4

There is another way to pass parameters to a controller by injecting $routeParams into your controller and then using url parameters described here What's the most concise way to read query parameters in AngularJS?

Community
  • 1
  • 1
Nigel Findlater
  • 1,684
  • 14
  • 34
3

This question is old but I struggled for a long time trying to get an answer to this problem that would work for my needs and did not easily find it. I believe my following solution is much better than the currently accepted one, perhaps because angular has added functionality since this question was originally posed.

Short answer, using the Module.value method allows you to pass data into a controller constructor.

See my plunker here

I create a model object, then associate it with the module's controller, referencing it with the name 'model'

HTML / JS

  <html>
  <head>
    <script>
      var model = {"id": 1, "name":"foo"};

      $(document).ready(function(){
        var module = angular.module('myApp', []);
        module.value('model', model);
        module.controller('MyController', ['model', MyController]);
        angular.bootstrap(document, ['myApp']);
      });

      function confirmModelEdited() {
        alert("model name: " + model.name + "\nmodel id: " + model.id);
      }
    </script>

  </head>
  <body >
      <div ng-controller="MyController as controller">
        id: {{controller.model.id}} <br>
        name: <input ng-model="controller.model.name"/>{{controller.model.name}}
        <br><button ng-click="controller.incrementId()">increment ID</button>
        <br><button onclick="confirmModelEdited()">confirm model was edited</button>
    </div>
  </body>

</html>

The constructor in my controller then accepts a parameter with that same identifier 'model' which it can then access.

Controller

function MyController (model) {
  this.model = model;
}

MyController.prototype.incrementId = function() {
  this.model.id = this.model.id + 1;
}

Notes:

I'm using manual initialization of bootstrapping, which allows me to initialize my model before sending it over to angular. This plays much more nicely with existing code, as you can wait to set up your relevant data and only compile the angular subset of your app on demand when you want to.

In the plunker I've added a button to alert the values of the model object that was initially defined in javascript and passed to angular, just to confirm that angular is truly referencing the model object, rather than copying it and working with a copy.

On this line:

module.controller('MyController', ['model', MyController]);

I'm passing the MyController object into the Module.controller function, rather than declaring as a function inline. I think this allows us to far more clearly define our controller object, but Angular documentation tends to do it inline so I thought it bears clarification.

I'm using the "controller as" syntax and assigning values to the "this" property of MyController, rather than using the "$scope" variable. I believe this would work fine using $scope just as well, the controller assignment would then look something like this:

module.controller('MyController', ['$scope', 'model', MyController]);

and the controller constructor would have a signature like this:

function MyController ($scope, model) {

If for whatever reason you wanted to, you could also attach this model as a value of a second module, which you then attach as a dependency to your primary module.

I believe his solution is much better than the currently accepted one because

  1. The model passed to the controller is actually a javascript object, not a string that gets evaluated. It is a true reference to the object and changes to it affect other references to this model object.
  2. Angular says that the accepted answer's use of ng-init is a misuse, which this solution doesn't do.

The way Angular seems to work in most all other examples I've seen has the controller defining the data of the model, which never made sense to me, there is no separation between the model and the controller, that doesn't really seem like MVC to me. This solution allows you to really have a completely separate model object which you pass into the controller. Also of note, if you use the ng-include directive you can put all your angular html in a separate file, fully separating your model view and controller into separate modular pieces.

O. Winter
  • 66
  • 6
2

If using angular-ui-router, then this is the correct solution: https://github.com/angular-ui/ui-router/wiki#resolve

Basically, you declare a set of dependecies to "resolve" before the controller is instantiated. You may declare dependencies for each of your "states". These dependencies are then passed in the controller's "constructor".

chesscov77
  • 770
  • 8
  • 14
1

One way of doing that would be having a separate service that can be used as a 'vessel' for those arguments where they're public data members.

Marcin Wyszynski
  • 2,188
  • 1
  • 17
  • 16
  • Best answer, IMO. I currently also use browser storage in some cases for those users that dare to hit F5, so this state is maintained. – Dormouse Feb 12 '15 at 14:43
1

This is an expansion of @Michael Tiller's excellent answer. His answer works for initializing variables from the view by injecting the $attrs object into the controller. The problem occurs if the same controller is called from $routeProvider when you navigate by routing. Then you get injector error Unknown provider : $attrsProvider because $attrs is only available for injection when the view is compiled. The solution is to pass the variable (foo) through $routeParams when initializing controller from route and by $attrs when initializing controller from view. Here's my solution.

From Route

$routeProvider.
        when('/mypage/:foo', {
            templateUrl: 'templates/mypage.html',
            controller: 'MyPageController',
            caseInsensitiveMatch: true,
            resolve: {
                $attrs: function () {
                    return {};
                }
            }
        });

This handles a url such as '/mypage/bar'. As you can see foo is passed by url param and we provide the $injector with a blank object for $attrs so no injector errors.

From View

<div ng-controller="MyPageController" data-foo="bar">

</div>

Now the controller

var app = angular.module('myapp', []);
app.controller('MyPageController',['$scope', '$attrs', '$routeParams'], function($scope, $attrs, $routeParams) {
   //now you can initialize foo. If $attrs contains foo, it's been initialized from view
   //else find it from $routeParams
   var foo = $attrs.foo? $attrs.foo : $routeParams.foo;
   console.log(foo); //prints 'bar'

});
Moses Machua
  • 11,245
  • 3
  • 36
  • 50
1

I didn't really like any of the solutions here for my particular use case, so I figured I'd post what I did because I didn't see it here.

I simply wanted to use a controller more like a directive, within a ng-repeat loop:

<div ng-repeat="objParameter in [{id:'a'},{id:'b'},{id:'c'}]">
  <div ng-controller="DirectiveLikeController as ctrl"></div>
</div>

Now, in order to access the objParameter on creation within each DirectiveLikeController (or to get the up-to-date objParameter at ANY time), all I need to do is inject $scope and call $scope.$eval('objParameter'):

var app = angular.module('myapp', []);
app.controller('DirectiveLikeController',['$scope'], function($scope) {
   //print 'a' for the 1st instance, 'b' for the 2nd instance, and 'c' for the 3rd.
   console.log($scope.$eval('objParameter').id); 
});

The only real downside that I see is that it requires the parent controller to know that the parameter is named objParameter.

JohnTD
  • 69
  • 1
  • 5
0

No, It is not possible. I think you can use ng-init as hack http://docs.angularjs.org/api/ng.directive:ngInit.

SunnyShah
  • 28,934
  • 30
  • 90
  • 137
  • 4
    I beg to differ but you can accomplish this using ng-init and using an init fuction. – Jigar Patel Jan 25 '13 at 22:41
  • a word of warning -- if you're trying to bind to a scope value, or really do anything that expects the value to be present in the controller function, it's already run the controller function before `ng-init` occurs -- see http://plnkr.co/edit/donCm6FRBSX9oENXh9WJ?p=preview – drzaus Oct 16 '14 at 18:39
  • Yes it is possible. At the very least, you would use data- params or whatever. But its definitely possible. – chesscov77 Jul 06 '15 at 22:26
0

Here is a solution (based on Marcin Wyszynski's suggestion) which works where you want to pass a value into your controller but you aren't explicitly declaring the controller in your html (which ng-init seems to require) - if, for example, you are rendering your templates with ng-view and declaring each controller for the corresponding route via routeProvider.

JS

messageboard.directive('currentuser', ['CurrentUser', function(CurrentUser) {
  return function(scope, element, attrs) {
    CurrentUser.name = attrs.name;
  };
}]);

html

<div ng-app="app">
  <div class="view-container">
    <div ng-view currentuser name="testusername" class="view-frame animate-view"></div>
  </div>
</div>

In this solution, CurrentUser is a service which can be injected into any controller, with the .name property then available.

Two notes:

  • a problem I've encountered is that .name gets set after the controller loads, so as a workaround I have a short timeout before rendering username on the controller's scope. Is there a neat way of waiting until .name has been set on the service?

  • this feels like a very easy way to get a current user into your Angular App with all the authentication kept outside Angular. You could have a before_filter to prevent non-logged in users getting to the html where your Angular app is bootstrapped in, and within that html you could just interpolate the logged in user's name and even their ID if you wanted to interact with the user's details via http requests from your Angular app. You could allow non-logged in users to use the Angular App with a default 'guest user'. Any advice on why this approach would be bad would be welcome - it feels too easy to be sensible!)

Ollie H-M
  • 485
  • 5
  • 17