向AngularJS控制器传递变量,最佳实践是什么?

70
我是一名全新的AngularJS使用者,迄今为止,我很喜欢它,特别是模型/视图绑定。我想利用这个功能来构建一个简单的“添加到购物篮”模块。 这是我的控制器内容:
function BasketController($scope) {
    $scope.products = [];

    $scope.AddToBasket = function (Id, name, price, image) {

        ...

    };
}

这是我的HTML代码:

<a ng-click="AddToBasket('237', 'Laptop', '499.95', '237.png')">Add to basket</a>

现在这个方法虽然可行,但我非常怀疑这是在模型中创建新产品对象的正确方法。 然而,这就是我的完全缺乏AngularJS经验的地方。
如果这不是最佳实践,那么该怎么做呢?

2
第二个答案:您可以将参数传递给AngularJS控制器的创建函数中。这是一个示例:app.controller('myController', ['$scope', '$routeParams', function($scope, $routeParams) { $scope.param1 = $routeParams.param1; $scope.param2 = $routeParams.param2; }]);然后在路由配置中定义那些参数:.when('/myRoute/:param1/:param2', { templateUrl: 'views/myView.html', controller: 'myController' })现在,当您访问/myRoute/valueForParam1/valueForParam2时,AngularJS将自动创建myController控制器,并将$routeParams注入其中。因此,$scope.param1$scope.param2将包含相应的值:valueForParam1valueForParam2 - bresleveloper
3个回答

86
你可以在外部包含的 div 上使用 ng-init:

您可以在外层 div 中使用ng-init

<div ng-init="param='value';">
    <div ng-controller="BasketController" >
        <label>param: {{value}}</label>
    </div>
</div>  

参数将会在你的控制器作用域中可用:

function BasketController($scope) {
        console.log($scope.param);
}

我能不用父级 div 来实现这个吗? - Sanghyun Lee
6
可以,如果您喜欢,可以在与 ng-controller 属性相同的元素上使用 ng-init 参数。 - rossipedia
2
我在将ng-init与ng-controller放在同一个元素上遇到了问题。这个有变化还是我做错了什么? - Rene Groeschke
17
这个答案现在已经过时。如 类似的答案 的更新所述,Angular 的 ngInit 文档 现在明确指出 ng-init 唯一被批准使用的方法是为 "别名 ngRepeat 的特殊属性" (例如 fooIndex = $index)。 - Daryn
或者在同一元素上使用它,但是这样做:ng-init="init('value')",然后在控制器中定义该函数:scope.init = function(val) { scope.param=val; } - Jens
2
@Daryn,“Angular方式”是使用静态模板和REST API来处理动态内容。要获取数据,您需要发出HTTP请求并获取它(通过另一个请求)。虽然这可能是最佳实践,在许多情况下页面是动态生成的。此外,如果您仅使用AJAX加载数据,则可能会影响SEO。因此,我真的看不出废弃“ng-init”指令的意义。而且我真的看不到除了一些自定义指令(几乎与“ng-init”相同)之外的其他方法。 - Marius Balčytis

65

你可以创建一个购物篮服务。一般来说,在JS中,你使用对象而不是很多参数。

下面是一个例子:http://jsfiddle.net/2MbZY/

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

app.factory('basket', function() {
    var items = [];
    var myBasketService = {};

    myBasketService.addItem = function(item) {
        items.push(item);
    };
    myBasketService.removeItem = function(item) {
        var index = items.indexOf(item);
        items.splice(index, 1);
    };
    myBasketService.items = function() {
        return items;
    };

    return myBasketService;
});

function MyCtrl($scope, basket) {
    $scope.newItem = {};
    $scope.basket = basket;    
}

7
明白了 - 这也教会了我如何做服务!不错。谢谢你 - 但是,如果没有一系列的HTML输入,我该如何创建并将对象传递给控制器呢? - Greg
bootstrap.css的URL是404。我已经创建了一个更新的jsfiddle - Peter V. Mørch
将每个特定于域的类型都实现为服务,是否应该被视为不良实践?如果您想在没有Angular的环境中使用它们怎么办? - John Freeman
1
@AndyJoslin Angular是如何知道如何将一个basket传递给MyCtrl的? - Antoine
3
这种方法的问题在于,它创建了一个单例实例,并且对整个应用程序都是相同的,所以对于特定页面子控制器,您可能会遇到冲突或已经拥有某些数据的服务。这感觉就像是全局变量来存储东西一样。 - graffic
显示剩余3条评论

2
我对AngularJS不是很了解,但我的解决方案是使用一个简单的JS类(在CoffeeScript意义上)来扩展购物车(cart)的功能。AngularJS的美妙之处在于您可以像下面展示的那样使用ng-click传递您的“model”对象。
我不明白使用工厂模式的优点,因为我认为它比CoffeeScript类更加复杂。
为了可重用的目的,我的解决方案可以转换为服务(Service)。但除此之外,我看不出使用工厂或服务等工具的任何优点。
class Basket extends Array
  constructor: ->

  add: (item) ->
    @push(item)

  remove: (item) ->
    index = @indexOf(item)
    @.splice(index, 1)

  contains: (item) ->
    @indexOf(item) isnt -1

  indexOf: (item) ->
    indexOf = -1
    @.forEach (stored_item, index) ->
      if (item.id is stored_item.id)
        indexOf = index
    return indexOf

然后在您的控制器中初始化此内容,并为该操作创建一个函数:
 $scope.basket = new Basket()
 $scope.addItemToBasket = (item) ->
   $scope.basket.add(item)

最后,您可以将ng-click设置为锚点,在这里将您从数据库检索到的JSON对象传递给函数:

li ng-repeat="item in items"
  a href="#" ng-click="addItemToBasket(item)" 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接