ChatGPT解决这个技术问题 Extra ChatGPT

您可以在创建时将参数传递给 AngularJS 控制器吗?

我有一个控制器负责与 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。但我得到错误。

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

如果您将 id 作为 url 的一部分,那么您只需读取 url
我有非常相似的问题,我解决了我在我的答案中发布的问题。有时使用库我们会忽略 JavaScript 函数调用的简单基本概念。
@nickponline 21+ 岁之后,您仍然认为这是不可能的吗?

J
Josh

笔记:

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

原答案:

我回答说是的,你绝对可以使用 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)
  };


});

谢谢你——让我省了很多麻烦,而且它非常适合我的情况。我必须用一个对象 ID 来初始化我的控制器,这是正确的。
From docsThe 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.
答案清楚地表明文档建议反对这种方法,但是有人可以指出文档提供官方解决方案的位置吗?
好吧,我认为文档只是在不说明原因的情况下推荐反对这种方法。我认为这是一个绝妙的方法。如果框架作者不希望框架以对他们来说是“错误”的方式使用,那么他们不应该以这种“错误”的方式使用框架......并提供有关“正确的路!
一个警告——如果你试图绑定到一个范围值,或者真的做任何期望值出现在控制器函数中的事情,它已经在 ng-init 发生之前运行了控制器函数——参见 {1 }
M
Michael Tiller

我已经很晚了,我不知道这是否是个好主意,但是您可以在控制器函数中包含可注入的 $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>

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


我将如何使用这种方法传递一个对象? var obj = {a: 1, b: 2};
对于制作混合应用程序的问题,这是一个非常合理的解决方案。
@Neil:您必须对您的 json 对象进行字符串化,然后在控制器内对其进行解析。不是更好的解决方案,但它可能会起作用。迈克尔的解决方案适用于类似字符串的参数......
这实际上比使用 ng-init 更可靠——如果您尝试绑定到范围值,它已经在 ng-init 发生之前运行了控制器函数——请参阅 plnkr.co/edit/donCm6FRBSX9oENXh9WJ?p=preview
我没有用指令等重新发明轮子。如果你有一个控制器,你想在多个视图中使用几个不同的初始化参数,这是最简单和最可靠的解决方案。
J
Jonghee Park

这也有效。

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>

这是一个很好的解决方案,适用于现有的构造函数注入机制。本质上,您是在创建两个简单的服务,分别称为“name”和“id”。在构造过程中,注入器负责按名称匹配它们。请参阅:docs.angularjs.org/guide/providers 的价值配方部分
好的。请注意,这也适用于对象,而不仅仅是像字符串这样的原始类型。
我更喜欢这个解决方案。我能够通过参数传递模块化我的重复代码。干杯!
这看起来是最惯用的方式,但我不确定 Angular 团队是否推荐它
这是否实质上设置了全局键/值对?这些是否可以限制在给定的控制器实例上,以便仅在主控制器下的子控制器上拾取它?如果不是,这似乎与仅在窗口级别设置常规全局 Javascript 变量并没有太大区别。
N
Nucleon

视图不应指定配置

在 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


我已经投了赞成票,但我想对你的贡献表示感谢。这个答案让我走上了一条更好的道路。当人们分享最佳实践而不仅仅是 hacky 片段时,stackoverflow 是最好的。这太棒了:“模板永远不应该指定配置,这是人们想要从模板文件向控制器传递参数时所希望的”。感谢您将激光视觉带入问题的核心。
F
François Romain

就像@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;
    })
}]);

然后,根据 id,可以在视图中访问 elm


S
Serhii Holinei

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

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


作为参考,这很好用,但在当前版本的角度上它是 $attrs (vs attrs)
b
btesser

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

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

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>

D
Dmitri Algazin

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

    }

s
ssaini

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

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


C
Community

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


O
O. Winter

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

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

看看我的笨蛋here

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

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

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

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

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

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


J
Juan

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

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


M
Marcin Wyszynski

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


最佳答案,海事组织。我目前也在某些情况下为那些敢于按F5的用户使用浏览器存储,因此保持这种状态。
M
Moses Machua

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

从路线

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

这会处理诸如 '/mypage/bar' 之类的 URL。如您所见, foo 由 url 参数传递,我们为 $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'

});

J
JohnTD

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

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

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

现在,为了在每个 DirectiveLikeController 中访问创建时的 objParameter(或随时获取最新的 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


S
SunnyShah

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


我不敢苟同,但您可以使用 ng-init 和使用 init 函数来完成此操作。
一个警告——如果你试图绑定到一个范围值,或者真的做任何期望值出现在控制器函数中的事情,它已经在 ng-init 发生之前运行了控制器函数——参见 {1 }
对的,这是可能的。至少,您会使用数据参数或其他任何东西。但这绝对是可能的。
O
Ollie H-M

这是一个解决方案(基于 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 应用程序。任何关于为什么这种方法不好的建议都会受到欢迎 - 感觉太容易明智了!)