AngularJS:コントローラー間で変数を渡すにはどうすればよいですか?


326

2つのAngularコントローラーがあります。

function Ctrl1($scope) {
    $scope.prop1 = "First";
}

function Ctrl2($scope) {
    $scope.prop2 = "Second";
    $scope.both = Ctrl1.prop1 + $scope.prop2; //This is what I would like to do ideally
}

Ctrl1内部Ctrl2が未定義のため使用できません。しかし、そのように渡そうとすると…

function Ctrl2($scope, Ctrl1) {
    $scope.prop2 = "Second";
    $scope.both = Ctrl1.prop1 + $scope.prop2; //This is what I would like to do ideally
}

エラーが発生します。誰でもこれを行う方法を知っていますか?

している

Ctrl2.prototype = new Ctrl1();

また失敗します。

注:これらのコントローラーは相互にネストされていません。


多くの方法がありますが、最善の方法は角度時計です。フレームワークを使用するときは常に、作業を行うために彼女自身のメソッドを使用する最良の方法です
pejman

私はこのブログが非常に役立つブログ
Black Mamba

回答:


503

複数のコントローラー間で変数を共有する1つの方法は、サービス作成し、それを使用するコントローラーに挿入することです。

簡単なサービスの例:

angular.module('myApp', [])
    .service('sharedProperties', function () {
        var property = 'First';

        return {
            getProperty: function () {
                return property;
            },
            setProperty: function(value) {
                property = value;
            }
        };
    });

コントローラでのサービスの使用:

function Ctrl2($scope, sharedProperties) {
    $scope.prop2 = "Second";
    $scope.both = sharedProperties.getProperty() + $scope.prop2;
}

これはこのブログで非常にうまく説明されています(特にレッスン2以降)。

複数のコントローラー間でこれらのプロパティにバインドする場合は、バインドされた参照を保持するためにプリミティブ型(ブール、文字列、数値)ではなくオブジェクトのプロパティにバインドする方が効果的です。

例:のvar property = { Property1: 'First' };代わりにvar property = 'First';


更新:(うまくいけば)ここで物事をより明確にすることは、次の例を示すフィドルです:

  • 共有値の静的コピーへのバインド(myController1内)
    • プリミティブへのバインド(文字列)
    • オブジェクトのプロパティへのバインド(スコープ変数に保存)
  • 値が更新されるとUIを更新する共有値へのバインド(myController2内)
    • プリミティブを返す関数へのバインド(文字列)
    • オブジェクトのプロパティへのバインド
    • オブジェクトのプロパティへの双方向バインディング

5
この場合、sharedProperties.getProperty()が値を変更するときに、Ctrl2のスコープはどのように「認識」するでしょうか。
OpherV 2013

5
プロパティが変更bothされるたびにUIを更新したい場合は、関数に変更でき、角度ダイジェストプロセス中に呼び出されたり再評価されたりします。例については、このフィドルを参照してください。また、オブジェクトのプロパティにバインドする場合は、ビューで直接使用でき、この例のようにデータが変更されると更新されます。
Gloopy 2013

11
コントローラの変更を検出して対応したい場合は、オプションにgetProperty()関数をスコープに追加し、この例のように$ scope。$ watchを使用します。これらの例がお役に立てば幸いです!
Gloopy 2013

1
サービスはステートレスである必要があるため、ここに問題があります。サービス内でのプロパティの格納は間違っています(ただし便利です)。$ cacheFactoryを使用してデータの読み取りと書き込みを開始しました。私はGloopyとほぼ同じサービスを使用していますが、サービスに状態を保存する代わりに、キャッシュにあります。まず、キャッシュサービスを作成します。angular.module( 'CacheService'、['ng']).factory( 'CacheService'、function($ cacheFactory){return $ cacheFactory( 'CacheService');}); app.jsに含め、サービスに注入し、次のように使用します。return CacheService.get(key); またはCacheService.put(key、value);
ジョーダンパパレオ2013年

