การส่งผ่านข้อมูลระหว่างคอนโทรลเลอร์ใน Angular JS หรือไม่


243

ฉันมีตัวควบคุมพื้นฐานที่แสดงผลิตภัณฑ์ของฉัน

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

ในมุมมองของฉันฉันกำลังแสดงผลิตภัณฑ์นี้ในรายการ

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

สิ่งที่ฉันพยายามทำคือเมื่อมีคนคลิกที่ชื่อผลิตภัณฑ์ฉันมีมุมมองอื่นที่ชื่อรถเข็นซึ่งมีการเพิ่มผลิตภัณฑ์นี้

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

ดังนั้นข้อสงสัยของฉันที่นี่คือจะส่งผ่านผลิตภัณฑ์ที่ถูกคลิกจากตัวควบคุมแรกไปยังตัวที่สองได้อย่างไร ฉันคิดว่ารถเข็นควรเป็นตัวควบคุมด้วย

ฉันจัดการกับเหตุการณ์คลิกโดยใช้คำสั่ง นอกจากนี้ฉันรู้สึกว่าฉันควรจะใช้บริการเพื่อให้ได้ฟังก์ชั่นดังกล่าวไม่สามารถคิดได้อย่างไร เพราะรถเข็นจะถูกกำหนดไว้ล่วงหน้าจำนวนของผลิตภัณฑ์ที่เพิ่มอาจเป็น 5/10 ขึ้นอยู่กับผู้ใช้หน้า ดังนั้นฉันต้องการที่จะเก็บทั่วไปนี้

ปรับปรุง:

ฉันสร้างบริการเพื่อออกอากาศและในตัวควบคุมที่สองฉันได้รับมัน ตอนนี้เคียวรีคือฉันจะอัพเดต dom ได้อย่างไร? เนื่องจากรายการของฉันที่จะวางผลิตภัณฑ์ค่อนข้าง hardcoded


คำตอบ:


322

ดูเหมือนว่าคุณควรจะใช้บริการ ตรวจสอบhttp://egghead.io/lessons/angularjs-sharing-data-between-controllersและบริการ AngularJS ผ่านข้อมูลระหว่างตัวควบคุมเพื่อดูตัวอย่าง

คุณสามารถกำหนดบริการผลิตภัณฑ์ของคุณ (เป็นโรงงาน) เช่น:

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

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

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

  return {
    addProduct: addProduct,
    getProducts: getProducts
  };

});

การพึ่งพาการฉีดบริการลงในตัวควบคุมทั้งสอง

ในของคุณProductControllerกำหนดการกระทำบางอย่างที่เพิ่มวัตถุที่เลือกไปยังอาร์เรย์:

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

ในของคุณCartControllerรับผลิตภัณฑ์จากบริการ:

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

3
ตัวควบคุมรถเข็นจะอัปเดตหนึ่งครั้งเมื่อมีการเรียกใช้ getProducts () หรือทุกครั้งที่มีการเพิ่มผลิตภัณฑ์ใหม่
kishanio

1
หากคุณต้องการให้อัปเดตโดยอัตโนมัติคุณสามารถเพิ่มการออกอากาศจากคำตอบของ Maxim Shoustin และเรียกใช้ getProducts () ภายในฟังก์ชัน $ on เพื่ออัปเดตขอบเขตของ CartCtrl
Chalise

2
@ krillgar คุณถูกต้องสมบูรณ์ซึ่งถูกมองข้ามในตอนแรก ฉันได้แก้ไขคำตอบเพื่อสะท้อนถึงวิธีการทำงาน
Chalise

1
@DeveshM ใช่ทำตามที่คุณแนะนำ คุณอาจต้องการพิจารณาการขยายวิธีการเพื่อไม่เพียงแค่เก็บข้อมูลไว้ในหน่วยความจำและยังคงอยู่นานขึ้น (เช่นบันทึกไปยังเซิร์ฟเวอร์ผ่านการ$httpโทร)
Chalise

1
จะเกิดอะไรขึ้นถ้าฉันมี 2 productControllers และ 2 carts เป็นตัวอย่าง? ทั้งสองจะมีองค์ประกอบเพิ่มหรือไม่ คุณจะแก้ไขในกรณีนี้ได้อย่างไร
atoth

67

จะส่งผ่านผลิตภัณฑ์ที่ถูกคลิกนี้จากตัวควบคุมแรกไปที่สองได้อย่างไร

เมื่อคลิกคุณสามารถเรียกวิธีการที่เรียกใช้การออกอากาศได้ :

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

และตัวควบคุมที่สองจะฟังในแท็กนี้เช่น:

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

