282

我有一个控制器负责与 API 通信以更新用户、姓名、电子邮件等的属性。每个用户都有一个'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。但我得到错误。

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

4

17 回答 17

366

笔记:

这个答案很旧。这只是关于如何实现预期结果的概念证明。但是,根据下面的一些评论,它可能不是最佳解决方案。我没有任何文件支持或拒绝以下方法。请参阅下面的一些评论,以进一步讨论该主题。

原答案:

我回答说是的,你绝对可以使用ng-init一个简单的 init 函数来做到这一点。

这是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)
  };


});
于 2013-01-25T22:56:53.043 回答
147

我已经很晚了,我不知道这是否是一个好主意,但是您可以$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>

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

于 2014-05-12T15:13:43.520 回答
39

这也有效。

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>
于 2014-03-15T13:46:53.417 回答
17

视图不应指定配置

在 Angular 中,模板永远不应该指定配置,这本质上是人们想要从模板文件向控制器传递参数时所希望的。这变成了一个滑坡。如果配置设置在模板中是硬编码的(例如通过指令或控制器参数属性),则您不能再将该模板重复用于除单一用途之外的任何用途。很快你就会想要重新使用那个模板,但是使用不同的配置,现在为了做到这一点,你要么在模板被传递给 angular 之前预处理模板以注入变量,要么使用大量指令来吐出巨大的HTML 块,因此您可以重复使用除包装器 div 及其参数之外的所有控制器 HTML。对于小型项目,这没什么大不了的。对于一些大的东西(角度擅长的东西),它很快就会变得丑陋。

替代方案:模块

这种类型的配置是模块被设计来处理的。在许多 Angular 教程中,人们的整个应用程序只有一个模块,但实际上系统是经过设计的,并且完全支持许多小模块,每个小模块都包含整个应用程序的一小部分。理想情况下,控制器、模块等将在单独的文件中声明,并在特定的可重用块中拼接在一起。当您的应用程序以这种方式设计时,除了简单的控制器参数之外,您还可以获得很多重用。

下面的示例有 2 个模块,重复使用相同的控制器,但每个模块都有自己的配置设置。该配置设置通过依赖注入使用module.value. 这遵循了 angular 方式,因为我们有以下内容:构造函数依赖注入、可重用的控制器代码、可重用的控制器模板(控制器 div 可以很容易地包含在 ng-include 中)、无需 HTML 的易于单元测试的系统,最后是可重用的模块作为将零件缝合在一起的工具。

这是一个例子:

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

于 2014-12-30T22:22:08.187 回答
15

就像@akonsu 和 Nigel Findlater 建议的那样,您可以读取 url 所在的 urlindex.html#/user/:id$routeParams.id在控制器中使用它。

你的应用:

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

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

资源服务

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

控制器

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

然后,elm可以在视图中访问,具体取决于id.

于 2014-02-13T10:23:49.793 回答
8

如果ng-init不是为了将对象传递给$scope,您可以随时编写自己的指令。所以这就是我得到的:

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>

但是我的方法只能修改已经在控制器中定义的对象。

于 2013-11-25T22:00:59.587 回答
8

看起来对您来说最好的解决方案实际上是一个指令。这允许您仍然拥有控制器,但为其定义自定义属性。

如果您需要访问包装范围内的变量,请使用它:

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>

如果您不需要访问包装范围内的变量,请使用此选项:

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>
于 2014-11-26T09:47:55.113 回答
7

我发现从 $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);

    }
于 2014-12-01T11:31:10.110 回答
6

您可以在设置路线时执行此操作,例如

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

稍后将其用作

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

因此,当我们设置路由时,我们发送 :itemType 并稍后从 $routeParams 中检索它。

于 2015-07-06T20:18:56.150 回答
4

还有另一种将参数传递给控制器​​的方法,方法是将 $routeParams 注入控制器,然后使用此处描述的 url 参数在 AngularJS 中读取查询参数的最简洁方法是什么?

于 2013-09-23T12:50:59.673 回答
3

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

简短的回答,使用 Module.value 方法允许您将数据传递到控制器构造函数中。

在这里看到我的 plunker

我创建了一个模型对象,然后将它与模块的控制器相关联,并用名称“模型”引用它

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>

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

控制器

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 对象传递给 Module.controller 函数,而不是声明为内联函数。我认为这使我们能够更清楚地定义我们的控制器对象,但是 Angular 文档倾向于内联,所以我认为它需要澄清。

