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

Posted

技术标签:

【中文标题】在Angular JS中的控制器之间传递数据?【英文标题】:Passing data between controllers in Angular JS? 【发布时间】:2013-12-09 11:39:15 【问题描述】:

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

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

我想要做的是当有人点击产品名称时,我有另一个名为购物车的视图,其中添加了该产品。

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

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

我使用指令处理点击事件。另外我觉得我应该使用服务来实现上述功能只是不知道怎么做?因为购物车将被预定义添加的产品数量可能是 5/10,具体取决于用户所在的页面。所以我想保持这个通用性。

更新:

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

【问题讨论】:

Pass variables to AngularJS controller, best practice?的可能重复 【参考方案1】:

从描述来看,您似乎应该使用一项服务。查看http://egghead.io/lessons/angularjs-sharing-data-between-controllers 和AngularJS Service Passing Data Between Controllers 以查看一些示例。

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

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

【讨论】:

调用 getProducts() 时购物车控制器会更新一次吗?还是每次添加新产品? 如果您希望它自动更新,您可以添加来自 Maxim Shoustin 回答的广播并在 $on 函数中调用 getProducts() 以更新 CartCtrl 的范围。 @krillgar 你是完全正确的,最初被忽略了。我已经编辑了答案以反映一个可行的解决方案。 @DeveshM 是的,按照你的建议去做。您可能需要考虑扩展这些方法,以便它们不仅仅是将数据保存在内存中并且更加持久(例如,通过$http 调用保存到服务器)。 如果我有 2 个 productControllers 和 2 个购物车会发生什么?两者都会添加元素?在这种情况下,您如何解决?【参考方案2】:

如何将点击的产品从第一个控制器传递到第二个控制器?

点击时你可以调用调用broadcast的方法:

$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的好文章

【讨论】:

是的,这就像我正在使用工厂的魅力?只有最后一件事我被卡住了,每次点击产品时我都会在新控制器中获取数据。现在我如何在 DOM 中更新它?因为我已经有了 5 个带有边框的硬编码列表,所以每个产品都需要放入其中 @KT - 你得到答案了吗?这似乎是一个显而易见的问题,但我无法在任何地方找到答案。我想要一个控制器来改变一些值。当该应用程序值更改时,任何其他侦听控制器都应根据需要进行自我更新。找不到。 @daylight 回答你的问题。取决于您拥有的控制器结构:并行或子父级。 $rootScope.$broadcast 通知所有具有并行结构的控制器,即同一级别。 @MS 感谢您的回复。有没有办法使用 $watch 做到这一点?顺便说一句,我的是并行控制器,我用你的方法让它工作。对我来说,每当我尝试添加 $watch(甚至添加到 $rootScope)时,与第二个控制器中的 $watch 关联的方法只会在第一次触发(第二个控制器的初始化)。谢谢。 如果值存在于$rootScope 级别,我们可以使用$watch。否则只有广播可能会通知其他控制器。仅供参考,如果其他程序员在您的代码中看到 broadcast - 逻辑很清楚您尝试做什么 - “广播事件”。无论如何,从我的经验。将rootscope 用于watch 并不是一个好习惯。关于“仅第一次开火”:看看这个例子:plnkr.co/edit/5zqkj7iYloeHYkzK1Prt?p=preview 你有旧值和新值。确保每次获得不等于旧值的新值【参考方案3】:

不创建服务的解决方案,使用 $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>

【讨论】:

$rootScope 应尽可能避免。 @Shaz 你能详细说明原因吗? @Mirko 你应该尽量避免全局状态,因为任何人都可以改变它——让你的程序状态变得不可预测。 $rootScope 具有全局作用域,因此像原生全局变量一样,我们必须谨慎使用它们。如果任何控制器更改其值,它将在全局范围内更改。 Services 和 Factories 都是 Singleton ,但是你可以选择在你的 Controller 中注入或不注入任何服务。但是所有子作用域都源自根作用域并遵循原型链。因此,如果您尝试访问范围内的属性并且它不存在,那么它将查找链。您可能会无意中更改 rootscope 属性。【参考方案4】:

您可以通过两种方法做到这一点。

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

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

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

    你可以看到我的小提琴here。

【讨论】:

还有第三个选项,你可以发送事件,只是添加到列表中 如果用户刷新页面怎么办?那么 getProducts() 不会给你任何东西!(你不能告诉所有用户不要刷新,可以吗?) @shreedharbhat 问题不是要在页面重新加载之间保留数据。【参考方案5】:

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

$scope.customer = ;

我们可以使用

$scope.data =  customer:  ;

data 属性将从父作用域继承,因此我们可以覆盖其字段,保持其他控制器的访问权限。

【讨论】:

控制器从父控制器继承作用域属性到它自己的作用域。干净的!简单使它变得美丽 不能让它工作。在第二个控制器上我使用 $scope.data 并且它是未定义的。 我认为他说的是嵌套控制器。对我也没多大用处。【参考方案6】:
angular.module('testAppControllers', [])
    .controller('ctrlOne', function ($scope) 
        $scope.$broadcast('test');
    )
    .controller('ctrlTwo', function ($scope) 
        $scope.$on('test', function() 
        );
    );

【讨论】:

它使用 niether【参考方案7】:

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

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

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

【讨论】:

观察者模式 (en.wikipedia.org/wiki/Observer_pattern) 在这里不是更相关吗?访客模式是另外一回事...en.wikipedia.org/wiki/Visitor_pattern 是的,就是观察者模式。【参考方案8】:

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

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

其他地方

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

【讨论】:

【参考方案9】:

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: '';
);

【讨论】:

在您的答案中添加更多解释。【参考方案10】:

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

.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

【讨论】:

【参考方案11】:

在您的模块中创建工厂并在控制器中添加工厂的引用并在控制器中使用其变量,现在通过在您想要的任何地方添加引用来获取另一个控制器中的数据值

【讨论】:

【参考方案12】:

我不知道它是否会帮助任何人,但基于 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
    ;
);

【讨论】:

【参考方案13】:

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

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/

【讨论】:

【参考方案14】:

仅供参考 $scope 对象有 $emit、$broadcast、$on 和 $rootScope 对象具有相同的 $emit、$broadcast、$on

在 Angular here 中阅读有关发布/订阅设计模式的更多信息

【讨论】:

【参考方案15】:

为了改进@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
)

【讨论】:

【参考方案16】:

有三种方法可以做到,

a) 使用服务

b) 利用控制器范围之间的依赖父/子关系。

c) 在 Angular 2.0 中,“As”关键字将数据从一个控制器传递到另一个控制器。

有关示例的更多信息,请查看以下链接:

http://www.tutorial-points.com/2016/03/angular-js.html

【讨论】:

【参考方案17】:
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>

【讨论】:

【参考方案18】:

我觉得

最好的方法

是使用$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')

【讨论】:

以上是关于在Angular JS中的控制器之间传递数据?的主要内容,如果未能解决你的问题,请参考以下文章

Angular UI在控制器之间传递数据[重复]

如何在页面之间传递Angular中的参数

angular js权威指南笔记三--向指令中传递数据

如何在Angular中的路由组件之间传递数据

Angular 8:通过本地服务在单独模块中的组件之间传递数据

如果组件不是Angular2中的父子组件,如何在组件之间传递数据?