เนื่องจากเราไม่สามารถฉีด $ scope ในบริการจึงไม่มีอะไรเหมือนขอบเขต $ singleton

แต่เราสามารถฉีด$rootScopeได้ ดังนั้นหากคุณเก็บค่าไว้ในบริการคุณสามารถเรียกใช้$rootScope.$broadcast('SOME_TAG', 'your value');ในส่วนบริการได้ (ดูคำอธิบาย @Charx เกี่ยวกับบริการ)

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

โปรดตรวจสอบบทความที่ดีเกี่ยวกับการ ออกอากาศ$ , $ ปล่อย


1
มันใช้งานได้เหมือนมีเสน่ห์ไหมฉันกำลังใช้โรงงานอยู่ใช่ไหม มีเพียงสิ่งสุดท้ายที่ฉันติดอยู่ฉันจะได้รับข้อมูลในตัวควบคุมใหม่ทุกครั้งที่ฉันคลิกผลิตภัณฑ์ ตอนนี้ฉันจะอัปเดตใน DOM ได้อย่างไร เพราะผมมีอยู่แล้วช่วยให้รายการพูด 5 hardcoded ที่มีเส้นขอบเพื่อให้ผลิตภัณฑ์ของแต่ละต้องไปภายในพวกเขา
kishanio

1
@KT - คุณเคยได้รับคำตอบใช่ไหม ดูเหมือนว่าเป็นคำถามที่ชัดเจน แต่ฉันไม่สามารถหาคำตอบได้ทุกที่ ฉันต้องการให้คอนโทรลเลอร์เปลี่ยนค่าบางอย่าง เมื่อค่าแอพนั้นเปลี่ยนไปคอนโทรลเลอร์การฟังอื่น ๆ ควรอัพเดตตัวเองตามความจำเป็น หาไม่เจอ
raddevus

2
@ daylight คำตอบสำหรับคำถามของคุณ ขึ้นอยู่กับโครงสร้างคอนโทรลเลอร์ที่คุณมี: ขนานหรือ child-parent $rootScope.$broadcastแจ้งคอนโทรลเลอร์ทั้งหมดที่มีโครงสร้างแบบขนานหรือระดับเดียวกัน
Maxim Shoustin

@MS ขอบคุณสำหรับการตอบกลับ มีวิธีทำเช่นนี้โดยใช้ $ watch หรือไม่? BTW, Mine เป็นตัวควบคุมแบบขนานและฉันได้มันไปใช้งานโดยใช้วิธีของคุณ สำหรับฉันทุกครั้งที่ฉันพยายามเพิ่ม $ watch (ถึง $ rootScope) วิธีการที่เกี่ยวข้องกับ $ watch ในคอนโทรลเลอร์อันดับ 2 จะเริ่มต้นในครั้งแรกเท่านั้น (การเริ่มต้นของคอนโทรลเลอร์อันดับ 2) ขอบคุณ
raddevus

1
เราสามารถใช้$watchถ้าค่าอยู่ใน$rootScopeระดับ มิฉะนั้นการออกอากาศเท่านั้นที่สามารถแจ้งเตือนคอนโทรลเลอร์อื่น ๆ ได้ FYI ถ้าโปรแกรมเมอร์คนอื่นเห็นในโค้ดของคุณbroadcast- ตรรกะนั้นชัดเจนว่าคุณพยายามทำอะไร - "ออกอากาศเหตุการณ์" อย่างไรก็ตามจากประสบการณ์ของฉัน การปฏิบัติที่ไม่ดีที่จะใช้ สำหรับrootscope watchเกี่ยวกับ "fire 1 time only": ดูตัวอย่างนี้: plnkr.co/edit/5zqkj7iYloeHYkzK1Prt?p=previewคุณมีค่าเก่าและใหม่ ตรวจสอบให้แน่ใจว่าทุกครั้งที่คุณได้รับค่าใหม่ที่ไม่เท่ากับเก่า
Maxim Shoustin

24

โซลูชันโดยไม่สร้างบริการโดยใช้ $ rootScope:

ในการแชร์คุณสมบัติต่าง ๆ ผ่านแอพคอนโทรลเลอร์คุณสามารถใช้ Angular $ rootScope นี่เป็นอีกหนึ่งตัวเลือกในการแชร์ข้อมูลวางไว้เพื่อให้ผู้คนรู้ได้

วิธีที่ต้องการในการแบ่งปันฟังก์ชั่นบางอย่างในส่วนควบคุมคือบริการเพื่ออ่านหรือเปลี่ยนแปลงคุณสมบัติทั่วโลกที่คุณสามารถใช้ $ rootcope

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

การใช้ $ rootScope ในเทมเพลต (คุณสมบัติการเข้าถึงด้วย $ root):

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

