作成時にパラメーターをAngularJSコントローラーに渡すことはできますか?


278

私は、ユーザーのプロパティ、名前、電子メールなどを更新するためにAPIと通信するコントローラーを持っています。各ユーザーは'id'、プロファイルページが表示されるときにサーバーから渡されるものを持っています。

この値をAngularJSコントローラーに渡して、現在のユーザーのAPIエントリポイントが何であるかがわかるようにしたいと思います。に値を渡してみましたng-controller。例えば:

function UserCtrl(id, $scope, $filter) {

$scope.connection = $resource('api.com/user/' + id)

そしてHTMLで

<body ng-controller="UserCtrl({% id %})">

ここで{% id %}、サーバーから送信されたIDを出力します。エラーが発生します。

作成時にコントローラーに値を渡す正しい方法は何ですか?


6
URLの一部としてIDがある場合は、URLを読み取るだけです
akonsu 2013年

私は非常に似た問題を抱えていましたが、私の回答に投稿したとおりに解決しました。ライブラリを使用していると、JavaScript関数呼び出しの単純な基本概念を見落とすことがあります。
Jigar Patel 2013

@nickponline 21歳以上になっても、それは不可能だと思いますか?
om471987 2013年

回答:


362

ノート:

この答えは古いです。これは、望ましい結果をどのように達成できるかについての概念の証明にすぎません。ただし、以下のコメントのとおり、これは最適なソリューションではない可能性があります。次のアプローチをサポートまたは拒否するためのドキュメントはありません。このトピックの詳細については、以下のコメントを参照してください。

元の答え:

私はこれに「はい」と答え、絶対にそうすることができng-init、簡単な初期化関数を使用します。

これはプランカーでの例です

HTML

<!DOCTYPE html>
<html ng-app="angularjs-starter">
  <head lang="en">
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
    <script src="app.js"></script>
  </head>  
  <body ng-controller="MainCtrl" ng-init="init('James Bond','007')">
    <h1>I am  {{name}} {{id}}</h1>
  </body>
</html>

JavaScript

var app = angular.module('angularjs-starter', []);

app.controller('MainCtrl', function($scope) {

  $scope.init = function(name, id)
  {
    //This function is sort of private constructor for controller
    $scope.id = id;
    $scope.name = name; 
    //Based on passed argument you can make a call to resource
    //and initialize more objects
    //$resource.getMeBond(007)
  };


});

5
これをありがとう-私は多くの頭を悩ますことを節約し、それは私の状況に完全に働きました。コントローラをオブジェクトIDで初期化する必要があり、これはちょうど良かったです。
Masonoise 2013

26
ドキュメントからThe only appropriate use of ngInit for aliasing special properties of ngRepeat, as seen in the demo below. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.
Sergey Goliney

8
答えは、ドキュメントがこのアプローチに対して推奨することを明確にしますが、ドキュメントが公式のソリューションを提供する場所を誰かが私に指摘できますか?
マイケル・ペル2014

53
ええと、その理由を説明せずに、このアプローチを推奨しないだけではドキュメントは貧弱だと思います。これは素晴らしいアプローチだと思います。フレームワークの作成者がフレームワークを「間違った」方法で使用することを望まない場合は、その「間違った」方法での使用を可能にしたり、「正しい方法!
Shawn de Wet 2014

2
警告の言葉-スコープ値にバインドしようとしている場合、または値がコントローラー関数に存在することを期待していることを実際に実行している場合、コントローラー関数は実行前に既に実行されていng-initます-plnkr.co/editを
drzaus 2014年

143

私は非常に遅く、これによ、これは良いアイデアである場合、私は考えているが、あなたは含めることができ$attrs、コントローラは、要素の上に設けられた「引数」を使用して初期化することができるコントローラ機能での注射を例えば

app.controller('modelController', function($scope, $attrs) {
    if (!$attrs.model) throw new Error("No model for modelController");

    // Initialize $scope using the value of the model attribute, e.g.,
    $scope.url = "http://example.com/fetch?model="+$attrs.model;
})

<div ng-controller="modelController" model="foobar">
  <a href="{{url}}">Click here</a>
</div>

繰り返しますが、これが良いアイデアであるかどうかはわかりませんが、うまくいくようで、もう1つの選択肢です。


3
このアプローチを使用してオブジェクトを渡すにはどうすればよいですか?var obj = {a:1、b:2};
Neil

3
これは、ハイブリッドアプリの作成に関する問題に対する非常に合理的な解決策です。
超光速2014

2
@ニール:jsonオブジェクトを文字列化し、それをコントローラー内で解析する必要があります。より良い解決策ではありませんが、うまくいくかもしれません。マイケルの解法は、文字列のようなパラメーターに
適してい

1
これは実際に使用するよりも信頼性ng-initがあります-スコープ値にバインドしようとしている場合、それはng-init発生する前に既にコントローラー関数を実行しています-plnkr.co/edit/donCm6FRBSX9oENXh9WJ?p=preview
drzaus

2
ディレクティブなどを使用してすべての再発明を行うわけではありません。いくつかの異なる初期化パラメーターを持つ複数のビューで使用したいコントローラーがある場合、これが最も簡単で信頼性の高いソリューションです。
エリックH.

39

これも機能します。

JavaScript:

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

app.controller('MainCtrl', function($scope, name, id) {
    $scope.id = id;
    $scope.name = name;
    // and more init
});

HTML:

<!DOCTYPE html>
<html ng-app="angularApp">
  <head lang="en">
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
    <script src="app.js"></script>
    <script>
       app.value("name", "James").value("id", "007");
    </script>
  </head>
  <body ng-controller="MainCtrl">
    <h1>I am  {{name}} {{id}}</h1>
  </body>
</html>

3
これは、既存のコンストラクター注入メカニズムで機能する優れたソリューションです。あなたは本質的に、「name」と「id」と呼ばれる2つの単純なサービスを作成しています。インジェクターは、構築時に名前でそれらを照合します。参照:docs.angularjs.org/guide/providersの
Todd

1
いいね。これは、文字列のようなプリミティブ型だけでなく、オブジェクトでも機能することに注意してください。
jbustamovej 2014年

このソリューションの方が好きです。パラメータを渡すことで、重複したコードをモジュール化することができました。乾杯!
agentpx

これは最も慣用的な方法のように見えますが、角度のチームによって推奨されているかどうかは
わかり

これは本質的にグローバルなキー/値のペアをセットアップしますか?これらを特定のコントローラーインスタンスにスコープ指定して、メインコントローラーの下の子コントローラーでのみピックアップされるようにすることはできますか?そうでない場合、これはウィンドウレベルで通常のグローバルJavascript変数を設定することとほとんど変わらないようです。
jpierson 2017

16

ビューは設定を指示すべきではありません

Angularでは、テンプレートは決して設定を指示すべきではありません。これは本質的に人々がテンプレートファイルからコントローラーに引数を渡したいときに望むものです。これは滑りやすい斜面になります。構成設定がテンプレートにハードコーディングされている場合(ディレクティブやコントローラーの引数属性など)、そのテンプレートを1回限りの用途以外に再利用することはできません。すぐにそのテンプレートを再利用したいと思いますが、別の設定で今それを行うには、テンプレートを前処理して、変数がアンギュラーに渡される前に変数を注入するか、巨大なディレクティブを使用して巨人を吐き出しますHTMLのブロックなので、ラッパーdivとその引数を除くすべてのコントローラーHTMLを再利用します。小さなプロジェクトの場合、それは大したことではありません。大きなもの(角度が優れているもの)の場合、見苦しくなります。

代替:モジュール

このタイプの構成は、モジュールが処理するように設計されたものです。多くの角度のあるチュートリアルでは、人々はアプリケーション全体に対して単一のモジュールを持っていますが、実際にはシステムは設計されており、アプリケーション全体の小さな部分をラップする多くの小さなモジュールを完全にサポートしています。理想的には、コントローラー、モジュールなどは個別のファイルで宣言され、特定の再利用可能なチャンクにまとめられます。アプリケーションをこのように設計すると、簡単なコントローラー引数に加えて、多くの再利用が可能になります。

以下の例には2つのモジュールがあり、同じコントローラーを再利用していますが、それぞれに独自の構成設定があります。その構成設定は、依存関係の注入を介して渡されますmodule.value。コンストラクター依存性注入、再利用可能なコントローラーコード、再利用可能なコントローラーテンプレート(コントローラーdivはng-includeで簡単にインクルードできます)、HTMLなしで簡単に単体テスト可能なシステム、そして最後に再利用できるため、これは角度のある方法に準拠していますピースをつなぎ合わせるためのモジュールとしてのモジュール。

次に例を示します。

<!-- index.html -->
<div id="module1">
    <div ng-controller="MyCtrl">
        <div>{{foo}}</div>
    </div>
</div>
<div id="module2">
    <div ng-controller="MyCtrl">
        <div>{{foo}}</div>
    </div>
</div>
<script>
    // part of this template, or a JS file designed to be used with this template
    angular.element(document).ready(function() {
        angular.bootstrap(document.getElementById("module1"), ["module1"]);
        angular.bootstrap(document.getElementById("module2"), ["module2"]);
    });
</script>

<!-- scripts which will likely in be in their seperate files -->
<script>
    // MyCtrl.js
    var MyCtrl = function($scope, foo) {
    $scope.foo = foo;
    }

    MyCtrl.$inject = ["$scope", "foo"];

    // Module1.js
    var module1 = angular.module('module1', []);
    module1.value("foo", "fooValue1");
    module1.controller("MyCtrl", MyCtrl);

    // Module2.js file
    var module2 = angular.module('module2', []);
    module2.value("foo", "fooValue2");
    module2.controller("MyCtrl", MyCtrl);
</script>

実際の動作を確認してください:jsFiddle


私はすでに賛成していますが、あなたの貢献に感謝を表明したいと思います。この答えは私をより良い道に導きました。stackoverflowは、ハックスニペットだけでなく、ベストプラクティスを共有する場合に最適です。これは素晴らしいです:「テンプレートは決して設定を指示すべきではありません。それは本質的に人々がテンプレートファイルからコントローラーに引数を渡したいときに望むものです」。問題の核心にレーザービジョンを持っていることに感謝します。
害虫

15

@akonsuとナイジェルFindlaterが示唆するように、あなたは、URLがURL読むことができるindex.html#/user/:idとし$routeParams.id、それは、コントローラの内部で使用します。

あなたのアプリ:

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

app.config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/:type/:id', {templateUrl: 'myView.html', controller: 'myCtrl'});
}]);

