简体   繁体   English

在 AngularJS 控制器之间共享数据

[英]Share data between AngularJS controllers

I'm trying to share data across controllers.我正在尝试跨控制器共享数据。 Use-case is a multi-step form, data entered in one input is later used in multiple display locations outside the original controller.用例是一种多步骤形式,在一个输入中输入的数据稍后用于原始控制器之外的多个显示位置。 Code below and in jsfiddle here .下面的代码和这里的 jsfiddle代码。

HTML HTML

<div ng-controller="FirstCtrl">
    <input type="text" ng-model="FirstName"><!-- Input entered here -->
    <br>Input is : <strong>{{FirstName}}</strong><!-- Successfully updates here -->
</div>

<hr>

<div ng-controller="SecondCtrl">
    Input should also be here: {{FirstName}}<!-- How do I automatically updated it here? -->
</div>

JS JS

// declare the app with no dependencies
var myApp = angular.module('myApp', []);

// make a factory to share data between controllers
myApp.factory('Data', function(){
    // I know this doesn't work, but what will?
    var FirstName = '';
    return FirstName;
});

// Step 1 Controller
myApp.controller('FirstCtrl', function( $scope, Data ){

});

// Step 2 Controller
myApp.controller('SecondCtrl', function( $scope, Data ){
    $scope.FirstName = Data.FirstName;
});

Any help is greatly appreciated.任何帮助是极大的赞赏。

A simple solution is to have your factory return an object and let your controllers work with a reference to the same object:一个简单的解决方案是让您的工厂返回一个对象,并让您的控制器使用对同一对象的引用:

JS: JS:

// declare the app with no dependencies
var myApp = angular.module('myApp', []);

// Create the factory that share the Fact
myApp.factory('Fact', function(){
  return { Field: '' };
});

// Two controllers sharing an object that has a string in it
myApp.controller('FirstCtrl', function( $scope, Fact ){
  $scope.Alpha = Fact;
});

myApp.controller('SecondCtrl', function( $scope, Fact ){
  $scope.Beta = Fact;
});

HTML: HTML:

<div ng-controller="FirstCtrl">
    <input type="text" ng-model="Alpha.Field">
    First {{Alpha.Field}}
</div>

<div ng-controller="SecondCtrl">
<input type="text" ng-model="Beta.Field">
    Second {{Beta.Field}}
</div>

Demo: http://jsfiddle.net/HEdJF/演示: http : //jsfiddle.net/HEdJF/

When applications get larger, more complex and harder to test you might not want to expose the entire object from the factory this way, but instead give limited access for example via getters and setters:当应用程序变得更大、更复杂且更难测试时,您可能不希望以这种方式从工厂公开整个对象,而是通过 getter 和 setter 等方式提供有限的访问权限:

myApp.factory('Data', function () {

    var data = {
        FirstName: ''
    };

    return {
        getFirstName: function () {
            return data.FirstName;
        },
        setFirstName: function (firstName) {
            data.FirstName = firstName;
        }
    };
});

With this approach it is up to the consuming controllers to update the factory with new values, and to watch for changes to get them:使用这种方法,由消费控制器使用新值更新工厂,并观察更改以获取它们:

myApp.controller('FirstCtrl', function ($scope, Data) {

    $scope.firstName = '';

    $scope.$watch('firstName', function (newValue, oldValue) {
        if (newValue !== oldValue) Data.setFirstName(newValue);
    });
});

myApp.controller('SecondCtrl', function ($scope, Data) {

    $scope.$watch(function () { return Data.getFirstName(); }, function (newValue, oldValue) {
        if (newValue !== oldValue) $scope.firstName = newValue;
    });
});

HTML: HTML:

<div ng-controller="FirstCtrl">
  <input type="text" ng-model="firstName">
  <br>Input is : <strong>{{firstName}}</strong>
</div>
<hr>
<div ng-controller="SecondCtrl">
  Input should also be here: {{firstName}}
</div>

Demo: http://jsfiddle.net/27mk1n1o/演示: http : //jsfiddle.net/27mk1n1o/

I prefer not to use $watch for this.我不想为此使用$watch Instead of assigning the entire service to a controller's scope you can assign just the data.您可以只分配数据,而不是将整个服务分配给控制器的范围。

JS: JS:

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

myApp.factory('MyService', function(){
  return {
    data: {
      firstName: '',
      lastName: ''
    }
    // Other methods or objects can go here
  };
});

myApp.controller('FirstCtrl', function($scope, MyService){
  $scope.data = MyService.data;
});

myApp.controller('SecondCtrl', function($scope, MyService){
   $scope.data = MyService.data;
});