27
$rootScopeควรหลีกเลี่ยงให้มากที่สุด
Shaz

1
@Shaz คุณช่วยอธิบายได้ไหมทำไม?
Mirko

5
@Mirko คุณควรพยายามหลีกเลี่ยงสถานะทั่วโลกให้มากที่สุดเพราะใคร ๆ ก็สามารถเปลี่ยนได้ - ทำให้สถานะโปรแกรมของคุณไม่แน่นอน
Umut Seven

4
$ rootScope มีขอบเขตทั่วโลกเช่นเดียวกับตัวแปรทั่วโลกที่เราต้องใช้อย่างระมัดระวัง หากตัวควบคุมใด ๆ เปลี่ยนค่าของมันจะได้รับการเปลี่ยนแปลงสำหรับขอบเขตทั่วโลก
Sanjeev

1
บริการและโรงงานทั้งคู่เป็นแบบซิงเกิลตัน แต่คุณสามารถเลือกที่จะฉีดหรือไม่ใช้บริการใด ๆ ในคอนโทรลเลอร์ของคุณ แต่ขอบเขตของเด็กทั้งหมดนั้นมาจากสโคปและติดตามลูกโซ่ต้นแบบ ดังนั้นถ้าคุณพยายามเข้าถึงคุณสมบัติในขอบเขตของคุณและมันไม่ได้อยู่ในนั้นมันจะค้นหาเชน มีความเป็นไปได้ที่คุณอาจเปลี่ยนคุณสมบัติของ Rootcope
Sanjeev

16

คุณสามารถทำได้สองวิธี

  1. โดยใช้$rootscopeแต่ฉันไม่แนะนำอีกเลย $rootScopeเป็นบนสุดขอบเขต แอปสามารถมีได้เพียงแอปเดียว$rootScopeที่จะแชร์ระหว่างส่วนประกอบทั้งหมดของแอพ ดังนั้นมันจึงทำหน้าที่เหมือนตัวแปรทั่วโลก

  2. การใช้บริการ คุณสามารถทำได้โดยแบ่งปันบริการระหว่างตัวควบคุมสองตัว รหัสสำหรับการบริการอาจมีลักษณะเช่นนี้:

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

    ท่านสามารถเข้าดูไวโอลินของฉันที่นี่


และตัวเลือกที่ 3 คุณสามารถส่งกิจกรรมเพียงเพื่อเพิ่มในรายการ
DanteTheSmith

เกิดอะไรขึ้นถ้าผู้ใช้อ้างอิงถึงหน้า? getProducts จากนั้นจะทำให้คุณไม่มีอะไร (U ไม่สามารถบอกผู้ใช้ทั้งหมดไม่ให้รีเฟรชคุณสามารถได้ไหม)
shreedhar bhat

2
@shreedharbhat คำถามนี้ไม่ได้ถามเกี่ยวกับข้อมูลที่มีอยู่ในการโหลดหน้าซ้ำ
Jack Vial

9

วิธีที่ง่ายยิ่งขึ้นในการแบ่งปันข้อมูลระหว่างตัวควบคุมคือการใช้โครงสร้างข้อมูลแบบซ้อน ตัวอย่างเช่นแทน

$scope.customer = {};

เราสามารถใช้

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

dataคุณสมบัติจะได้รับการสืบทอดมาจากขอบเขตของผู้ปกครองเพื่อให้เราสามารถเขียนทับเขตของมันทำให้การเข้าถึงจากตัวควบคุมอื่น ๆ


1
ตัวควบคุมสืบทอดคุณสมบัติขอบเขตจากตัวควบคุมหลักไปยังขอบเขตของตัวเอง Clean! ความเรียบง่ายทำให้มันสวยงาม
Carlos R Balebona

ไม่สามารถทำงานได้ ในตัวควบคุมที่สองฉันใช้ $ scope.data และมันไม่ได้กำหนด
Bart Calixto

เขาพูดถึงตัวควบคุมที่ซ้อนกันฉันคิดว่า ไม่ค่อยมีประโยชน์กับฉันเช่นกัน
Norbert Norbertson

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

มันใช้
niether

5

เราสามารถเก็บข้อมูลในเซสชั่นและสามารถใช้งานได้ทุกที่ในโปรแกรมออก

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

สถานที่อื่น ๆ

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

4

ฉันเห็นคำตอบที่นี่และเป็นการตอบคำถามของการแบ่งปันข้อมูลระหว่างตัวควบคุม แต่ฉันควรทำอย่างไรถ้าฉันต้องการให้ตัวควบคุมตัวหนึ่งแจ้งอีกอันหนึ่งเกี่ยวกับข้อเท็จจริงที่ว่าข้อมูลมีการเปลี่ยนแปลง (โดยไม่ใช้การออกอากาศ)? ง่าย! เพียงใช้รูปแบบผู้เข้าชมที่มีชื่อเสียง:

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

