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>

だから私の疑問は、このクリックされた製品を最初のコントローラーから2番目のコントローラーにどのように渡すのですか?カートもコントローラーであると思いました。

ディレクティブを使用してクリックイベントを処理します。また、私は上記の機能を実現するためにサービスを使用する必要があると感じていますが、どうやって理解できないのですか?カートは事前に定義されているため、ユーザーがページに応じて追加される製品の数は5/10になる可能性があります。だから私はこのジェネリックを維持したいと思います。

更新:

ブロードキャストするサービスを作成し、2番目のコントローラーでそれを受信します。今、クエリはどのようにしてDOMを更新するのですか?私の製品をドロップするリストはかなりハードコードされているので。


回答:


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

1
自動的に更新する場合は、Maxim Shoustinの回答からのブロードキャストを追加し、$ on関数内でgetProducts()を呼び出して、CartCtrlのスコープを更新できます。
チャリーズ2013年

2
@krillgarあなたは完全に正しいです、それは最初に見落とされました。有効な解決策を反映するように回答を編集しました。
チャリーズ2014

1
@DeveshMはい、あなたが提案するようにしてください。メソッドを拡張して、メモリにデータを保持するだけでなく、より永続的にすることを検討することもできます($httpたとえば、呼び出しを介してサーバーに保存します)。
チャリーズ2015

1
たとえば、2つのproductControllersと2つのカートがある場合はどうなりますか?両方に要素が追加されますか?この場合、それをどのように解決しますか?
atoth '25

67

このクリックした製品を最初のコントローラーから2番目のコントローラーに渡すにはどうすればよいですか?

クリックすると、broadcastを呼び出すメソッドを呼び出すことができます。

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

そして、2番目のコントローラーは次のようにこのタグをリッスンします。

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

$ scopeをサービスに注入できないので、シングルトン$ scopeのようなものはありません。

しかし、注入でき$rootScopeます。したがって、値をサービスに格納する$rootScope.$broadcast('SOME_TAG', 'your value');と、サービス本体で実行できます。(サービスについては@Charxの説明を参照してください)

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

$ broadcast$ emit についての良い記事をチェックしてください


1
うん、これは私が工場を使っている魅力のように機能しますか?私が行き詰まっている最後の1つだけがあります。製品をクリックするたびに新しいコントローラーでデータを取得します。DOMで更新するにはどうすればよいですか?私はすでに境界線でハードコードされた5つのリストを言っているので、各製品はそれらの中に入る必要があります
kishanio

1
@KT-あなたは今までに答えを得ましたか?明らかな質問のようですが、どこにも答えが見つかりません。コントローラーで値を変更してください。そのアプリの値が変更されると、他のリスニングコントローラーは必要に応じて自身を更新します。見つかりません。
raddevus 14

2
@daylightあなたのqの答え。使用しているコントローラー構造(並列または子親)に基づいています。$rootScope.$broadcast同じレベルの並列構造を持つすべてのコントローラーに通知します。
Maxim Shoustin 14

@MS返信ありがとうございます。$ watchを使用してこれを行う方法はありますか?ところで、Mineは並列コントローラーであり、私はあなたの方法を使用してそれを動作させました。私にとって、$ watchを($ rootScopeにさえ)追加しようとするときはいつでも、2番目のコントローラーの$ watchに関連付けられたメソッドは、初めて(2番目のコントローラーの初期化)だけを起動します。ありがとう。
raddevus 14

1
$watch値が$rootScopeレベルに存在する場合に使用できます。そうでない場合、ブロードキャストのみが他のコントローラーに通知する可能性があります。参考までに、他のプログラマーがあなたのコードを見る場合broadcast-ロジックはあなたがやろうとしていることは明らかです-「ブロードキャストイベント」。とにかく、私の経験から。使用するためにその良いではない練習 rootscopeのためにwatch。「初回のみ火災」について:この例を見てみましょう:次のご覧ください plnkr.co/edit/5zqkj7iYloeHYkzK1Prt ? p=preview古い値と新しい値があります。古い値と等しくない新しい値を取得するたびに確認してください
Maxim Shoustin 2014

24

$ rootScopeを使用して、サービスを作成しないソリューション:

アプリコントローラー間でプロパティを共有するには、Angular $ rootScopeを使用できます。これは、データを共有するもう1つのオプションであり、人々がそれを理解できるようにデータを配置します。

コントローラ間で一部の機能を共有するための推奨される方法は、$ 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;
}]);

テンプレートで$ 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 14

4
$ rootScopeにはグローバルスコープがあるため、ネイティブグローバル変数と同様に、慎重に使用する必要があります。いずれかのコントローラーがその値を変更すると、グローバルスコープに対して変更されます。
Sanjeev 14

1
サービスとファクトリーはどちらもシングルトンですが、コントローラーにサービスを注入するかどうかを選択できます。ただし、すべての子スコープはルートスコープから派生し、プロトタイプチェーンに従います。したがって、スコープのプロパティにアクセスしようとして、そのプロパティが存在しない場合は、チェーンを検索します。ルートスコープのプロパティを不用意に変更する可能性があります。
Sanjeev

16

これは2つの方法で実行できます。

  1. を使用しますが$rootscope、これはお勧めしません。$rootScope最上位のスコープです。アプリ$rootScopeは、アプリのすべてのコンポーネント間で共有される1つだけを持つことができます。したがって、グローバル変数のように機能します。

  2. サービスの使用。これを行うには、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

ユーザーがページを更新するとどうなりますか?THEN getProducts()は、お客様に何も与えないWILL(Uはリフレッシュにないすべてのユーザーを伝えることができない、できのuを?)!
shreedharをBHAT

2
@shreedharbhat問題は、ページの再読み込み後もデータを保持することについては問いません。
Jack Vial 2017年

9

コントローラ間でデータを共有するさらに簡単な方法は、ネストされたデータ構造を使用することです。代わりに、例えば

$scope.customer = {};

私たちは使うことができます

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

data私たちはそのフィールドを上書きすることができるようにプロパティが他のコントローラからのアクセスを保ち、親スコープから継承されます。


1
コントローラは、親コントローラから独自のスコープにスコーププロパティを継承します。掃除!シンプルさで
美しく

それを機能させることはできません。2番目のコントローラーでは$ scope.dataを使用していますが、これは未定義です。
Bart Calixto 2015

彼はネストされたコントローラーについて話していると思います。私もあまり使いません。
Norbert Norbertson 2017年

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

Nietherを使用します
Leathan

5

セッションにデータを保存して、outプログラムのどこでも使用できます。

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

別の場所

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

4

私はここで答えを見ました、そしてそれはコントローラー間でデータを共有するという質問に答えていますが、(ブロードキャストを使用せずに)データが変更されたことを1つのコントローラーから他のコントローラーに通知したい場合はどうすればよいですか?かんたん!有名なビジターパターンを使用するだけです。

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 2015

3

ルートパスのパターン間の共有スコープを制御するファクトリを作成したので、ユーザーが同じルートの親パスに移動しているときにも共有データを維持できます。

.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


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) {
      // ....
})

using $rootScope:

4

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

5

角度サービスを使用する1つの方法:

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
あなたの答えにもう少し説明を入れてください。
Gerhard Barnard

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

角度サービスを使用する1つの方法:

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オブジェクトには同じ$ emit、$ broadcast、$ onがあります

Angular でのパブリッシュ/サブスクライブデザインパターンについての詳細はこちら


1

$ broadcastを使用して@Maximによって提案されたソリューションを改善するには、送信データを変更しないでください

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


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>

2番目のコントローラー

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