リソースサービス

app.factory('MyElements', ['$resource', function($resource) {
     return $resource('url/to/json/:type/:id', { type:'@type', id:'@id' });
}]);

コントローラー

app.controller('MyCtrl', ['$scope', '$routeParams', 'MyElements', function($scope, $routeParams, MyElements) {
    MyElements.get({'type': $routeParams.type, "id": $routeParams.id }, function(elm) {
        $scope.elm = elm;
    })
}]);

次に、にelm応じてビューでアクセスできますid


8

ng-initがオブジェクトを$scopeに渡さない場合は、いつでも独自のディレクティブを記述できます。だからここに私が得たものがあります:

http://jsfiddle.net/goliney/89bLj/

Javasript:

var app = angular.module('myApp', []);
app.directive('initData', function($parse) {
    return function(scope, element, attrs) {
        //modify scope
        var model = $parse(attrs.initData);
        model(scope);
    };
});

function Ctrl1($scope) {
    //should be defined
    $scope.inputdata = {foo:"east", bar:"west"};
}

HTML:

<div ng-controller="Ctrl1">
    <div init-data="inputdata.foo=123; inputdata.bar=321"></div>
</div>

しかし、私のアプローチでは、コントローラーで既に定義されているオブジェクトのみを変更できます。


参考までにこれはうまく機能しますが、現在のangularのバージョンでは$ attrs(vs attrs)です
Dinis Cruz

