我正在尝试在控制器之间共享数据。用例是一个多步骤的表单,在一个输入中输入的数据以后会在原始控制器之外的多个显示位置中使用。

HTML









>非常感谢您的帮助。

评论

如何在Angular中使用$ rootScope来存储变量?

#1 楼

一个简单的解决方案是让您的工厂返回一个对象,并让您的控制器对同一个对象进行引用:

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:

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


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

当应用程序变大时,变得更复杂,更难测试您可能不想以这种方式从工厂公开整个对象,而是例如通过getter和setter提供有限的访问权限:

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

    var data = {
        FirstName: ''
    };

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


使用这种方法由消耗者控制的控制器使用新值更新工厂,并观察更改以获取新值:

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:

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


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

评论


第一个解决方案对我来说最有效-让服务维护对象并且控制器仅处理引用。 Mucho谢谢。

–johnkeese
2014年2月21日在16:29

$ scope。$ watch方法可以很好地工作,可以从一个作用域进行调用,并将结果应用于另一个作用域

–aclave1
2014年8月2日在4:09

而不是返回return {FirstName:''}之类的对象;或返回包含与对象进行交互的方法的对象,是否不应该返回类(函数)的实例,以便在使用对象时它具有特定的类型,而不仅仅是“ Object {}” ?

–RPDeshaies
15年3月19日在11:45

请注意,侦听器函数不需要newValue!== oldValue检查,因为如果oldValue和newValue相同,则Angular不会执行该函数。唯一的例外是您是否关心初始值。请参阅:docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch

– Gautham C.
15年4月23日在18:20



@tasseKATT,如果我不刷新页面,则效果很好。如果我刷新页面,您能建议我任何解决方案吗?

– MaNn
15年7月23日在13:20

#2 楼

我不想为此使用$watch。除了将整个服务分配给控制器的作用域之外,您还可以仅分配数据。

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:

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


或者,您也可以使用直接方法更新服务数据。

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


评论


您没有显式使用watch(),但是在内部AngularJS确实使用$ scope变量中的新值来更新视图。在性能方面是否相同?

–马丁·多明格斯(Mart Dominguez)
15年5月8日在10:28

我不确定这两种方法的性能。据我了解,Angular已经在示波器上运行了一个摘要循环,因此设置其他监视表达式可能会增加Angular的工作量。您必须运行性能测试才能真正找到答案。我只是更喜欢通过以上答案进行数据传输和共享,而不是通过$ watch表达式进行更新。

– Bennick
15年5月8日在18:05

这不是$ watch的替代方案。这是在两个Angular对象(在本例中为控制器)之间共享数据的另一种方式,而不必使用$ watch。

– Bennick
15年7月30日在19:09

IMO这种解决方案是最好的解决方案,因为它更多地是出于Angular声明性方法的精神,而不是添加$ watch语句,这种感觉有点像jQuery。

–JamesG
2015年9月22日,下午1:37

我想知道为什么这不是最投票的答案。毕竟$ watch使代码更复杂

– Shyamal Parikh
15年10月16日在15:05

#3 楼

您可以通过多种方式在控制器之间共享数据


使用服务
使用$ state.go服务
使用stateparams
使用rootscope

每种方法的解释:


我不会解释它,因为有人已经使用$state.go解释了它

/>
  $state.go('book.name', {Name: 'XYZ'}); 

  // then get parameter out of URL
  $state.params.Name;


$stateparam的工作方式与$state.go相似,您将其作为对象从发送方控制器传递,并使用stateparam在接收方控制器中收集

使用$rootscope

(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)从父级向子控制器发送数据

  $scope.SaveDB(Obj,function(data){
      $scope.$broadcast('savedata',data);
  });

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




#4 楼

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

.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

评论


如果您有ui.router,请使用$ rootScope。$ on('$ stateChangeSuccess',function(event,toState,toParams,fromState,fromParams){...})

– Nuno Silva
16年7月9日在21:29



#5 楼

有多种方法可以在控制器之间共享数据


角度服务
$ broadcast,$ emit方法
父级子控制器通讯的父类
$ rootscope
/>
我们知道$rootscope不是数据传输或通信的首选方式,因为它是一个全局范围,可用于整个应用程序

对于Angular Js控制器之间的数据共享Angular服务是最好的做法,例如。 .factory.service
作为参考

从父级到子级控制器的数据传输,您可以通过$scope直接访问子级控制器中的父级数据。
如果使用ui-router,则可以使用$stateParmas传递诸如idnamekey之类的url参数等

