Truyền dữ liệu giữa các bộ điều khiển trong Angular JS?


243

Tôi có một bộ điều khiển cơ bản hiển thị các sản phẩm của mình,

App.controller('ProductCtrl',function($scope,$productFactory){
     $productFactory.get().success(function(data){
           $scope.products = data;
     });
});

Theo quan điểm của tôi, tôi đang hiển thị các sản phẩm này trong một danh sách

<ul>
    <li ng-repeat="product as products">
        {{product.name}}
    </li>
</ul

Những gì tôi đang cố gắng làm là khi ai đó nhấp vào tên sản phẩm, tôi có một chế độ xem khác có tên giỏ hàng nơi sản phẩm này được thêm vào.

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

Vì vậy, nghi ngờ của tôi ở đây là, làm thế nào để vượt qua các sản phẩm được nhấp này từ bộ điều khiển đầu tiên đến thứ hai? tôi giả định rằng giỏ hàng cũng nên là một bộ điều khiển.

Tôi xử lý nhấp chuột sự kiện bằng cách sử dụng chỉ thị. Ngoài ra tôi cảm thấy tôi nên sử dụng dịch vụ để đạt được chức năng trên chỉ là không thể làm thế nào? bởi vì giỏ hàng sẽ được xác định trước số lượng sản phẩm được thêm vào có thể là 5/10 tùy thuộc vào người dùng trang nào. Vì vậy, tôi muốn giữ chung chung này.

Cập nhật:

Tôi đã tạo một dịch vụ để phát sóng và trong bộ điều khiển thứ hai tôi nhận được nó. Bây giờ truy vấn là làm thế nào để tôi cập nhật dom? Vì danh sách của tôi để thả sản phẩm là khá cứng.


Câu trả lời:


322

Từ mô tả, có vẻ như bạn nên sử dụng một dịch vụ. Kiểm tra http://egghead.io/lessons/angularjs-shaming-data-b between-controlersAngularJS Dịch vụ truyền dữ liệu giữa các bộ điều khiển để xem một số ví dụ.

Bạn có thể định nghĩa dịch vụ sản phẩm của mình (như một nhà máy) như sau:

app.factory('productService', function() {
  var productList = [];

  var addProduct = function(newObj) {
      productList.push(newObj);
  };

  var getProducts = function(){
      return productList;
  };

  return {
    addProduct: addProduct,
    getProducts: getProducts
  };

});

Phụ thuộc tiêm dịch vụ vào cả hai bộ điều khiển.

Trong của bạn ProductController, xác định một số hành động thêm đối tượng được chọn vào mảng:

app.controller('ProductController', function($scope, productService) {
    $scope.callToAddToProductList = function(currObj){
        productService.addProduct(currObj);
    };
});

Trong của bạn CartController, có được các sản phẩm từ dịch vụ:

app.controller('CartController', function($scope, productService) {
    $scope.products = productService.getProducts();
});

3
Bộ điều khiển giỏ hàng sẽ cập nhật một lần khi get Products () được gọi? Hoặc mỗi khi sản phẩm mới được thêm vào?
kishanio

1
Nếu bạn muốn nó tự động cập nhật, bạn có thể thêm chương trình phát từ câu trả lời của Maxim Shoustin và gọi get Products () trong hàm $ on để cập nhật phạm vi của CartCtrl.
Chalise

2
@krillgar Bạn hoàn toàn chính xác, điều đó đã bị bỏ qua ban đầu. Tôi đã chỉnh sửa câu trả lời để phản ánh một giải pháp hiệu quả.
Chalise

1
@DeveshM Vâng, làm như bạn đề nghị. Bạn có thể muốn xem xét việc mở rộng các phương thức để chúng không chỉ giữ dữ liệu trong bộ nhớ và để bền bỉ hơn ( $httpví dụ: lưu vào máy chủ qua cuộc gọi).
Chalise

1
Điều gì xảy ra nếu tôi có 2 sản phẩm Trình điều khiển và 2 giỏ hàng chẳng hạn? Cả hai sẽ có thêm yếu tố nào? Làm thế nào để bạn giải quyết điều đó trong trường hợp này?
vào

67