HTML: HTML:

<div ng-controller="FirstCtrl">
  <input type="text" ng-model="data.firstName">
  <br>Input is : <strong>{{data.firstName}}</strong>
</div>
<hr>
<div ng-controller="SecondCtrl">
  Input should also be here: {{data.firstName}}
</div>

Alternatively you can update the service data with a direct method.或者,您可以使用直接方法更新服务数据。

JS: JS:

// A new factory with an update method
myApp.factory('MyService', function(){
  return {
    data: {
      firstName: '',
      lastName: ''
    },
    update: function(first, last) {
      // Improve this method as needed
      this.data.firstName = first;
      this.data.lastName = last;
    }
  };
});

// Your controller can use the service's update method
myApp.controller('SecondCtrl', function($scope, MyService){
   $scope.data = MyService.data;

   $scope.updateData = function(first, last) {
     MyService.update(first, last);
   }
});

There are many ways you can share the data between controllers您可以通过多种方式在控制器之间共享数据

  1. using services使用服务
  2. using $state.go services使用 $state.go 服务
  3. using stateparams使用状态参数
  4. using rootscope使用根作用域

Explanation of each method:每种方法的解释:

  1. I am not going to explain as its already explained by someone我不打算解释,因为它已经有人解释过了

  2. using $state.go使用$state.go

     $state.go('book.name', {Name: 'XYZ'}); // then get parameter out of URL $state.params.Name;
  3. $stateparam works in a similar way to $state.go , you pass it as object from sender controller and collect in receiver controller using stateparam $stateparam工作方式与$state.go类似,您将其作为对象从发送方控制器传递并使用 stateparam 在接收方控制器中收集

  4. using $rootscope使用$rootscope

    (a) sending data from child to parent controller (a) 从子控制器向父控制器发送数据

     $scope.Save(Obj,function(data) { $scope.$emit('savedata',data); //pass the data as the second parameter }); $scope.$on('savedata',function(event,data) { //receive the data as second parameter });

    (b) sending data from parent to child controller (b) 从父控制器向子控制器发送数据

     $scope.SaveDB(Obj,function(data){ $scope.$broadcast('savedata',data); }); $scope.SaveDB(Obj,function(data){`enter code here` $rootScope.$broadcast('saveCallback',data); });

I've created a factory that controls shared scope between route path's pattern, so you can maintain the shared data just when users are navigating in the same route parent path.我创建了一个工厂来控制路由路径模式之间的共享范围,因此您可以在用户在同一路由父路径中导航时维护共享数据。

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

So, if the user goes to another route path, for example '/Support', the shared data for path '/Customer' will be automatically destroyed.因此,如果用户转到另一个路由路径,例如“/Support”,路径“/Customer”的共享数据将自动销毁。 But, if instead of this the user goes to 'child' paths, like '/Customer/1' or '/Customer/list' the the scope won't be destroyed.但是,如果用户转到“子”路径(例如“/Customer/1”或“/Customer/list”)而不是这个,则范围不会被破坏。

You can see an sample here: http://plnkr.co/edit/OL8of9您可以在此处查看示例: http : //plnkr.co/edit/OL8of9

There are multiple ways to share data between controllers有多种方式可以在控制器之间共享数据

  • Angular services角度服务
  • $broadcast, $emit method $broadcast, $emit 方法
  • Parent to child controller communication父子控制器通信
  • $rootscope $rootscope

As we know $rootscope is not preferable way for data transfer or communication because it is a global scope which is available for entire application我们知道$rootscope不是数据传输或通信的首选方式,因为它是一个全局范围,可用于整个应用程序

For data sharing between Angular Js controllers Angular services are best practices eg.对于 Angular Js 控制器之间的数据共享,Angular 服务是最佳实践,例如。 .factory , .service .factory ,. .service
For reference参考

In case of data transfer from parent to child controller you can directly access parent data in child controller through $scope如果数据从父控制器传输到子控制器,您可以通过$scope直接访问子控制器中的父数据
If you are using ui-router then you can use $stateParmas to pass url parameters like id , name , key , etc如果您使用的是ui-router那么您可以使用$stateParmas来传递idnamekey等 url 参数

$broadcast is also good way to transfer data between controllers from parent to child and $emit to transfer data from child to parent controllers $broadcast也是在控制器之间从父控制器传输数据到子控制器和$emit将数据从子控制器传输到父控制器的好方法

HTML HTML

<div ng-controller="FirstCtrl">
   <input type="text" ng-model="FirstName">
   <br>Input is : <strong>{{FirstName}}</strong>
</div>

<hr>

<div ng-controller="SecondCtrl">
   Input should also be here: {{FirstName}}
</div>

JS JS

myApp.controller('FirstCtrl', function( $rootScope, Data ){
    $rootScope.$broadcast('myData', {'FirstName': 'Peter'})
});

myApp.controller('SecondCtrl', function( $rootScope, Data ){
    $rootScope.$on('myData', function(event, data) {
       $scope.FirstName = data;
       console.log(data); // Check in console how data is coming
    });
});

Refer given link to know more about $broadcast请参阅给定链接以了解有关$broadcast更多信息

Simplest Solution:最简单的解决方案:

I have used an AngularJS service .我使用了AngularJS 服务

Step1: I have created an AngularJS service named SharedDataService.第 1我创建了一个名为 SharedDataService 的 AngularJS 服务。

myApp.service('SharedDataService', function () {
     var Person = {
        name: ''

    };
    return Person;
});

Step2: Create two controllers and use the above created service. Step2:创建两个控制器,使用上面创建的服务。

//First Controller
myApp.controller("FirstCtrl", ['$scope', 'SharedDataService',
   function ($scope, SharedDataService) {
   $scope.Person = SharedDataService;
   }]);

//Second Controller
myApp.controller("SecondCtrl", ['$scope', 'SharedDataService',
   function ($scope, SharedDataService) {
   $scope.Person = SharedDataService;
   }]);

Step3: Simply use the created controllers in the view. Step3:只需在视图中使用创建的控制器。

<body ng-app="myApp">

<div ng-controller="FirstCtrl">
<input type="text" ng-model="Person.name">
<br>Input is : <strong>{{Person.name}}</strong>
</div>

<hr>

<div ng-controller="SecondCtrl">
Input should also be here: {{Person.name}}
</div>

</body>

To see working solution to this problem please press the link below要查看此问题的有效解决方案,请按以下链接

https://codepen.io/wins/pen/bmoYLr https://codepen.io/wins/pen/bmoYLr

.html file: .html 文件:

<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>

<body ng-app="myApp">

  <div ng-controller="FirstCtrl">
    <input type="text" ng-model="Person.name">
    <br>Input is : <strong>{{Person.name}}</strong>
   </div>

<hr>

  <div ng-controller="SecondCtrl">
    Input should also be here: {{Person.name}}
  </div>

//Script starts from here

<script>

var myApp = angular.module("myApp",[]);
//create SharedDataService
myApp.service('SharedDataService', function () {
     var Person = {
        name: ''

    };
    return Person;
});

//First Controller
myApp.controller("FirstCtrl", ['$scope', 'SharedDataService',
    function ($scope, SharedDataService) {
    $scope.Person = SharedDataService;
    }]);

//Second Controller
myApp.controller("SecondCtrl", ['$scope', 'SharedDataService',
    function ($scope, SharedDataService) {
    $scope.Person = SharedDataService;
}]);

</script>


</body>
</html>

There is another way without using $watch, using angular.copy:还有另一种不使用 $watch 的方法,使用 angular.copy:

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

myApp.factory('Data', function(){

    var service = {
        FirstName: '',
        setFirstName: function(name) {
            // this is the trick to sync the data
            // so no need for a $watch function
            // call this from anywhere when you need to update FirstName
            angular.copy(name, service.FirstName); 
        }
    };
    return service;
});


// Step 1 Controller
myApp.controller('FirstCtrl', function( $scope, Data ){

});

// Step 2 Controller
myApp.controller('SecondCtrl', function( $scope, Data ){
    $scope.FirstName = Data.FirstName;
});

There are multiple ways to do this.有多种方法可以做到这一点。

  1. Events - already explained well.事件 - 已经解释得很好。

  2. ui router - explained above. ui 路由器 - 上面解释过。

  3. Service - with update method displayed above服务 - 上面显示了更新方法
  4. BAD - Watching for changes.- 观察变化。
  5. Another parent child approach rather than emit and brodcast -另一个父子方法而不是发射广播-

* *

<superhero flight speed strength> Superman is here! </superhero>
<superhero speed> Flash is here! </superhero>

* *

app.directive('superhero', function(){
    return {
        restrict: 'E',
        scope:{}, // IMPORTANT - to make the scope isolated else we will pollute it in case of a multiple components.
        controller: function($scope){
            $scope.abilities = [];
            this.addStrength = function(){
                $scope.abilities.push("strength");
            }
            this.addSpeed = function(){
                $scope.abilities.push("speed");
            }
            this.addFlight = function(){
                $scope.abilities.push("flight");
            }
        },
        link: function(scope, element, attrs){
            element.addClass('button');
            element.on('mouseenter', function(){
               console.log(scope.abilities);
            })
        }
    }
});
app.directive('strength', function(){
    return{
        require:'superhero',
        link: function(scope, element, attrs, superHeroCtrl){
            superHeroCtrl.addStrength();
        }
    }
});
app.directive('speed', function(){
    return{
        require:'superhero',
        link: function(scope, element, attrs, superHeroCtrl){
            superHeroCtrl.addSpeed();
        }
    }
});
app.directive('flight', function(){
    return{
        require:'superhero',
        link: function(scope, element, attrs, superHeroCtrl){
            superHeroCtrl.addFlight();
        }
    }
});

Not sure where I picked up this pattern but for sharing data across controllers and reducing the $rootScope and $scope this works great.不知道我从哪里学到了这种模式,但为了在控制器之间共享数据并减少 $rootScope 和 $scope 这很有效。 It is reminiscent of a data replication where you have publishers and subscribers.它让人想起有发布者和订阅者的数据复制。 Hope it helps.希望能帮助到你。

The Service:服务:

(function(app) {
    "use strict";
    app.factory("sharedDataEventHub", sharedDataEventHub);

    sharedDataEventHub.$inject = ["$rootScope"];

    function sharedDataEventHub($rootScope) {
        var DATA_CHANGE = "DATA_CHANGE_EVENT";
        var service = {
            changeData: changeData,
            onChangeData: onChangeData
        };
        return service;

        function changeData(obj) {
            $rootScope.$broadcast(DATA_CHANGE, obj);
        }

        function onChangeData($scope, handler) {
            $scope.$on(DATA_CHANGE, function(event, obj) {
                handler(obj);
            });
        }
    }
}(app));

The Controller that is getting the new data, which is the Publisher would do something like this..获取新数据的控制器,即发布者会做这样的事情..

var someData = yourDataService.getSomeData();

sharedDataEventHub.changeData(someData);

The Controller that is also using this new data, which is called the Subscriber would do something like this...也使用这个新数据的控制器,称为订阅者会做这样的事情......

sharedDataEventHub.onChangeData($scope, function(data) {
    vm.localData.Property1 = data.Property1;
    vm.localData.Property2 = data.Property2;
});

This will work for any scenario.这适用于任何场景。 So when the primary controller is initialized and it gets data it would call the changeData method which would then broadcast that out to all the subscribers of that data.因此,当主控制器初始化并获取数据时,它会调用 changeData 方法,然后将其广播给该数据的所有订阅者。 This reduces the coupling of our controllers to each other.这减少了我们的控制器之间的耦合。

As pointed out by @MaNn in one of the comments of the accepted answers, the solution wont work if the page is refreshed.正如@MaNn 在已接受答案的评论之一中指出的那样,如果刷新页面,该解决方案将不起作用。

The Solution for this is to use localStorage or sessionStorage for temporary persistence of the data you want to share across controllers.解决方案是使用 localStorage 或 sessionStorage 来临时持久化要跨控制器共享的数据。

  1. Either you make a sessionService whose GET and SET method, encrypts and decrypts the data and reads the data from either localStorage or sessionStorage.要么创建一个 sessionService,其 GET 和 SET 方法,加密和解密数据,并从 localStorage 或 sessionStorage 读取数据。 So now you use this service directly to read and write the data in the storage via any controller or service you want.因此,现在您可以直接使用此服务通过您想要的任何控制器或服务读取和写入存储中的数据。 This is a open approach and easy one这是一种开放的方法并且很容易
  2. Else you make a DataSharing Service and use localStorage inside it - so that if the page is refreshed the service will try and check the storage and reply back via the Getters and Setters you have made public or private in this service file.否则,您创建一个数据共享服务并在其中使用 localStorage - 这样,如果刷新页面,该服务将尝试检查存储并通过您在此服务文件中公开或私有的 Getter 和 Setter 进行回复。

Just do it simple (tested with v1.3.15):做简单的事情(用 v1.3.15 测试):

<article ng-controller="ctrl1 as c1">
    <label>Change name here:</label>
    <input ng-model="c1.sData.name" />
    <h1>Control 1: {{c1.sData.name}}, {{c1.sData.age}}</h1>
</article>
<article ng-controller="ctrl2 as c2">
    <label>Change age here:</label>
    <input ng-model="c2.sData.age" />
    <h1>Control 2: {{c2.sData.name}}, {{c2.sData.age}}</h1>
</article>

<script>
    var app = angular.module("MyApp", []);

    var dummy = {name: "Joe", age: 25};

    app.controller("ctrl1", function () {
        this.sData = dummy;
    });

    app.controller("ctrl2", function () {
        this.sData = dummy;
    });
</script>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM