我有一个控制器负责与API通信,以更新用户的属性,名称,电子邮件等。每个用户都有一个“id”,当查看配置文件页面时,这个“id”从服务器传递过来。

我想把这个值传递给AngularJS控制器,这样它就知道当前用户的API入口点是什么。我试着在ng-controller中传递这个值。例如:

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

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

在HTML中

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

其中{% id %}打印从服务器发送的id。但是会有错误。

在创建控制器时将值传递给控制器的正确方法是什么?


当前回答

我发现从$routeProvider传递变量很有用。

例如,你在多个屏幕上使用一个控制器MyController,在里面传递一些非常重要的变量“mySuperConstant”。

使用这个简单的结构:

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);

    }

其他回答

这个问题很老了,但我挣扎了很长时间,试图找到一个适合我需要的答案,但并不容易找到。我相信我下面的解决方案比目前接受的解决方案要好得多,也许是因为angular在最初提出这个问题时增加了功能。

简单的回答,使用模块。Value方法允许您将数据传递给控制器构造函数。

看我的活塞

我创建了一个模型对象,然后将它与模块的控制器相关联,引用它的名称为'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>

然后,我的控制器中的构造函数接受一个具有相同标识符“model”的参数,然后它可以访问这个标识符。

控制器

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

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

注:

我使用手动初始化bootstrapping,这允许我在将模型发送给angular之前初始化它。这对于现有的代码来说效果更好,因为你可以等待相关数据的设置,只在你想要的时候按需编译应用程序的angular子集。

在plunker中,我添加了一个按钮来提醒最初在javascript中定义并传递给angular的模型对象的值,只是为了确认angular真正引用了模型对象,而不是复制它并使用副本。

在这一行:

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

我将MyController对象传递到模块中。控制器函数,而不是声明为内联函数。我认为这可以让我们更清楚地定义控制器对象,但Angular文档倾向于内联,所以我认为有必要澄清一下。

我使用“controller as”语法并将值分配给MyController的“this”属性,而不是使用“$scope”变量。我相信使用$scope也能很好地工作,控制器赋值看起来是这样的:

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

控制器构造函数会有这样的签名:

function MyController ($scope, model) {

如果出于任何原因,您还可以将此模型作为第二个模块的值附加,然后将其作为依赖项附加到您的主模块。

我相信他的解决办法比目前公认的好得多,因为

传递给控制器的模型实际上是一个javascript对象,而不是要计算的字符串。它是对对象的真实引用,对它的更改会影响对该模型对象的其他引用。 Angular说,接受的答案使用ng-init是一种误用,而这个解决方案不会这样做。

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.

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.

从路线

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

它处理一个url,如'/mypage/bar'。正如你所看到的,foo是通过url param传递的,我们为$attrs提供了一个空白对象,因此注入器没有错误。

从视图

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

</div>

现在是控制器

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'

});

我对此很晚了,我不知道这是否是一个好主意,但你可以在控制器函数中包括$attrs注入,允许控制器使用元素上提供的“参数”进行初始化,例如。

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>

同样,不知道这是否是一个好主意,但它似乎有效,是另一种选择。

视图不应该指定配置

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.

替代方案:模块

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.

这里有一个例子:

<!-- 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>

查看它的实际运行情况:jsFiddle。

不,不可能。我认为你可以使用ng-init作为hack http://docs.angularjs.org/api/ng.directive:ngInit。