在Angular JS的控制器之间传递数据?


243

我有一个基本的控制器来显示我的产品,

App.controller('ProductCtrl',function($scope,$productFactory){
     $productFactory.get().success(function(data){
           $scope.products = data;
     });
});

在我看来,我正在列表中显示此产品

<ul>
    <li ng-repeat="product as products">
        {{product.name}}
    </li>
</ul

我要做的是,当有人单击产品名称时,我有另一个名为cart的视图,其中已添加该产品。

 <ul class="cart">
      <li>
          //click one added here
      </li>
      <li>
          //click two added here
      </li>
 </ul>

因此,我的疑问是,如何将这种点击产品从第一台控制器传递到第二台?我认为购物车也应该是控制器。

我使用指令处理click事件。我也觉得我应该使用服务来实现上述功能,只是想不通如何?因为购物车将是预定义的,所以添加的产品数可能是5/10,具体取决于用户所在的页面。所以我想保持这种通用。

更新:

我创建了一个要广播的服务,并在第二个控制器中接收了它。现在查询是如何更新dom?由于我要删除的产品清单是硬编码的。


Answers:


322

根据描述,似乎您应该使用服务。查看http://egghead.io/lessons/angularjs-sharing-data-between-controllersAngularJS服务在控制器之间传递数据,以查看一些示例。

您可以这样定义您的产品服务(作为工厂):

app.factory('productService', function() {
  var productList = [];

  var addProduct = function(newObj) {
      productList.push(newObj);
  };

  var getProducts = function(){
      return productList;
  };

  return {
    addProduct: addProduct,
    getProducts: getProducts
  };

});

依赖关系将服务注入到两个控制器中。

在您的中ProductController,定义一些将所选对象添加到数组的操作:

app.controller('ProductController', function($scope, productService) {
    $scope.callToAddToProductList = function(currObj){
        productService.addProduct(currObj);
    };
});

在您的CartController,从服务中获取产品:

app.controller('CartController', function($scope, productService) {
    $scope.products = productService.getProducts();
});

3
当调用getProducts()时,购物车控制器将更新一次吗?还是每次添加新产品?
kishanio

1
如果希望它自动更新,则可以从Maxim Shoustin的答案中添加广播,并在$ on函数中调用getProducts()以更新CartCtrl的范围。
Chalise

2
@krillgar你是完全正确的,最初被忽略了。我已经编辑了答案以反映可行的解决方案。
Chalise 2014年

1
@DeveshM是的,按照您的建议进行操作。您可能需要考虑扩展这些方法,以便它们不仅将数据保存在内存中而且更具持久性($http例如,通过调用保存到服务器)。
Chalise 2015年

1
例如,如果我有2个productControllers和2个购物车,会发生什么?两者都会添加元素吗?在这种情况下,您该如何解决?
atoth,2013年

67

如何将此点击产品从第一个控制者传递给第二个?

点击时,您可以调用调用广播的方法:

$rootScope.$broadcast('SOME_TAG', 'your value');

第二个控制器将像这样监听这个标签:

$scope.$on('SOME_TAG', function(response) {
      // ....
})

由于我们无法将$ scope注入服务中,因此没有什么比单例$ scope更好。

但是我们可以注入$rootScope。因此,如果您将价值存储到服务中,则可以$rootScope.$broadcast('SOME_TAG', 'your value');在服务主体中运行。(请参阅有关服务的@Charx描述)