8

あなたにとって最良の解決策は実際にはディレクティブであるように見えます。これにより、コントローラーを引き続き使用できますが、コントローラーのカスタムプロパティを定義できます。

ラッピングスコープの変数にアクセスする必要がある場合は、これを使用します。

angular.module('myModule').directive('user', function ($filter) {
  return {
    link: function (scope, element, attrs) {
      $scope.connection = $resource('api.com/user/' + attrs.userId);
    }
  };
});

<user user-id="{% id %}"></user>

ラッピングスコープの変数にアクセスする必要がない場合は、これを使用します。

angular.module('myModule').directive('user', function ($filter) {
  return {
    scope: {
      userId: '@'
    },
    link: function (scope, element, attrs) {
      $scope.connection = $resource('api.com/user/' + scope.userId);
    }
  };
});

<user user-id="{% id %}"></user>

7

$ routeProviderから変数を渡すと便利です。

たとえば、複数の画面に1つのコントローラーMyControllerを使用して、非常に重要な変数「mySuperConstant」を内部に渡します。

その単純な構造を使用します。

Router:

$routeProvider
            .when('/this-page', {
                templateUrl: 'common.html',
                controller: MyController,
                mySuperConstant: "123"
            })
            .when('/that-page', {
                templateUrl: 'common.html',
                controller: MyController,
                mySuperConstant: "456"
            })
            .when('/another-page', {
                templateUrl: 'common.html',
                controller: MyController,
                mySuperConstant: "789"
            })

