首页 文章

你能在创建时将参数传递给AngularJS控制器吗?

提问于
浏览
276

我有一个控制器负责与API通信以更新用户的属性,名称,电子邮件等 . 每个用户都有一个 'id' ,它在查看 Profiles 页面时从服务器传递 .

我想将此值传递给AngularJS控制器,以便它知道当前用户的API入口点是什么 . 我试过传递 ng-controller 中的值 . 例如:

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

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

并在HTML中

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

其中 {% id %} 打印从服务器发送的id . 但我得到错误 .

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

15 回答

  • 1

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

    你的应用:

    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 .

  • 4

    The view should not dictate config

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

    The Alternative: Modules

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

    下面的示例有2个模块,重新使用相同的控制器,但每个模块都有自己的配置设置 . 使用 module.value 通过依赖注入传递该配置设置 . 这遵循角度方式,因为我们有以下内容:构造函数依赖注入,可重用的控制器代码,可重用的控制器模板(控制器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 .

  • 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);
    
        }
    
  • 139

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

  • 8

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

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

  • 0

    还有另一种方法可以通过将$ routeParams注入控制器然后使用此处描述的url参数将参数传递给控制器What's the most concise way to read query parameters in AngularJS?

  • 2

    这也有效 .

    使用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>
    
  • 15

    这是一个解决方案(基于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 App与默认的“访客用户”一起使用 . 关于为什么这种方法会变坏的任何建议都会受到欢迎 - 感觉太容易变得合情合理!)

  • 6

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

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

    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>
    
  • 8

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

    .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中检索它 .

  • 39

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

  • 14

    如果 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>
    

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

  • 357

    Notes:

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

    Original Answer:

    我回答这个是你绝对可以使用 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)
      };
    
    
    });
    
  • 0

    我已经很晚了,我不知道这是不是一个好主意,但你可以在控制器功能中包含 $attrs 注入,允许使用元素上提供的"arguments"来初始化控制器,例如

    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>
    

    再一次,不知道这是不是一个好主意,但它似乎有效,是另一种选择 .

  • 3

    这个问题已经很久了,但是我努力寻找能够满足我需求并且不容易找到它的问题的答案 . 我相信我的以下解决方案比目前接受的解决方案要好得多,也许是因为角度已经添加了功能,因为这个问题最初提出了 .

    简短的回答,使用Module.value方法可以将数据传递给控制器构造函数 .

    看我的plunker here

    我创建了一个模型对象,然后将其与模块的控制器关联,并使用名称“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”的参数,然后它可以访问该参数 .

    Controller

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

    笔记:

    我正在使用bootstrapping的手动初始化,它允许我在将模型发送到角度之前初始化我的模型 . 这与现有代码的效果非常好,因为您可以等待设置相关数据,并且只在需要时根据需要编译应用程序的角度子集 .

    在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) {
    

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

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

    • 传递给控制器的模型实际上是一个javascript对象,而不是一个被评估的字符串 . 它是对象的真实引用,对它的更改会影响对此模型对象的其他引用 .

    • Angular说接受的答案是's use of ng-init is a misuse, which this solution doesn' .

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

相关问题