$broadcast也是在控制器之间将数据从父级传输到子级以及$emit将数据从子级传输到父级控制器的好方法

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

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


请参阅给出的链接以了解有关$broadcast的更多信息

#6 楼

最简单的解决方案:

我使用了AngularJS服务。

第一步:我创建了一个名为SharedDataService的AngularJS服务。

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

    };
    return Person;
});


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:只需在视图中使用创建的控制器。

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


要查看此问题的有效解决方案,请按下面的链接

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


.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>


评论


对于使用angularJs的新手来说,这是一个非常明显的例子。您将如何共享/传递父子关系中的数据? FirstCtrl是父级,并且得到了SecondCtrl(子级)也需要的承诺?我不想打两个API。谢谢。

– Chris22
19-10-4在16:44

#7 楼

还有一种不使用$ 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;
});


#8 楼

有多种方法可以执行此操作。


事件-已经很好地解释了。服务-上面显示了更新方法,

不良-注意变化。
另一种父子方式而不是发出和广播-

*

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


#9 楼

不知道我在哪里选择了这种模式,但是要在控制器之间共享数据并减少$ rootScope和$ scope效果很好。它让人想起您拥有发布者和订阅者的数据复制。希望它能对您有所帮助。

服务:

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


正在获取新数据的控制器,即发布服务器将执行以下操作..

var someData = yourDataService.getSomeData();

sharedDataEventHub.changeData(someData);


也在使用此新数据的Controller(称为订户)将执行以下操作...

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


这适用于任何情况。因此,当主控制器初始化并获取数据时,它将调用changeData方法,该方法随后会将其广播给该数据的所有订阅者。这样可以减少我们的控制器之间的耦合。

评论


使用在控制器之间共享状态的“模型”似乎是最常用的选项。不幸的是,这并不涵盖您将刷新子控制器的情况。如何从服务器中“提取”数据并重新创建模型?您将如何处理缓存失效?

–安德烈(Andrei)
2015年10月5日在16:36

我认为父级和子级控制器的想法有点危险,并且会导致彼此之间过多的依赖关系。但是,当与UI-Router结合使用时,您可以轻松刷新“子”上的数据,尤其是通过状态配置注入数据时。缓存失效发生在发布者上,由此发布者将负责检索数据并调用sharedDataEventHub.changeData(newData)

– ewahner
2015年10月5日在17:15

我认为有误会。刷新是指用户从字面上按键盘上的F5键,并使浏览器进行回发。如果发生这种情况时您处于子视图/详细信息视图中,则没有人可以调用“ var someData = yourDataService.getSomeData()”。问题是,您将如何处理这种情况?谁应该更新模型?

–安德烈(Andrei)
2015年10月5日17:52



根据您激活控制器的方式,这可能非常简单。如果您有一个激活方法,则负责最初加载数据的控制器将加载它,然后使用sharedDataEventHub.changeData(newData),则依赖于该数据的任何子代或控制器将在其控制器主体中具有某个位置。以下:sharedDataEventHub.onChangeData($ scope,function(obj){vm.myObject = obj.data;});如果您使用了UI-Router,则可以从状态配置中将其注入。

– ewahner
2015年10月5日在19:04

这是一个说明我要描述的问题的示例:plnkr.co/edit/OcI30YX5Jx4oHyxHEkPx?p=preview。如果弹出插件,请导航到编辑页面并刷新浏览器,它将崩溃。在这种情况下,谁应该重新创建模型?

–安德烈(Andrei)
2015年10月6日在8:34

#10 楼

正如@MaNn在接受的答案的注释之一中指出的那样,如果刷新页面,该解决方案将不起作用。
解决方案是使用localStorage或sessionStorage来永久保留要共享的数据跨控制器。

您都可以通过sessionService的GET和SET方法对数据进行加密和解密,并从localStorage或sessionStorage中读取数据。因此,现在您可以直接使用此服务通过所需的任何控制器或服务来读写存储中的数据。这是一种开放的方法,很容易
否则,您将创建一个DataSharing Service并在其中使用localStorage-这样,如果刷新页面,该服务将尝试检查存储并通过您创建的Getter和Setter进行回复此服务文件中的公共或私有。


#11 楼

只需做简单(已在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>




评论


我认为这是不赞成的,因为它不是模块化的。 dummy对两个控制器都是全局的,而不是通过与$ scope或controllerA的继承或使用服务以更模块化的方式共享。

–乍得·约翰逊(Chad Johnson)
15年7月27日在5:40