Chia sẻ dữ liệu giữa các bộ điều khiển AngularJS


362

Tôi đang cố gắng chia sẻ dữ liệu trên các bộ điều khiển. Ca sử dụng là một hình thức gồm nhiều bước, dữ liệu được nhập vào một đầu vào sau đó được sử dụng ở nhiều vị trí hiển thị bên ngoài bộ điều khiển gốc. Mã dưới đây và trong jsfiddle ở đây .

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>

Mã não

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

Bất kỳ trợ giúp nào cũng được đánh giá rất cao.


Câu trả lời:


481

Một giải pháp đơn giản là yêu cầu nhà máy của bạn trả về một đối tượng và để bộ điều khiển của bạn hoạt động với tham chiếu đến cùng một đối tượng:

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>

Bản trình diễn: http://jsfiddle.net/HEdJF/

Khi các ứng dụng trở nên lớn hơn, phức tạp hơn và khó kiểm tra hơn, bạn có thể không muốn phơi bày toàn bộ đối tượng từ nhà máy theo cách này, nhưng thay vào đó, chỉ cung cấp quyền truy cập hạn chế thông qua getters và setters:

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

    var data = {
        FirstName: ''
    };

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

Với phương pháp này, tùy thuộc vào các bộ điều khiển tiêu thụ để cập nhật nhà máy với các giá trị mới và để theo dõi các thay đổi để có được chúng:

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>

Bản trình diễn: http://jsfiddle.net/27mk1n1o/


2
Giải pháp đầu tiên hoạt động tốt nhất với tôi - có dịch vụ duy trì đối tượng và các bộ điều khiển chỉ xử lý với tham chiếu. Rất nhiều cảm ơn.
johnkeese

5
phương thức $ scope. $ hoạt động tuyệt vời để thực hiện cuộc gọi phần còn lại từ một phạm vi và áp dụng kết quả cho một phạm vi khác
aclave1

Thay vì trả về một đối tượng như return { FirstName: '' };hoặc trả về một đối tượng có chứa các phương thức tương tác với một đối tượng, sẽ không thể trả về một thể hiện của một lớp ( function) để khi bạn sử dụng đối tượng của mình, nó có một kiểu cụ thể và nó không chỉ một đối tượng{}" ?
RPDeshaies 19/03/2015

8
Chỉ cần ngẩng cao lên, chức năng người nghe không cần newValue !== oldValuekiểm tra vì Angular không thực thi chức năng nếu oldValue và newValue giống nhau. Ngoại lệ duy nhất này là nếu bạn quan tâm đến giá trị ban đầu. Xem: docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch
Gautham C.

@tasseKATT, Nó hoạt động tốt, nếu tôi không làm mới trang. Bạn có thể gợi ý cho tôi bất kỳ giải pháp nào nếu tôi làm mới trang ???
MaNn

71

Tôi không thích sử dụng $watchcho việc này. Thay vì chỉ định toàn bộ dịch vụ cho phạm vi của bộ điều khiển, bạn chỉ có thể gán dữ liệu.

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>

Ngoài ra, bạn có thể cập nhật dữ liệu dịch vụ bằng phương pháp trực tiếp.

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

Bạn không sử dụng theo dõi rõ ràng () nhưng bên trong AngularJS sẽ cập nhật chế độ xem với các giá trị mới trong các biến $ scope. Về mặt hiệu suất nó có giống nhau hay không?
Mart Toduez

4
Tôi không chắc chắn về hiệu suất của một trong hai phương pháp. Theo những gì tôi hiểu thì Angular đã chạy một vòng lặp tiêu hóa trên phạm vi, vì vậy việc thiết lập các biểu thức đồng hồ bổ sung có thể sẽ tăng thêm công việc cho Angular. Bạn sẽ phải chạy một bài kiểm tra hiệu suất để thực sự tìm ra. Tôi chỉ thích truyền dữ liệu và chia sẻ thông qua câu trả lời ở trên hơn là cập nhật qua biểu thức $ watch.
bennick

2
Đây không phải là một thay thế cho $ watch. Đó là một cách khác nhau để chia sẻ dữ liệu giữa hai đối tượng Angular, trong trường hợp này là bộ điều khiển, mà không phải sử dụng $ watch.
bennick

1
IMO giải pháp này là giải pháp tốt nhất vì nó dựa trên tinh thần của cách tiếp cận khai báo của Angular, trái ngược với việc thêm các câu lệnh $ watch, cảm thấy giống jQuery hơn một chút.
JamesG