MyController:

    MyController: function ($scope, $route) {
        var mySuperConstant: $route.current.mySuperConstant;
        alert(mySuperConstant);

    }

6

たとえば、ルートを設定するときに行うことができます

 .when('/newitem/:itemType', {
            templateUrl: 'scripts/components/items/newEditItem.html',
            controller: 'NewEditItemController as vm',
            resolve: {
              isEditMode: function () {
                return true;
              }
            },
        })

そして後でそれを

(function () {
  'use strict';

  angular
    .module('myApp')
    .controller('NewEditItemController', NewEditItemController);

  NewEditItemController.$inject = ['$http','isEditMode',$routeParams,];

  function NewEditItemController($http, isEditMode, $routeParams) {
    /* jshint validthis:true */

    var vm = this;
    vm.isEditMode = isEditMode;
    vm.itemType = $routeParams.itemType;
  }
})();

したがって、ここでは、ルートを設定するときに:itemTypeを送信し、後で$ routeParamsから取得します。



3

この質問は古いですが、私は自分のニーズに合うこの問題への回答を得ようと長い間苦労しましたが、簡単に見つけることができませんでした。次の解決策は現在受け入れられている解決策よりもはるかに優れていると思います。おそらく、この質問が最初に提起されたため、angularが機能を追加したためです。

短い答えですが、Module.valueメソッドを使用すると、データをコントローラーコンストラクターに渡すことができます。

ここで私のプランカーを参照してください

モデルオブジェクトを作成し、モジュールのコントローラーに関連付けて、「モデル」という名前で参照します

HTML / JS

  <html>
  <head>
    <script>
      var model = {"id": 1, "name":"foo"};

      $(document).ready(function(){
        var module = angular.module('myApp', []);
        module.value('model', model);
        module.controller('MyController', ['model', MyController]);
        angular.bootstrap(document, ['myApp']);
      });

      function confirmModelEdited() {
        alert("model name: " + model.name + "\nmodel id: " + model.id);
      }
    </script>

  </head>
  <body >
      <div ng-controller="MyController as controller">
        id: {{controller.model.id}} <br>
        name: <input ng-model="controller.model.name"/>{{controller.model.name}}
        <br><button ng-click="controller.incrementId()">increment ID</button>
        <br><button onclick="confirmModelEdited()">confirm model was edited</button>
    </div>
  </body>

</html>

次に、私のコントローラーのコンストラクターは、同じ識別子「モデル」を持つパラメーターを受け入れ、これにアクセスできます。

コントローラ

function MyController (model) {
  this.model = model;
}