4
Angularのドキュメントで説明されているのでは.serviceなく、この答えがどのように、そしてなぜ使用されるのかを理解しようとしてい.factoryます。ドキュメントが別の方法を使用している場合、なぜこの回答の投票数が非常に多いのですか?
pspahn 2015

44

私は単純な例で単純なものを説明するのが好きです:)

これは非常に簡単なService例です:


angular.module('toDo',[])

.service('dataService', function() {

  // private variable
  var _dataObj = {};

  // public API
  this.dataObj = _dataObj;
})

.controller('One', function($scope, dataService) {
  $scope.data = dataService.dataObj;
})

.controller('Two', function($scope, dataService) {
  $scope.data = dataService.dataObj;
});

そしてここにjsbin

そして、これは非常に単純なFactory例です:


angular.module('toDo',[])

.factory('dataService', function() {

  // private variable
  var _dataObj = {};

  // public API
  return {
    dataObj: _dataObj
  };
})

.controller('One', function($scope, dataService) {
  $scope.data = dataService.dataObj;
})

.controller('Two', function($scope, dataService) {
  $scope.data = dataService.dataObj;
});

そしてここにjsbin


それが単純すぎる場合は、より洗練された例を次に示します

関連するベストプラクティスのコメントについては、こちらの回答ご覧ください。


1
はい、あなたに賛成です。常に物事をシンプルにするようにしてください。
Evan Hu

var _dataObj = {};直接参照を返すときに宣言する意味は何ですか?それは非公開ではありません。最初の例では実行できthis.dataObj = {};、2番目return { dataObj: {} };の例では無用の変数宣言IMHOです。
TJ

@TJポイントは、他のコンポーネント間でこの変数を共有することです。これは、共有の概念を示す基本的な例です。変数はブロック内でプライベートであり、公開パターンを使用してパブリック変数として公開します。このように、変数を保持することと使用することの間には責任の分離があります。
Dmitri Zaitsev

@DmitriZaitsevあなたは「簡単な例」と言いますが、私的な状態をどのように利用するかを適切に示さない限り、あなたは人々を混乱させるだけです。直接参照を返す限り、例にはプライベート状態はありません。
TJ

@TJ何も混乱しないと思います。プライベート変数はモジュールによって公開できます。より良い答えを自由に書いてください。
Dmitri Zaitsev

26

---この回答はこの質問に対するものではないことはわかっていますが、この質問を読んで、工場などのサービスを処理して、問題の発生を回避したいと考えている人が欲しいです-----

このためには、サービスまたはファクトリを使用する必要があります。

サービスは、ネストされていないコントローラー間でデータを共有するためのベストプラクティスです。

データ共有に関するこのトピックに関する非常に優れた注釈は、オブジェクトを宣言する方法です。AngularJSの罠に陥ったので、読む前に私は不運でした。この問題を回避できるようにお手伝いさせてください。

「ng-book:AngularJSに関する完全な本」を読んだところ、ベアデータとしてコントローラーで作成されたAngularJS ng-modelsが間違っている!

$ scope要素は次のように作成する必要があります:

angular.module('myApp', [])
.controller('SomeCtrl', function($scope) {
  // best practice, always use a model
  $scope.someModel = {
    someValue: 'hello computer'
  });

そしてこれは好きではありません:

angular.module('myApp', [])
.controller('SomeCtrl', function($scope) {
  // anti-pattern, bare value
  $scope.someBareValue = 'hello computer';
  };
});

これは、DOM(htmlドキュメント)に次のように呼び出しを含めることが推奨されているため(BEST PRACTICE)

<div ng-model="someModel.someValue"></div>  //NOTICE THE DOT.

これは、子コントローラーが親コントローラーからオブジェクトを変更できるようにする場合に、ネストされたコントローラーにとって非常に役立ちます。