8
Tại sao đây không phải là câu trả lời được bình chọn nhiều nhất mà tôi tự hỏi. Sau khi tất cả đồng hồ $ làm cho mã phức tạp hơn
Shyamal Parikh

11

Có nhiều cách bạn có thể chia sẻ dữ liệu giữa các bộ điều khiển

  1. sử dụng dịch vụ
  2. sử dụng các dịch vụ $ state.go
  3. sử dụng nhà nước
  4. sử dụng rootscope

Giải thích từng phương pháp:

  1. Tôi sẽ không giải thích vì nó đã được giải thích bởi ai đó

  2. sử dụng $state.go

      $state.go('book.name', {Name: 'XYZ'}); 
    
      // then get parameter out of URL
      $state.params.Name;
  3. $stateparamhoạt động theo cách tương tự $state.go, bạn chuyển nó dưới dạng đối tượng từ bộ điều khiển người gửi và thu thập trong bộ điều khiển máy thu bằng cách sử dụng stateparam

  4. sử dụng $rootscope

    (a) gửi dữ liệu từ con đến bộ điều khiển chính

      $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) gửi dữ liệu từ cha mẹ đến bộ điều khiển con

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

6

Tôi đã tạo một nhà máy kiểm soát phạm vi được chia sẻ giữa mẫu của đường dẫn tuyến đường, do đó bạn có thể duy trì dữ liệu được chia sẻ ngay khi người dùng đang điều hướng trong cùng một đường dẫn chính của tuyến.

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

Vì vậy, nếu người dùng đi đến một tuyến đường khác, ví dụ '/ Hỗ trợ', dữ liệu được chia sẻ cho đường dẫn '/ Khách hàng' sẽ tự động bị hủy. Nhưng, nếu thay vì điều này, người dùng sẽ đi đến các đường dẫn 'con', như '/ Khách hàng / 1' hoặc '/ Khách hàng / danh sách' thì phạm vi sẽ không bị phá hủy.

Bạn có thể xem một mẫu ở đây: http://plnkr.co/edit/OL8of9


Sử dụng $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams){ ... })nếu bạn có ui.router
Nuno Silva

6

Có nhiều cách để chia sẻ dữ liệu giữa các bộ điều khiển

  • Dịch vụ góc
  • $ phát sóng, phương thức $ phát ra
  • Giao tiếp điều khiển từ cha mẹ đến con
  • $ rootscope

Như chúng ta biết $rootscopekhông phải là cách tốt nhất để truyền dữ liệu hoặc liên lạc vì đó là phạm vi toàn cầu có sẵn cho toàn bộ ứng dụng

Để chia sẻ dữ liệu giữa các bộ điều khiển Angular Js Các dịch vụ góc là các thực tiễn tốt nhất, vd. .factory, .service
Để tham khảo

Trong trường hợp truyền dữ liệu từ công ty mẹ để điều khiển con bạn có thể dữ liệu mẹ truy cập trực tiếp trong điều khiển con qua $scope
Nếu bạn đang sử dụng ui-routersau đó bạn có thể sử dụng $stateParmasđể vượt qua các tham số url như id, name, key, vv

$broadcastcũng là cách tốt để chuyển dữ liệu giữa các bộ điều khiển từ cha mẹ sang con cái và $emitchuyển dữ liệu từ con sang bộ điều khiển cha

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>

Mã não

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

Tham khảo liên kết đã cho để biết thêm về$broadcast


4

Giải pháp đơn giản nhất:

Tôi đã sử dụng dịch vụ AngularJS .

Bước 1: Tôi đã tạo một dịch vụ AngularJS có tên SharedDataService.

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

    };
    return Person;
});

Bước2: Tạo hai bộ điều khiển và sử dụng dịch vụ đã tạo ở trên.

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

Bước 3: Đơn giản chỉ cần sử dụng các bộ điều khiển được tạo trong khung nhìn.

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

Để xem giải pháp làm việc cho vấn đề này, vui lòng nhấn vào liên kết dưới đây

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

tập tin .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>

1
đây là ví dụ rất rõ ràng cho dân gian mới sử dụng angularJs. Làm thế nào bạn sẽ chia sẻ / truyền dữ liệu từ mối quan hệ cha mẹ / con cái? FirstCtrllà cha mẹ và nhận được một lời hứa mà SecondCtrl(con) cũng sẽ cần? Tôi không muốn thực hiện hai cuộc gọi api. Cảm ơn.
Chris22

1

Có một cách khác mà không cần sử dụng $ watch, sử dụng 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;
});

1

