在 AngularJS 控制器之间共享数据

IT技术 javascript angularjs
2020-12-14 16:39:09

我正在尝试跨控制器共享数据。用例是一种多步骤形式,在一个输入中输入的数据稍后用于原始控制器之外的多个显示位置。下面的代码和这里的 jsfiddle代码

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

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

任何帮助是极大的赞赏。

6个回答

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

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/

$scope.$watch 方法非常适用于从一个范围进行休息调用并将结果应用到另一个范围
2021-02-07 16:39:09
与其返回一个像return { FirstName: '' };或返回一个包含与对象交互的方法的对象,不如返回一个 class( function)的实例,这样当你使用你的对象时,它有一个特定的类型,它不仅是一个东西{}” ?
2021-02-08 16:39:09
第一个解决方案对我来说效果最好——让服务维护对象,控制器只处理引用。多谢。
2021-02-11 16:39:09
请注意,侦听器函数不需要newValue !== oldValue检查,因为如果 oldValue 和 newValue 相同,Angular 不会执行该函数。唯一的例外是如果您关心初始值。请参阅:docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch
2021-02-25 16:39:09
@tasseKATT,如果我不刷新页面,效果很好。如果我刷新页面,你能给我建议任何解决方案吗???
2021-03-08 16:39:09

我不想用$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 变量中的新值更新视图。在性能方面它是一样的还是不一样的?
2021-02-08 16:39:09
我不确定这两种方法的性能。据我了解,Angular 已经在作用域上运行了一个摘要循环,因此设置额外的监视表达式可能会为 Angular 增加更多工作。您必须运行性能测试才能真正找出答案。我只是更喜欢通过上面的答案传输和共享数据,而不是通过 $watch 表达式进行更新。
2021-02-23 16:39:09
为什么这不是我想知道的投票最多的答案。毕竟 $watch 使代码更复杂
2021-02-24 16:39:09
这不是 $watch 的替代品。这是在两个 Angular 对象(在本例中为控制器)之间共享数据的不同方式,而无需使用 $watch。
2021-03-02 16:39:09
IMO 这个解决方案是最好的解决方案,因为它更符合 Angular 声明式方法的精神,而不是添加 $watch 语句,后者感觉更像 jQuery。
2021-03-06 16:39:09

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

  1. 使用服务
  2. 使用 $state.go 服务
  3. 使用状态参数
  4. 使用根范围

每种方法的解释:

  1. 我不打算解释,因为它已经有人解释过了

  2. 使用 $state.go

      $state.go('book.name', {Name: 'XYZ'}); 
    
      // then get parameter out of URL
      $state.params.Name;
    
  3. $stateparam以类似于 的方式工作$state.go,您将其作为对象从发送方控制器传递并使用 stateparam 在接收方控制器中收集

  4. 使用 $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);
      });
    

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

.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

使用 $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams){ ... }),如果你有ui.router
2021-02-15 16:39:09

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

  • 角度服务
  • $broadcast, $emit 方法
  • 父子控制器通信
  • $rootscope

正如我们所知,$rootscope这不是数据传输或通信的首选方式,因为它是一个全局范围,可用于整个应用程序

对于 Angular Js 控制器之间的数据共享,Angular 服务是最佳实践,例如。.factory,.service
参考

在从父数据传输到子控制器的情况下,你可以通过孩子控制器直接访问父数据$scope
如果您使用的ui-router话,可以用$stateParmas传递URL参数,如idnamekey,等

$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