app.service('productService',  function($rootScope) {/*....*/}

请查看有关 $ broadcast$ emit的好文章


1
是的,这就像我在使用工厂的魅力一样?最后一件事是,我每次点击产品时都会在新控制器中获取数据。现在如何在DOM中更新它?因为我已经说出5个带有边框的硬编码列表,所以每个产品都需要放入其中
kishanio

1
@KT-您有没有得到答案?这似乎是一个显而易见的问题,但我找不到任何答案。我希望控制器更改一些值。当该应用程序的值更改时,任何其他监听控制器都应根据需要进行更新。找不到。
raddevus

2
@daylight在您的q上的答案。基于您拥有的控制器结构:并行或父级。$rootScope.$broadcast通知具有并行结构(也称为同一级别)的所有控制器。
Maxim Shoustin 2014年

@MS感谢您的答复。有没有办法使用$ watch做到这一点?顺便说一句,我的是并行控制器,我可以使用您的方法来使它工作。对我来说,每当我尝试将$ watch(甚至添加到$ rootScope)时,与第二个控制器中的$ watch相关联的方法都只会在第一次启动(第二个控制器的初始化)。谢谢。
raddevus

1
$watch如果值存在于$rootScope水平,我们可以使用。否则,只有广播可能会通知其他控制器。仅供参考,如果其他程序员在您的代码中看到了broadcast-逻辑清楚的是您尝试执行的操作-“广播事件”。无论如何,从我的经验。它不是好的做法是使用 rootscopewatch。关于“仅第一时间开火”:看一下以下示例:plnkr.co/edit/5zqkj7iYloeHYkzK1Prt?p=preview您具有新旧值。确保每次您获得的新价值不等于旧价值
Maxim Shoustin 2014年

24

不使用$ rootScope创建服务的解决方案:

要在应用程序控制器之间共享属性,可以使用Angular $ rootScope。这是共享数据的另一种选择,使人们知道它。

跨控制器共享某些功能的首选方法是服务,您可以使用$ rootscope来读取或更改全局属性。

var app = angular.module('mymodule',[]);
app.controller('Ctrl1', ['$scope','$rootScope',
  function($scope, $rootScope) {
    $rootScope.showBanner = true;
}]);

app.controller('Ctrl2', ['$scope','$rootScope',
  function($scope, $rootScope) {
    $rootScope.showBanner = false;
}]);

在模板中使用$ rootScope(使用$ root访问属性):

<div ng-controller="Ctrl1">
    <div class="banner" ng-show="$root.showBanner"> </div>
</div>

27
$rootScope应尽可能避免。
Shaz

1
@Shaz您能详细说明为什么吗?
Mirko 2014年

5
@Mirko您应该尽可能避免全局状态,因为任何人都可以更改它-使您的程序状态不可预测。
乌穆特2014年

4
$ rootScope具有全局范围,因此像本地全局变量一样,我们必须谨慎使用它们。如果任何控制器更改其值,它将在全局范围内更改。
桑耶夫2014年

1
服务和工厂都是Singleton,但是您可以选择是否在Controller中注入任何服务。但是所有子范围都源自rootscope并遵循原型链。因此,如果您尝试访问范围内的某个属性并且该属性不存在,那么它将查找链。您可能会不必要地更改rootscope属性。
桑耶夫2015年

16

您可以通过两种方法执行此操作。

  1. 通过使用$rootscope,但是我不建议这样做。该$rootScope是最顶层的范围。一个应用程序只能有一个应用程序的$rootScope所有组件之间共享。因此,它就像一个全局变量。

  2. 使用服务。您可以通过在两个控制器之间共享服务来做到这一点。服务代码可能如下所示:

    app.service('shareDataService', function() {
        var myList = [];
    
        var addList = function(newObj) {
            myList.push(newObj);
        }
    
        var getList = function(){
            return myList;
        }
    
        return {
            addList: addList,
            getList: getList
        };
    });

    你可以在这里看到我的小提琴。


和第三个选项,您可以发送事件,只需添加到列表即可
DanteTheSmith

如果用户刷新页面怎么办?然后getProducts()会给您什么!(您不能告诉所有用户不要刷新,可以吗?)
shreedhar bhat

2
@shreedharbhat问题不是问有关跨页面重新加载的持久数据。
Jack Vial

9

在控制器之间共享数据的一种甚至更简单的方法是使用嵌套数据结构。而不是例如

$scope.customer = {};

我们可以用

$scope.data = { customer: {} };

data属性将从父范围继承,因此我们可以覆盖其字段,从而保留来自其他控制器的访问。


1
控制器将范围属性从父控制器继承到其自己的范围。清洁!简单让它变得美丽
Carlos R Balebona

无法使其工作。在第二个控制器上,我使用$ scope.data,它是未定义的。
Bart Calixto

我在说他在说嵌套控制器。对我也没有多大用处。
诺伯特·诺伯森

8
angular.module('testAppControllers', [])
    .controller('ctrlOne', function ($scope) {
        $scope.$broadcast('test');
    })
    .controller('ctrlTwo', function ($scope) {
        $scope.$on('test', function() {
        });
    });

它使用
虚空

5

我们可以将数据存储在会话中,并且可以在程序外的任何地方使用它。

$window.sessionStorage.setItem("Mydata",data);

其他地方

$scope.data = $window.sessionStorage.getItem("Mydata");

4

我在这里看到了答案,它正在回答控制器之间共享数据的问题,但是如果我希望一个控制器通知另一个控制器有关数据已更改的事实(不使用广播),该怎么办?简单!仅使用著名的访客模式:

myApp.service('myService', function() {

    var visitors = [];

    var registerVisitor = function (visitor) {
        visitors.push(visitor);
    }

    var notifyAll = function() {
        for (var index = 0; index < visitors.length; ++index)
            visitors[index].visit();
    }

    var myData = ["some", "list", "of", "data"];

    var setData = function (newData) {
        myData = newData;
        notifyAll();
    }

    var getData = function () {
        return myData;
    }

    return {
        registerVisitor: registerVisitor,
        setData: setData,
        getData: getData
    };
}

myApp.controller('firstController', ['$scope', 'myService',
    function firstController($scope, myService) {

        var setData = function (data) {
            myService.setData(data);
        }

    }
]);

myApp.controller('secondController', ['$scope', 'myService',
    function secondController($scope, myService) {

        myService.registerVisitor(this);

        this.visit = function () {
            $scope.data = myService.getData();
        }

        $scope.data = myService.getData();
    }
]);

以这种简单的方式,一个控制器可以更新另一控制器某些数据已被更新。


2
观察者模式(en.wikipedia.org/wiki/Observer_pattern)在这里是否更相关?访客模式是另一
Ant Kutschera 2015年

3

我创建了一个工厂来控制路由路径的模式之间的共享范围,因此您可以仅在用户在同一路由父路径中导航时维护共享数据。

.controller('CadastroController', ['$scope', 'RouteSharedScope',
    function($scope, routeSharedScope) {
      var customerScope = routeSharedScope.scopeFor('/Customer');
      //var indexScope = routeSharedScope.scopeFor('/');
    }
 ])

因此,如果用户转到另一个路由路径,例如“ / Support”,则路径“ / Customer”的共享数据将被自动销毁。但是,如果用户改为使用“子”路径(例如“ / Customer / 1”或“ / Customer / list”),则作用域将不会被破坏。

您可以在此处查看示例:http : //plnkr.co/edit/OL8of9


3

1个

using $localStorage

app.controller('ProductController', function($scope, $localStorage) {
    $scope.setSelectedProduct = function(selectedObj){
        $localStorage.selectedObj= selectedObj;
    };
});

app.controller('CartController', function($scope,$localStorage) { 
    $scope.selectedProducts = $localStorage.selectedObj;
    $localStorage.$reset();//to remove
});

2

单击时,您可以调用调用广播的方法:

$rootScope.$broadcast('SOME_TAG', 'your value');

and the second controller will listen on this tag like:
$scope.$on('SOME_TAG', function(response) {
      // ....
})

3

using $rootScope:

4

window.sessionStorage.setItem("Mydata",data);
$scope.data = $window.sessionStorage.getItem("Mydata");

5

一种使用角度服务的方式:

var app = angular.module("home", []);

app.controller('one', function($scope, ser1){
$scope.inputText = ser1;
});

app.controller('two',function($scope, ser1){
$scope.inputTextTwo = ser1;
});

app.factory('ser1', function(){
return {o: ''};
});

2
在您的答案中添加更多解释。
格哈德·巴纳德

2

在模块中建立工厂,并在控制器中添加工厂的引用,并在控制器中使用其变量,然后通过在任意位置添加引用来获取另一个控制器中的数据值


2

我不知道它是否会帮助任何人,但是基于Charx(谢谢!)的答案,我创建了简单的缓存服务。随时使用,混音和共享:

angular.service('cache', function() {
    var _cache, _store, _get, _set, _clear;
    _cache = {};

    _store = function(data) {
        angular.merge(_cache, data);
    };

    _set = function(data) {
        _cache = angular.extend({}, data);
    };

    _get = function(key) {
        if(key == null) {
            return _cache;
        } else {
            return _cache[key];
        }
    };

    _clear = function() {
        _cache = {};
    };

    return {
        get: _get,
        set: _set,
        store: _store,
        clear: _clear
    };
});

2

一种使用角度服务的方式:

var app = angular.module("home", []);

app.controller('one', function($scope, ser1){
$scope.inputText = ser1;
});


app.controller('two',function($scope, ser1){
$scope.inputTextTwo = ser1;
});

app.factory('ser1', function(){
return {o: ''};
});



<div ng-app='home'>

<div ng-controller='one'>
  Type in text: 
  <input type='text' ng-model="inputText.o"/>
</div>
<br />

<div ng-controller='two'>
  Type in text:
  <input type='text' ng-model="inputTextTwo.o"/>
</div>

</div>

https://jsfiddle.net/1w64222q/



1

为了改善@Maxim使用$ broadcast提出的解决方案,发送数据不要更改

$rootScope.$broadcast('SOME_TAG', 'my variable');

但是要听数据

$scope.$on('SOME_TAG', function(event, args) {
    console.log("My variable is", args);// args is value of your variable
})


0
var custApp = angular.module("custApp", [])
.controller('FirstController', FirstController)
.controller('SecondController',SecondController)
.service('sharedData', SharedData);

FirstController.$inject = ['sharedData'];
function FirstController(sharedData) {
this.data = sharedData.data;
}

SecondController.$inject['sharedData'];
function SecondController(sharedData) {
this.data = sharedData.data;
}

function SharedData() {
this.data = {
    value: 'default Value'
}
}

第一控制人

<div ng-controller="FirstController as vm">
<input type=text ng-model="vm.data.value" />
</div>

第二控制人

 <div ng-controller="SecondController as vm">
    Second Controller<br>
    {{vm.data.value}}
</div>

-1

我觉得

最好的办法

是使用$ localStorage。(一直有效)

app.controller('ProductController', function($scope, $localStorage) {
    $scope.setSelectedProduct = function(selectedObj){
        $localStorage.selectedObj= selectedObj;
    };
});

您的cardController将是

app.controller('CartController', function($scope,$localStorage) { 
    $scope.selectedProducts = $localStorage.selectedObj;
    $localStorage.$reset();//to remove
});

您也可以添加

if($localStorage.selectedObj){
    $scope.selectedProducts = $localStorage.selectedObj;
}else{
    //redirect to select product using $location.url('/select-product')
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.