しかし、あなたの場合、ネストされたスコープは必要ありませんが、オブジェクトをサービスからコントローラーに取得するための同様の側面があります。

サービス「ファクトリー」があり、戻りスペースに、objectCを含むobjectBを含むobjectAがあるとします。

コントローラーからobjectCをスコープに取得したい場合、それは間違いです。

$scope.neededObjectInController = Factory.objectA.objectB.objectC;

これは機能しません... 代わりにドットを1つだけ使用してください。

$scope.neededObjectInController = Factory.ObjectA;

次に、DOMで、objectAからobjectCを呼び出すことができます。これは、ファクトリに関連するベストプラクティスであり、最も重要なのは、予期しない、キャッチできないエラーを回避するのに役立ちます。


2
これは良い答えだと思いますが、消化するのはかなり難しいです。
pspahn 2015

17

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

5
その時点でグローバルスコープ変数を使用していますが、これは、さまざまな構造内のすべてをローカルにスコープするというAngularJSの考え方から逸脱しています。グローバル変数ファイルを追加しても同じことが実現され、変数が最初に定義された場所を見つけやすくなります。どちらの方法でも、推奨されません。
Organiccat 2014

4
@Organiccat-私はあなたの懸念を理解しています、そしてそれが私がすでに好ましい方法がサービスであることをすでに述べた理由です、それは間違いありません。しかし、ya angularもこの方法を提供します。グローバルをどのように管理したいかはあなた次第です。このアプローチが私にとって最も効果的なシナリオがありました。
Sanjeev 2014

8

上記のサンプルは魅力のように機能しました。複数の値を管理する必要がある場合に備えて、変更を加えました。これが役に立てば幸いです!

app.service('sharedProperties', function () {

    var hashtable = {};

    return {
        setValue: function (key, value) {
            hashtable[key] = value;
        },
        getValue: function (key) {
            return hashtable[key];
        }
    }
});

1
また、サービスを使用して、さまざまなコントローラー間でデータを共有するサンプルも作成しました。皆さんも気に入っていただけたら幸いです。 jsfiddle.net/juazammo/du53553a/1
Juan Zamora

1
動作しますが、これは通常の構文です.factorydocs.angularjs.org/api/auto/service/$provide#service.service
Dmitri Zaitsev

1
ドミトリ、あなたが私の観点からしかし角度みんな、右ある、ちょうど....まあ....私はサービス(ファサード)と工場の間で持っていた概念をビット変更
フアン・サモラ

1
そして、私が間違っていれば私を訂正してください。サービスは、オブジェクトまたは値になる可能性があるものを返すことを目的としています。ファクトリーはオブジェクトを作成することを目的としています。実際に何かを返す機能の集まりであるファサードは、サービスの場所を私が考えたものです。工場からの呼び出し機能を含みます。繰り返しますが、これは私にとって何であるかという基本的な概念に入るのであり、実際にAngularの観点からはそうではありません。(Abstract Factory dofactory.com/net/abstract-factory-design-pattern)とアダプターアプローチは、サービスとして公開するものです
Juan Zamora

1
アダプターパターンはこちらdofactory.com/net/adapter-design-pattern
Juan Zamora

6

私は値を使用する傾向があり、これがなぜ悪い考えであるかを誰もが話し合うのを喜んでいます

var myApp = angular.module('myApp', []);

myApp.value('sharedProperties', {}); //set to empty object - 

次に、サービスごとに値を挿入します。

ctrl1に設定:

myApp.controller('ctrl1', function DemoController(sharedProperties) {
  sharedProperties.carModel = "Galaxy";
  sharedProperties.carMake = "Ford";
});

ctrl2からのアクセス:

myApp.controller('ctrl2', function DemoController(sharedProperties) {
  this.car = sharedProperties.carModel + sharedProperties.carMake; 

});

これはサービスの使用とどう違うのですか?
dopatraman 2016

5

間で変数を渡す方法を次の例が示す兄弟のコントローラ値が変化したときにアクションを取ります。