我正在使用“controller as”语法并将值分配给 MyController 的“this”属性,而不是使用“$scope”变量。我相信使用 $scope 也可以正常工作,控制器分配看起来像这样:

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

并且控制器构造函数将具有如下签名:

function MyController ($scope, model) {

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

我相信他的解决方案比目前接受的解决方案要好得多,因为

  1. 传递给控制器​​的模型实际上是一个 javascript 对象,而不是被评估的字符串。它是对对象的真实引用,对它的更改会影响对该模型对象的其他引用。
  2. Angular 说,公认的答案对 ng-init 的使用是一种误用,而这个解决方案没有这样做。

在我见过的大多数其他示例中,Angular 的工作方式似乎是控制器定义了模型的数据,这对我来说从来没有意义,模型和控制器之间没有分离,这看起来并不像MVC 对我来说。该解决方案允许您真正拥有一个完全独立的模型对象,并将其传递给控制器​​。另外值得注意的是,如果您使用 ng-include 指令,您可以将所有 angular html 放在一个单独的文件中,将您的模型视图和控制器完全分离为单独的模块化部分。

于 2017-02-09T18:45:24.553 回答
2

如果使用 angular-ui-router,那么这是正确的解决方案:https ://github.com/angular-ui/ui-router/wiki#resolve

基本上,您在实例化控制器之前声明一组要“解决”的依赖项。您可以为每个“状态”声明依赖项。然后将这些依赖项传递到控制器的“构造函数”中。

于 2015-07-06T22:26:00.607 回答
1

这样做的一种方法是拥有一个单独的服务,该服务可以用作那些作为公共数据成员的参数的“容器”。

于 2014-07-10T15:40:00.470 回答
1

这是@Michael Tiller出色答案的扩展。他的答案适用于通过将$attrs对象注入控制器来初始化视图中的变量。$routeProvider如果在通过路由导航时调用同一个控制器,则会出现问题。然后你会得到注入器错误Unknown provider : $attrsProvider,因为$attrs只有在编译视图时才可用于注入。$routeParams解决方案是在从路由$attrs初始化控制器和从视图初始化控制器时传递变量(foo) 。这是我的解决方案。

从路线

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

这处理一个 url,例如'/mypage/bar'. 如您所见, foo 是通过 url 参数传递的,我们为 foo 提供了$injector一个空白对象,$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'

});
于 2019-02-15T01:08:09.357 回答
1

对于我的特定用例,我真的不喜欢这里的任何解决方案,所以我想我会发布我所做的,因为我在这里没有看到它。

我只是想在 ng-repeat 循环中使用更像指令的控制器:

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

现在,为了访问objParameter每个 DirectiveLikeController 中的创建(或随时获取最新的 objParameter),我需要做的就是注入 $scope 并调用$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); 
});

我看到的唯一真正的缺点是它需要父控制器知道参数是命名的objParameter

于 2019-04-18T18:36:59.523 回答
0

不,这是不可能的。我认为您可以将 ng-init 用作 hack http://docs.angularjs.org/api/ng.directive:ngInit

于 2013-01-25T14:31:20.560 回答
0

这是一个解决方案(基于 Marcin Wyszynski 的建议),它适用于您想要将值传递到控制器但您没有在 html 中明确声明控制器(ng-init 似乎需要)的地方 - 例如,如果,您正在使用 ng-view 渲染模板,并通过 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>

在这个解决方案中,CurrentUser 是一个服务,可以注入到任何控制器中,然后可以使用 .name 属性。

两个注意事项:

  • 我遇到的一个问题是 .name 在控制器加载后被设置,因此作为一种解决方法,我在控制器范围内呈现用户名之前有一个短暂的超时。是否有一种巧妙的方法可以等到在服务上设置了 .name ?

  • 这感觉像是让当前用户进入您的 Angular 应用程序的一种非常简单的方法,而所有身份验证都保留在 Angular 之外。您可以有一个 before_filter 来防止未登录的用户访问您的 Angular 应用程序引导所在的 html,如果您想与用户的详细信息交互,您可以在该 html 中插入登录用户的名称甚至他们的 ID通过来自 Angular 应用程序的 http 请求。您可以允许未登录的用户通过默认的“访客用户”使用 Angular 应用程序。任何关于为什么这种方法不好的建议都会受到欢迎 - 感觉太容易明智了!)

于 2016-09-02T15:39:07.670 回答