MyController.prototype.incrementId = function() {
  this.model.id = this.model.id + 1;
}

ノート:

ブートストラップの手動初期化を使用していますを使用しています。これにより、angularに送信する前にモデルを初期化できます。これは、関連するデータのセットアップを待機し、必要に応じてオンデマンドでアプリの角度サブセットのみをコンパイルできるため、既存のコードとはるかにうまく機能します。

plunkerには、JavaScriptで最初に定義され、angularに渡されたモデルオブジェクトの値を警告するボタンを追加しました。これは、angularがモデルオブジェクトを参照していることを確認するために、コピーしてコピーするのではありません。

この行で:

module.controller('MyController', ['model', MyController]);

関数としてインラインで宣言するのではなく、MyControllerオブジェクトをModule.controller関数に渡します。これにより、コントローラオブジェクトをはるかに明確に定義できると思いますが、Angularのドキュメントはインラインで行う傾向があるため、明確にする必要があると思いました。

「$ scope」変数を使用するのではなく、「controller as」構文を使用して、値をMyControllerの「this」プロパティに割り当てています。これは$ scopeを使用してもうまく機能すると思います。コントローラーの割り当ては次のようになります。

module.controller('MyController', ['$scope', 'model', MyController]);

コントローラのコンストラクタには、次のような署名があります。

function MyController ($scope, model) {

なんらかの理由で、このモデルを2番目のモジュールの値としてアタッチし、依存関係としてプライマリモジュールにアタッチすることもできます。

私は彼の解決策が現在受け入れられているものよりもはるかに優れていると信じています

  1. コントローラに渡されるモデルは実際にはJavaScriptオブジェクトであり、評価される文字列ではありません。これはオブジェクトへの真の参照であり、オブジェクトへの変更は、このモデルオブジェクトへの他の参照に影響します。
  2. Angularは、受け入れられた回答のng-initの使用は誤用であると述べていますが、このソリューションはそれを行いません。

私が見た他のほとんどすべての例でAngularが機能するように見える方法には、モデルのデータを定義するコントローラーがあり、私には意味がありません。モデルとコントローラーの間に分離はなく、実際には私にMVC。このソリューションにより、コントローラーに渡す完全に独立したモデルオブジェクトを実際に作成できます。また、ng-includeディレクティブを使用する場合は、すべての角度のhtmlを別のファイルに入れて、モデルビューとコントローラーを個別のモジュラーピースに完全に分離できます。


2

angular-ui-routerを使用している場合、これは正しい解決策です:https : //github.com/angular-ui/ui-router/wiki#resolve

基本的に、コントローラーがインスタンス化される前に、「解決」する一連の依存関係を宣言します。各「状態」の依存関係を宣言できます。これらの依存関係は、コントローラの「コンストラクタ」に渡されます。


1

それを行う1つの方法は、それらがパブリックデータメンバーである引数の「ベッセル」として使用できる個別のサービスを用意することです。


ベストアンサー、IMO。私は現在、F5キーを押す勇気のあるユーザーのために、場合によってはブラウザーストレージも使用しているため、この状態が維持されます。
ヤマネ2015

1

私はここでの特定のユースケースのソリューションがあまり好きではなかったので、ここに表示されなかったので、自分がしたことを投稿したいと思いました。

ng-repeatループ内で、コントローラーをディレクティブのようなものにしたかっただけです。

<div ng-repeat="objParameter in [{id:'a'},{id:'b'},{id:'c'}]">
  <div ng-controller="DirectiveLikeController as ctrl"></div>
</div>

ここで、objParameter各DirectiveLikeController内のon作成にアクセスする(またはいつでも最新のobjParameterを取得する)ために必要なのは、$ scopeを挿入して呼び出すだけ$scope.$eval('objParameter')です。

var app = angular.module('myapp', []);
app.controller('DirectiveLikeController',['$scope'], function($scope) {
   //print 'a' for the 1st instance, 'b' for the 2nd instance, and 'c' for the 3rd.
   console.log($scope.$eval('objParameter').id); 
});

私が目にする唯一の本当の欠点は、親コントローラーがパラメーターの名前を知っている必要があることobjParameterです。


0

いいえ、できません。ng-initをハックhttp://docs.angularjs.org/api/ng.directive:ngInitとして使用できると思います。


4
私は違いますが、ng-initとinit fuctionを使用してこれを実現できます。
Jigar Patel、2013

警告の言葉-スコープ値にバインドしようとしている場合、または値がコントローラー関数に存在することを期待していることを実際に実行している場合、コントローラー関数は実行前に既に実行されていng-initます-plnkr.co/editを
drzaus 2014年

はい、可能です。少なくとも、data-paramsなどを使用します。しかし、それは間違いなく可能です。
フアン

0

これは(Marcin Wyszynskiの提案に基づく)解決策です。コントローラーに値を渡したいが、htmlでコントローラーを明示的に宣言していない場合(ng-initが必要と思われる)-たとえば、 ng-viewを使用してテンプレートをレンダリングし、routeProviderを介して対応するルートの各コントローラーを宣言しています。

JS

messageboard.directive('currentuser', ['CurrentUser', function(CurrentUser) {
  return function(scope, element, attrs) {
    CurrentUser.name = attrs.name;
  };
}]);

html

<div ng-app="app">
  <div class="view-container">
    <div ng-view currentuser name="testusername" class="view-frame animate-view"></div>
  </div>
</div>

このソリューションでは、CurrentUserは任意のコントローラーに挿入できるサービスであり、.nameプロパティを使用できます。

2つのメモ:

  • 私が遭遇した問題は、コントローラーのロード後に.nameが設定されることです。そのため、回避策として、コントローラーのスコープでユーザー名をレンダリングする前に短いタイムアウトを設定します。サービスに.nameが設定されるまで待機する適切な方法はありますか?

  • これは、現在のユーザーをすべての認証をAngularの外部で保持して、Angularアプリにアクセスする非常に簡単な方法のように感じます。before_filterを使用して、ログインしていないユーザーがAngularアプリがブートストラップされているhtmlにアクセスするのを防ぐことができます。そのhtml内で、ユーザーの詳細とやり取りしたい場合は、ログインしているユーザーの名前とIDさえも補間できますAngularアプリからのhttpリクエスト経由。ログインしていないユーザーに、デフォルトの「ゲストユーザー」でAngularアプリを使用することを許可できます。なぜこのアプローチが悪いのかについてのアドバイスは大歓迎です-それは賢明であるには余りにも簡単に感じられます!)