Có nhiều cách để làm điều này.

  1. Sự kiện - đã được giải thích tốt.

  2. bộ định tuyến ui - giải thích ở trên.

  3. Dịch vụ - với phương thức cập nhật được hiển thị ở trên
  4. BAD - Theo dõi các thay đổi.
  5. Một cách tiếp cận khác của cha mẹ thay vì phát rabrodcast -

*

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

0

Không chắc chắn nơi tôi đã chọn mẫu này nhưng để chia sẻ dữ liệu qua các bộ điều khiển và giảm $ rootScope và $ scope, điều này hoạt động rất tốt. Nó gợi nhớ đến một bản sao dữ liệu nơi bạn có nhà xuất bản và người đăng ký. Hy vọng nó giúp.

Dịch vụ:

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

Bộ điều khiển nhận dữ liệu mới, đó là Nhà xuất bản sẽ làm một cái gì đó như thế này ..

var someData = yourDataService.getSomeData();

sharedDataEventHub.changeData(someData);

Bộ điều khiển cũng đang sử dụng dữ liệu mới này, được gọi là Người đăng ký sẽ làm một cái gì đó như thế này ...

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

Điều này sẽ làm việc cho bất kỳ kịch bản. Vì vậy, khi bộ điều khiển chính được khởi tạo và nó nhận được dữ liệu, nó sẽ gọi phương thức changeData, sau đó sẽ phát ra cho tất cả những người đăng ký dữ liệu đó. Điều này làm giảm sự ghép nối các bộ điều khiển của chúng tôi với nhau.


sử dụng một "mô hình" chia sẻ trạng thái giữa các bộ điều khiển có vẻ như tùy chọn được sử dụng nhiều nhất. Thật không may, điều này không bao gồm kịch bản mà bạn sẽ làm mới bộ điều khiển con. Làm thế nào để bạn 'tải lại' dữ liệu từ máy chủ và tạo lại mô hình? Và làm thế nào bạn sẽ đối phó với vô hiệu hóa bộ đệm?
Andrei

Tôi nghĩ rằng ý tưởng của bộ điều khiển cha mẹ và con cái hơi nguy hiểm và gây ra quá nhiều sự phụ thuộc lẫn nhau. Tuy nhiên, khi được sử dụng kết hợp với Bộ định tuyến UI, bạn có thể dễ dàng làm mới dữ liệu trên "con" của mình, đặc biệt nếu dữ liệu đó được đưa vào thông qua cấu hình trạng thái. Vô hiệu bộ nhớ cache xảy ra trên nhà xuất bản, theo đó họ sẽ chịu trách nhiệm truy xuất dữ liệu và gọisharedDataEventHub.changeData(newData)
ewahner

Tôi nghĩ rằng có một sự hiểu lầm. Bằng cách làm mới, tôi có nghĩa là người dùng thực sự nhấn phím F5 trên bàn phím và khiến trình duyệt phải đăng lại. Nếu bạn đang ở chế độ xem con / chi tiết khi điều này xảy ra, sẽ không có ai gọi "var someData = yourDataService.getSomeData ()". Câu hỏi là, làm thế nào bạn sẽ đối phó với kịch bản này? Ai nên cập nhật mô hình?
Andrei

Tùy thuộc vào cách bạn kích hoạt bộ điều khiển của mình, điều này có thể rất đơn giản. Nếu bạn có một phương thức kích hoạt thì bộ điều khiển chịu trách nhiệm tải dữ liệu ban đầu, sẽ tải nó và sau đó sử dụng sharedDataEventHub.changeData(newData)bất kỳ con hoặc bộ điều khiển nào phụ thuộc vào dữ liệu đó sẽ có ở đâu đó trong thân bộ điều khiển của chúng như sau: sharedDataEventHub.onChangeData($scope, function(obj) { vm.myObject = obj.data; });Nếu bạn đã sử dụng UI -Router này có thể được đưa vào từ cấu hình trạng thái.
ewahner

Dưới đây là một ví dụ minh họa cho vấn đề mà tôi đang cố gắng mô tả: plnkr.co/edit/OcI30YX5Jx4oHyxHEkPx?p=preview . Nếu bạn bật ra plunk, điều hướng đến trang chỉnh sửa và làm mới trình duyệt, nó sẽ bị sập. Ai nên tạo lại mô hình trong trường hợp này?
Andrei

-3

Chỉ cần làm đơn giản (đã thử nghiệm với 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>


5
Tôi nghĩ rằng điều này đã được bỏ phiếu vì nó không phải là mô-đun. dummylà toàn cầu cho cả hai bộ điều khiển thay vì chia sẻ theo cách mô đun hơn thông qua kế thừa với $ scope hoặc bộ điều khiển hoặc sử dụng dịch vụ.
Chad Johnson
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.