ในลักษณะที่เรียบง่ายนี้คอนโทรลเลอร์หนึ่งสามารถอัพเดตคอนโทรลเลอร์อื่นที่ข้อมูลบางส่วนได้รับการอัพเดต


2
รูปแบบผู้สังเกตการณ์จะไม่ (เกี่ยวข้องกันหรือไม่ en.wikipedia.org/wiki/Observer_pattern ) รูปแบบของผู้เข้าชมเป็นอย่างอื่น ... en.wikipedia.org/wiki/Visitor_pattern
Ant Kutschera

3

ฉันได้สร้างโรงงานที่ควบคุมขอบเขตการแชร์ระหว่างรูปแบบของเส้นทางของเส้นทางดังนั้นคุณสามารถรักษาข้อมูลที่ใช้ร่วมกันได้เมื่อผู้ใช้นำทางในเส้นทางหลักของเส้นทางเดียวกัน

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

ดังนั้นหากผู้ใช้ไปที่เส้นทางเส้นทางอื่นเช่น '/ Support' ข้อมูลที่ใช้ร่วมกันสำหรับเส้นทาง '/ ลูกค้า' จะถูกทำลายโดยอัตโนมัติ แต่ถ้าหากแทนที่จะเป็นเช่นนี้ผู้ใช้จะไปที่เส้นทาง 'ลูก' เช่น '/ ลูกค้า / 1' หรือ '/ ลูกค้า / รายการ' ขอบเขตจะไม่ถูกทำลาย

คุณสามารถดูตัวอย่างได้ที่นี่: 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

เมื่อคลิกคุณสามารถเรียกวิธีการที่เรียกใช้การออกอากาศได้:

$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

วิธีหนึ่งในการใช้บริการเชิงมุม:

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
ใส่คำอธิบายเพิ่มเติมลงในคำตอบของคุณ
แกร์ฮาร์ดบาร์นาร์ด

2

สร้างโรงงานในโมดูลของคุณและเพิ่มการอ้างอิงของโรงงานในคอนโทรลเลอร์และใช้ตัวแปรในคอนโทรลเลอร์และตอนนี้รับค่าของข้อมูลในคอนโทรลเลอร์อื่นโดยเพิ่มการอ้างอิงที่คุณต้องการ


2

ฉันไม่ทราบว่ามันจะช่วยให้ทุกคน แต่ขึ้นอยู่กับ Charx (ขอบคุณ!) คำตอบฉันได้สร้างบริการแคชที่เรียบง่าย รู้สึกอิสระที่จะใช้รีมิกซ์และแบ่งปัน:

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

วิธีหนึ่งในการใช้บริการเชิงมุม:

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 วัตถุ $ scope มี $ emit, $ Broadcast, $ on และ $ rootScope Object มี $ emit ที่เหมือนกัน, $ broadcast, $ on

อ่านเพิ่มเติมเกี่ยวกับรูปแบบการเผยแพร่ / สมัครสมาชิกในเชิงมุมที่นี่


1

ในการปรับปรุงโซลูชันที่เสนอโดย @Maxim โดยใช้ $ Broadcast ส่งข้อมูลจะไม่เปลี่ยนแปลง

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

แต่เพื่อฟังข้อมูล

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

1

มีสามวิธีในการทำ

a) การใช้บริการ

b) การเอารัดเอาเปรียบขึ้นอยู่กับความสัมพันธ์ระหว่างพาเรนต์ / ลูกระหว่างขอบเขตคอนโทรลเลอร์

c) ในคีย์เวิร์ด "เป็น" ของ Angular 2.0 จะถูกส่งผ่านข้อมูลจากคอนโทรลเลอร์หนึ่งไปยังอีกคอนโทรลเลอร์หนึ่ง

สำหรับข้อมูลเพิ่มเติมพร้อมตัวอย่างโปรดตรวจสอบลิงค์ด้านล่าง:

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

ตัวควบคุมแรก

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

ตัวควบคุมที่สอง

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

-1

ฉันคิดว่า

วิธีที่ดีที่สุด

คือการใช้ $ localStorage (ทำงานได้ตลอดเวลา)

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

cardController ของคุณจะเป็น

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

คุณยังสามารถเพิ่ม

if($localStorage.selectedObj){
    $scope.selectedProducts = $localStorage.selectedObj;
}else{
    //redirect to select product using $location.url('/select-product')
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.