使用例:別のビューのコンテンツを変更するフィルターがサイドバーにあります。

angular.module('myApp', [])

  .factory('MyService', function() {

    // private
    var value = 0;

    // public
    return {
      
      getValue: function() {
        return value;
      },
      
      setValue: function(val) {
        value = val;
      }
      
    };
  })
  
  .controller('Ctrl1', function($scope, $rootScope, MyService) {

    $scope.update = function() {
      MyService.setValue($scope.value);
      $rootScope.$broadcast('increment-value-event');
    };
  })
  
  .controller('Ctrl2', function($scope, MyService) {

    $scope.value = MyService.getValue();

    $scope.$on('increment-value-event', function() {    
      $scope.value = MyService.getValue();
    });
  });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="myApp">
  
  <h3>Controller 1 Scope</h3>
  <div ng-controller="Ctrl1">
    <input type="text" ng-model="value"/>
    <button ng-click="update()">Update</button>
  </div>
  
  <hr>
  
  <h3>Controller 2 Scope</h3>
  <div ng-controller="Ctrl2">
    Value: {{ value }}
  </div>  

</div>


4

コントローラとディレクティブの間でデータを共有するための推奨される方法は、すでに指摘されているようにサービス(ファクトリ)を使用することであることを指摘して、この質問に貢献したいと思いますが、それを行う方法の実用的な例を実行します。

これが作業中のプランカーです: http ://plnkr.co/edit/Q1VdKJP2tpvqqJL1LF6m?p=info

最初に、共有データを持つサービスを作成します。

app.factory('SharedService', function() {
  return {
    sharedObject: {
      value: '',
      value2: ''
    }
  };
});

次に、それをコントローラーに注入し、スコープで共有データを取得します。

app.controller('FirstCtrl', function($scope, SharedService) {
  $scope.model = SharedService.sharedObject;
});

app.controller('SecondCtrl', function($scope, SharedService) {
  $scope.model = SharedService.sharedObject;
});

app.controller('MainCtrl', function($scope, SharedService) {
  $scope.model = SharedService.sharedObject;
});

ディレクティブでも同じことができます:

app.directive('myDirective',['SharedService', function(SharedService){
  return{
    restrict: 'E',
    link: function(scope){
      scope.model = SharedService.sharedObject;
    },
    template: '<div><input type="text" ng-model="model.value"/></div>'
  }
}]);

この実用的で明確な答えが誰かに役立つことを願っています。


3

あなたはサービスや工場でそれを行うことができます。いくつかのコアの違いはありますが、基本的に同じです。thinkster.ioでのこの説明が最も理解しやすいものであることがわかりました。単純で、要点があり、効果的です。


1
「サービスや工場でそれができる」 -どのように。?それを行う方法は、OPが求めていることです...外部リソースにリンクするのではなく、stackoverflow自体に完全な回答を投稿してください。リンクが時間とともにダウンする可能性があります。
TJ

2

プロパティをスコープの親の一部にすることもできませんか?

$scope.$parent.property = somevalue;

私はそれが正しいと言っているわけではありませんが、それはうまくいきます。


3
著者はそれを述べたNOTE: These controllers are not nested inside each other.。これらがネストされたコントローラまたは同じ親を共有するコントローラである場合、これは機能しますが、それは期待できません。
Chris Foster

2
それが$parent回避できるかどうかに依存することは、一般的に悪い習慣です。適切に設計された再利用可能なコンポーネントは、その親を知らないはずです。
Dmitri Zaitsev 2014

2

ああ、別の代替手段としてこの新しいものを少し持ってください。これはlocalstorageであり、angularが機能する場所で機能します。どういたしまして。(しかし、本当に、男に感謝します)

https://github.com/gsklee/ngStorage

デフォルトを定義します。

$scope.$storage = $localStorage.$default({
    prop1: 'First',
    prop2: 'Second'
});

値にアクセスします。