Làm thế nào để vượt qua sản phẩm được nhấp này từ bộ điều khiển đầu tiên đến thứ hai?

Khi nhấp vào, bạn có thể gọi phương thức gọi phát sóng :

$rootScope.$broadcast('SOME_TAG', 'your value');

và bộ điều khiển thứ hai sẽ lắng nghe trên thẻ này như:

$scope.$on('SOME_TAG', function(response) {
      // ....
})

Vì chúng ta không thể đưa phạm vi $ vào các dịch vụ, nên không có gì giống như phạm vi $ singleton.

Nhưng chúng ta có thể tiêm $rootScope. Vì vậy, nếu bạn lưu trữ giá trị vào Dịch vụ, bạn có thể chạy $rootScope.$broadcast('SOME_TAG', 'your value');trong phần thân Dịch vụ. (Xem mô tả @Charx về dịch vụ)

app.service('productService',  function($rootScope) {/*....*/}

Vui lòng kiểm tra bài viết hay về $ Broadcast , $ emit


1
Yup này hoạt động như quyến rũ tôi đang sử dụng nhà máy? Chỉ còn một điều cuối cùng là tôi bị kẹt tôi nhận dữ liệu trong bộ điều khiển mới mỗi lần tôi nhấp vào sản phẩm. Bây giờ làm thế nào để tôi cập nhật nó trong DOM? Bởi vì tôi đã cho biết danh sách 5 mã hóa cứng có viền nên mỗi sản phẩm cần phải đi vào bên trong chúng
kishanio

1
@KT - Bạn đã bao giờ nhận được câu trả lời? Có vẻ như một câu hỏi rõ ràng nhưng tôi không thể tìm thấy câu trả lời ở bất cứ đâu. Tôi muốn một bộ điều khiển để thay đổi một số giá trị. Khi giá trị ứng dụng đó thay đổi thì mọi bộ điều khiển nghe khác sẽ tự cập nhật khi cần thiết. Không thể tìm thấy nó.
raddevus

2
@daylight câu trả lời trên q của bạn. được dựa trên cấu trúc bộ điều khiển mà bạn có: song song hoặc cha-con. $rootScope.$broadcastthông báo cho tất cả các bộ điều khiển có cấu trúc song song aka cùng cấp.
Maxim Shoustin

@MS Cảm ơn bạn đã trả lời. Có cách nào để làm điều này bằng cách sử dụng $ watch không? BTW, Mine là bộ điều khiển song song và tôi đã làm cho nó hoạt động bằng phương pháp của bạn. Đối với tôi, bất cứ khi nào tôi cố gắng thêm đồng hồ $ (thậm chí vào $ rootScope), phương thức được liên kết với đồng hồ $ trong bộ điều khiển thứ 2 sẽ chỉ kích hoạt lần đầu tiên (khởi tạo bộ điều khiển thứ 2). Cảm ơn.
raddevus

1
chúng ta có thể sử dụng $watchnếu giá trị tồn tại ở $rootScopemức. Nếu không, chỉ phát sóng có thể thông báo cho các bộ điều khiển khác. FYI, nếu lập trình viên khác nhìn thấy trong mã của bạn broadcast- logic rõ ràng những gì bạn cố gắng thực hiện - "sự kiện phát sóng". Dù sao, từ điểm kinh nghiệm của tôi. nó không phải là thực hành tốt để sử dụng rootscopecho watch. Giới thiệu về "chỉ lần đầu tiên cháy": hãy xem ví dụ này: plnkr.co/edit/5zqkj7iYloeHYkzK1Prt?p=preview bạn có các giá trị cũ và mới. hãy chắc chắn rằng mỗi khi bạn nhận được giá trị mới không bằng giá trị cũ
Maxim Shoustin

24

Giải pháp mà không cần tạo Dịch vụ, sử dụng $ rootScope:

Để chia sẻ các thuộc tính trên các bộ điều khiển ứng dụng, bạn có thể sử dụng Angular $ rootScope. Đây là một tùy chọn khác để chia sẻ dữ liệu, đưa nó để mọi người biết về nó.

Cách ưa thích để chia sẻ một số chức năng trên Bộ điều khiển là Dịch vụ, để đọc hoặc thay đổi thuộc tính toàn cầu mà bạn có thể sử dụng $ 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;
}]);