0

これは@Michael Tillerの優れた答えの拡張です。彼の答えは、$attrsオブジェクトをコントローラーに注入することにより、ビューから変数を初期化するために機能します。この問題は$routeProvider、ルーティングによってナビゲートするときに同じコントローラーが呼び出された場合に発生します。次に、ビューのコンパイル時にのみ使用できるUnknown provider : $attrsProviderため、インジェクターエラーが発生します$attrs。解決策は$routeParams、ルート$attrsからコントローラーを初期化するとき、およびビューからコントローラーを初期化するときに、変数(foo)を渡すことです。これが私の解決策です。

ルートから

$routeProvider.
        when('/mypage/:foo', {
            templateUrl: 'templates/mypage.html',
            controller: 'MyPageController',
            caseInsensitiveMatch: true,
            resolve: {
                $attrs: function () {
                    return {};
                }
            }
        });

これはなどのURLを処理し'/mypage/bar'ます。ご覧のとおり、fooはurlパラメータで渡され、インジェクタエラーがないようにに$injector空のオブジェクトを提供しています$attrs

ビューから

<div ng-controller="MyPageController" data-foo="bar">

</div>

今コントローラー

var app = angular.module('myapp', []);
app.controller('MyPageController',['$scope', '$attrs', '$routeParams'], function($scope, $attrs, $routeParams) {
   //now you can initialize foo. If $attrs contains foo, it's been initialized from view
   //else find it from $routeParams
   var foo = $attrs.foo? $attrs.foo : $routeParams.foo;
   console.log(foo); //prints 'bar'

});
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.