$scope.prop1 = $localStorage.prop1;
$scope.prop2 = $localStorage.prop2;

値を保存する

$localStorage.prop1 = $scope.prop1;
$localStorage.prop2 = $scope.prop2;

アプリにngStorageを、コントローラーに$ localStorageを注入することを忘れないでください。


1
これにより、別の問題、つまり永続ストレージが解決されます。これは、問題のスケーラブルな解決策ではありません。ローカルストレージオブジェクトを変更するなどの副次的な影響でコードが漏洩し、名前の衝突の脆弱性があるためです。
Dmitri Zaitsev

1

これを行うには2つの方法があります

1)get / setサービスを使用する

2) $scope.$emit('key', {data: value}); //to set the value

 $rootScope.$on('key', function (event, data) {}); // to get the value

1

2番目のアプローチ:

angular.module('myApp', [])
  .controller('Ctrl1', ['$scope',
    function($scope) {

    $scope.prop1 = "First";

    $scope.clickFunction = function() {
      $scope.$broadcast('update_Ctrl2_controller', $scope.prop1);
    };
   }
])
.controller('Ctrl2', ['$scope',
    function($scope) {
      $scope.prop2 = "Second";

        $scope.$on("update_Ctrl2_controller", function(event, prop) {
        $scope.prop = prop;

        $scope.both = prop + $scope.prop2; 
    });
  }
])

HTML:

<div ng-controller="Ctrl2">
  <p>{{both}}</p>
</div>

<button ng-click="clickFunction()">Click</button>

詳細については、plunkerを参照してください。

http://plnkr.co/edit/cKVsPcfs1A1Wwlud2jtO?p=preview


1
Ctrl2(リスナー)がの子コントローラーである場合にのみ機能しCtrl1ます。兄弟コントローラーはを介して通信する必要があります$rootScope
herzbube

0

サービスを提供したくない場合は、このようにすることができます。

var scope = angular.element("#another ctrl scope element id.").scope();
scope.plean_assign = some_value;

37
この答えが機能することは間違いありませんが、モデル/コントローラーコードにDOMオブジェクトを含めないというAngularJSの哲学に反することに注意したいと思います。
JoeCool 2013年

3
-1というのは、私の意見では、DOMを介したコントローラー通信はあまり慣習ではないからです。
Chris Foster

3
@ChrisFoster、ハンマーが「道具」として販売されているからといって、それがペーパーウェイトとして使用できないという意味ではありません。私は、そこにあるすべてのフレームワークまたはツールについて、「ベストプラクティス」リストを「曲げる」必要がある開発者を必ず見つけるでしょう。
Andrei V 14

5
@AndreiV-貧弱なアナロジー、紙の重りとしてハンマーを使用することに不利はありません。このような悪い習慣を行うことには明らかな欠点があり、スパゲッティコードに簡単につながる可能性があります。上記のコードは、コントローラーがDOM内のどこにあるかに依存しており、テストが非常に難しいため、脆弱です。サービスを使用することには理由があります。実装はテンプレートに関連付けられないためです。開発者はしばしばベストプラクティスのリストを曲げる必要があることに同意しますが、よりよく機能する明確で一般的な、よりモジュール化されたベストプラクティスがある場合はそうではありません。
クリスフォスター

-1

$ rootScopeとサービスの他に、角度を拡張して共有データを追加するためのクリーンで簡単な代替ソリューションがあります。

コントローラで:

angular.sharedProperties = angular.sharedProperties 
    || angular.extend(the-properties-objects);

このプロパティは、スコープから分離された「angular」オブジェクトに属し、スコープとサービスで共有できます。

その1つの利点は、オブジェクトを注入する必要がないことです。これらは、定義した直後にどこからでもアクセスできます。


2
これは、windowオブジェクト全体にグローバル変数があるようなものです... 角度を汚染する場合は、先に進んでウィンドウオブジェクトを汚染しないでください...
TJ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.