Sử dụng $ rootScope trong một mẫu (Thuộc tính truy cập với $ root):

<div ng-controller="Ctrl1">
    <div class="banner" ng-show="$root.showBanner"> </div>
</div>

27
$rootScopenên tránh càng nhiều càng tốt.
Shaz

1
@Shaz bạn có thể giải thích tại sao?
Mirko

5
@Mirko Bạn nên cố gắng tránh trạng thái toàn cầu càng nhiều càng tốt bởi vì bất kỳ ai cũng có thể thay đổi nó - làm cho trạng thái chương trình của bạn không thể đoán trước.
Umut Seven

4
$ rootScope có phạm vi toàn cầu, vì vậy, giống như các biến toàn cục gốc, chúng ta phải sử dụng chúng một cách cẩn thận. Nếu bất kỳ bộ điều khiển nào thay đổi giá trị của nó, nó sẽ được thay đổi cho phạm vi toàn cầu.
Sanjeev

1
Cả hai Dịch vụ & Nhà máy đều là Singleton, nhưng bạn có thể chọn tiêm hoặc không sử dụng bất kỳ dịch vụ nào trong Bộ điều khiển của mình. Nhưng tất cả phạm vi con xuất phát từ rootscope và theo chuỗi nguyên mẫu. Vì vậy, nếu bạn cố gắng truy cập một thuộc tính trên phạm vi của bạn và nó không có mặt thì nó sẽ tra cứu chuỗi. Có khả năng bạn không mong muốn có thể thay đổi thuộc tính rootscope.
Sanjeev

16

Bạn có thể làm điều này bằng hai phương pháp.

  1. Bằng cách sử dụng $rootscope, nhưng tôi không giới thiệu điều này. Đây $rootScopelà phạm vi hàng đầu nhất. Một ứng dụng có thể chỉ có một ứng dụng $rootScopesẽ được chia sẻ giữa tất cả các thành phần của ứng dụng. Do đó nó hoạt động như một biến toàn cầu.

  2. Sử dụng dịch vụ. Bạn có thể làm điều này bằng cách chia sẻ một dịch vụ giữa hai bộ điều khiển. Mã cho dịch vụ có thể trông như thế này:

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

    Bạn có thể thấy fiddle của tôi ở đây .


và tùy chọn thứ 3, bạn có thể gửi sự kiện, chỉ cần thêm vào danh sách
DanteTheSmith

CÁI GÌ NẾU NGƯỜI DÙNG LẠI TRANG? THEN get Products () S G CHO BẠN KHÔNG CẦN! (Bạn không thể nói với tất cả người dùng không được làm mới, phải không?)
shreedhar bhat 28/07/17

2
@shreedharbhat Câu hỏi không hỏi về dữ liệu vẫn tồn tại trên các trang tải lại.
Jack Vial

9

Một cách thậm chí đơn giản hơn để chia sẻ dữ liệu giữa các bộ điều khiển là sử dụng các cấu trúc dữ liệu lồng nhau. Thay vì, ví dụ

$scope.customer = {};

chúng ta có thể sử dụng

$scope.data = { customer: {} };

Các databất động sản sẽ được thừa hưởng từ cha mẹ phạm vi vì vậy chúng tôi có thể ghi đè lên các lĩnh vực của mình, hãy truy cập từ bộ điều khiển khác.


1
Bộ điều khiển kế thừa các thuộc tính phạm vi từ bộ điều khiển mẹ đến phạm vi riêng của nó. Dọn dẹp! sự đơn giản làm cho nó trở nên đẹp đẽ
Carlos R Balebona

Không thể làm cho nó hoạt động. trên bộ điều khiển thứ hai tôi sử dụng $ scope.data và nó không được xác định.
Bart Calixto

Anh ấy nói về bộ điều khiển lồng nhau tôi nghĩ. Tôi cũng không sử dụng nhiều.
Norbert Norbertson

8
angular.module('testAppControllers', [])
    .controller('ctrlOne', function ($scope) {
        $scope.$broadcast('test');
    })
    .controller('ctrlTwo', function ($scope) {
        $scope.$on('test', function() {
        });
    });

nó sử dụng
niether

5

chúng ta có thể lưu trữ dữ liệu trong phiên và có thể sử dụng nó ở bất cứ đâu trong chương trình.

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

Nơi khác

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

4

Tôi đã thấy các câu trả lời ở đây và nó đang trả lời câu hỏi chia sẻ dữ liệu giữa các bộ điều khiển, nhưng tôi nên làm gì nếu tôi muốn một bộ điều khiển thông báo cho người khác về thực tế là dữ liệu đã bị thay đổi (không sử dụng phát sóng)? DỄ DÀNG! Chỉ cần sử dụng mẫu khách truy cập nổi tiếng:

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

Theo cách đơn giản này, một bộ điều khiển có thể cập nhật một bộ điều khiển khác mà một số dữ liệu đã được cập nhật.


2
Không phải mẫu quan sát viên ( en.wikipedia.org/wiki/Observer_potype ) sẽ phù hợp hơn ở đây? Mẫu khách truy cập là một cái gì đó khác ... vi.wikipedia.org/wiki/Visitor_potype
Ant Kutschera

3

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


3

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

Khi nhấp vào, bạn có thể gọi phương thức gọi phát sóng:

$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

Một cách sử dụng dịch vụ góc:

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

2
đặt một số giải thích thêm vào câu trả lời của bạn.
Gerhard Barnard

2

Tạo một nhà máy trong mô-đun của bạn và thêm một tham chiếu của nhà máy trong bộ điều khiển và sử dụng các biến của nó trong bộ điều khiển và bây giờ lấy giá trị của dữ liệu trong bộ điều khiển khác bằng cách thêm tham chiếu bất cứ khi nào bạn muốn


2

Tôi không biết liệu nó có giúp được ai không, nhưng dựa trên câu trả lời của Charx (cảm ơn!) Tôi đã tạo ra dịch vụ bộ đệm đơn giản. Hãy sử dụng, phối lại và chia sẻ:

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

2

Một cách sử dụng dịch vụ góc:

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/


1

FYI Đối tượng $ scope có $ emit, $ Broadcast, $ trên AND Đối tượng $ rootScope có $ emit, $ Broadcast, $ on giống hệt nhau

đọc thêm về xuất bản / đăng ký mẫu thiết kế trong góc ở đây


1

Để cải thiện giải pháp được đề xuất bởi @Maxim bằng cách sử dụng $ Broadcast, hãy gửi dữ liệu không thay đổi

$rootScope.$broadcast('SOME_TAG', 'my variable');

nhưng để nghe dữ liệu

$scope.$on('SOME_TAG', function(event, args) {
    console.log("My variable is", args);// args is value of your variable
})

1

Có ba cách để làm điều đó,

a) sử dụng dịch vụ

b) Khai thác tùy thuộc vào mối quan hệ cha / con giữa các phạm vi điều khiển.

c) Từ khóa "As" trong Angular 2.0 sẽ truyền dữ liệu từ bộ điều khiển này sang bộ điều khiển khác.

Để biết thêm thông tin với ví dụ, vui lòng kiểm tra liên kết dưới đây:

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


0
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'
}
}

Bộ điều khiển đầu tiên

<div ng-controller="FirstController as vm">
<input type=text ng-model="vm.data.value" />
</div>

Bộ điều khiển thứ hai

 <div ng-controller="SecondController as vm">
    Second Controller<br>
    {{vm.data.value}}
</div>

-1

Tôi nghĩ rằng

cách tốt nhất

là sử dụng $ localStorage. (Hoạt động mọi lúc)

app.controller('ProductController', function($scope, $localStorage) {
    $scope.setSelectedProduct = function(selectedObj){
        $localStorage.selectedObj= selectedObj;
    };
});

Thẻ kiểm soát của bạn sẽ được

app.controller('CartController', function($scope,$localStorage) { 
    $scope.selectedProducts = $localStorage.selectedObj;
    $localStorage.$reset();//to remove
});

Bạn cũng có thể thêm

if($localStorage.selectedObj){
    $scope.selectedProducts = $localStorage.selectedObj;
}else{
    //redirect to select product using $location.url('/select-product')
}
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.