Angular js init ng-model from default values


126

データベースから読み込まれた値を持つフォームがあるとします。どのようにng-modelを初期化しますか?

例:

<input name="card[description]" ng-model="card.description" value="Visa-4242">

私のコントローラーでは、最初は$ scope.cardが未定義です。このようなことをする以外に方法はありますか?

$scope.card = {
  description: $('myinput').val()
}

回答:


136

これは、新しいAngularアプリケーションでよくある間違いです。回避できる場合は、サーバー上のHTMLに値を書き込む必要はありません。実際のところ、サーバーでHTMLを完全にレンダリングする必要がなくなるのであれば、なお良いでしょう。

理想的には、Angular HTMLテンプレートを送信してから、JSONの$ httpを介して値をプルダウンし、それらをスコープに配置します。

したがって、可能であれば、次のようにします。

app.controller('MyController', function($scope, $http) {
    $http.get('/getCardInfo.php', function(data) {
       $scope.card = data;
    });
});

<input type="text" ng-model="card.description" />

絶対にサーバーからHTMLに値をレンダリングする必要がある場合は、それらをグローバル変数に入れて、$ windowでアクセスできます。

ページのヘッダーに次のように書きます。

<head>
   <script>
       window.card = { description: 'foo' };
   </script>
</head>

そして、あなたのコントローラーではあなたはそれを次のように得るでしょう:

app.controller('MyController', function($scope, $window) {
   $scope.card = $window.card;
});

お役に立てば幸いです。


4
いや、それは役立ちます。Angularの連中がこの決定を下したことにショックを受けたと思います。
Calvin Froedge

125
驚かないでください... HTMLのレンダリングはサーバーからブラウザに移行しています。最近のJavaScriptには数十のMVCフレームワークがあり、サーバーですべての単一ページをレンダリングするよりも、サーバーでJSON / XMLデータをJavaScriptアプリにホストする方がはるかに効率的です。サーバーにヒットさせるのではなく、多くの作業をクライアントのマシンにオフセットします。言うまでもなく、帯域幅が節約されます。さらに、HTTP経由で同じJSONを使用するネイティブモバイルアプリ(または実際には何でも)を用意することもできます。これが未来です。
Ben Lesh

3
@blesh:すばらしい答えです。どうもありがとう。これが前進する方法であることに同意し、このアプローチを自分で採用し始めました。この主張を裏付けるリンクはありますか?htmlのレンダリングをサーバーからクライアントに移動すると、ページの読み込み時間が遅くなる可能性があることも心配です。特に、ナビゲーションツリーなど、多くのHTMLをレンダリングできるモバイルデバイスではそうです。
GFoley83 2013

19
これが「間違い」だとは思わない。場合によっては、クライアント側のレンダリングが最適なソリューションです。それ以外の場合は、サーバー側のレンダリングが適しています。両方の手法で角度を使用できます。クライアント側のレンダリングは、「角度のある方法」ではなく、「角度のある方法」として保持されるべきではありません。Angularはそれよりも柔軟性があります。
hunterloftis 2013

8
@blesh、確かに-SEOが重要であるが、クローラーの代替コンテンツのコストが、埋め込みデータでAngularを実装するコストより高い場合-認識される速度またはレンダリングまでの時間に非常に高い価値を置くアプリケーションユーザーエクスペリエンス-多くのコンテンツサイトとeコマースサイトは、これらのカテゴリの1つに分類されます。クライアントレンダリングを試みた後、より良いユーザーエクスペリエンスを提供するためにサーバーに戻ったTwitterのエンジニアも、その理由を概説しました: blog.twitter.com/2012/improving-performance-twittercom
hunterloftis

236

@bleshが提案するようにアプリを作り直すことができない場合($ httpまたは$ resourceを使用してJSONデータを引き下げ、$ scopeに入力する)、代わりにng-initを使用できます。

<input name="card[description]" ng-model="card.description" ng-init="card.description='Visa-4242'">

AngularJS-ng-modelが使用されている場合、入力テキストボックスのValue属性が無視されるも参照してください


角度ウェイの+1(あまり好ましくない方法)。例:jsfiddle.net/9ymB3
John Lehmann

2
AngularをC#のWebフォームで使用ng-initしています<input name="phone" data-ng-model="frm.phone" data-ng-init="frm.phone= '<%: Model.Phone %>'" data-ng-pattern="/^[0-9() \-+]+$/" type="tel" required />。コードビハインド/ポストバックなどから値を設定する場合に使用すると非常に便利です。ちょっと醜い?はい。ただし、その過程で統合の頭痛の種を解決します。
GFoley83 2013

13
+1、エレガント!人々は迅速で迅速な行動を支持していますが、SOの多くの人々は、「クライアント側ですべて実行する」と言っています。たとえば、何百万ものhttp呼び出しは、迅速なサイトに適しています。データサーバー側をテンプレートにシードすると、キャッシュ戦略が有効になります。静的、または動的/部分的なMemcached。これがTwitterの機能です。時にはそれは便利ですが、他の場合ではありません。それを強調したかった。それを追加する必要があるだけで、あなたが別の方法で言ったことではありません、@ Mark。
oma 2013年

1
実際、私はそれを取り戻します。これは私にとって最も効果的です。すばらしいです。stackoverflow.com
Darren

1
FWIW:テストできないため、テンプレート式での変数の割り当ては嫌いです。
Ben Lesh

60

これは明らかに欠けていますが、AngularJSの修正を簡単に追加できます。入力フィールドからモデル値を設定するクイックディレクティブを作成するだけです。

<input name="card[description]" value="Visa-4242" ng-model="card.description" ng-initial>

これが私のバージョンです:

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

app.directive('ngInitial', function() {
  return {
    restrict: 'A',
    controller: [
      '$scope', '$element', '$attrs', '$parse', function($scope, $element, $attrs, $parse) {
        var getter, setter, val;
        val = $attrs.ngInitial || $attrs.value;
        getter = $parse($attrs.ngModel);
        setter = getter.assign;
        setter($scope, val);
      }
    ]
  };
});

すてきなクイックディレクティブ。これをng-modelにパッケージ化しないで、ほとんどの人が期待するとおりにvalue =とng-model =が連携できるようにする理由はありますか?
hunterloftis 2013

かなりいい!これは便利です。
サンティアゴバスルト2013

3
正解です。これをtextareas
Ryan Montgomery

controllerディレクティブを定義した理由と、linkメソッドを使用しなかった理由 私はangularjsの初心者です
エブラムハリル14

1
val = $attrs.ngInitial || $attrs.value || $element.val()選択した要素で動作するように変更する必要がありました。
fjsj 2014年

12

私見の最善の解決策は@Kevin Stoneディレクティブですが、すべての条件(fe select、textarea)で機能するようにアップグレードする必要があり、これは確実に機能します。

    angular.module('app').directive('ngInitial', function($parse) {
        return {
            restrict: "A",
            compile: function($element, $attrs) {
                var initialValue = $attrs.value || $element.val();
                return {
                    pre: function($scope, $element, $attrs) {
                        $parse($attrs.ngModel).assign($scope, initialValue);
                    }
                }
            }
        }
    });

そして、selectはどうですか?
jwize 14

7

カスタムディレクティブを使用できます(textarea、select、radio、checkboxをサポートしています)。このブログの投稿https://glaucocustodio.github.io/2014/10/20/init-ng-model-from-form-をチェックしてください。 fields-attributes /


1
これは素晴らしい投稿です。質問以来の私のお気に入りの答えは、イニシャル値に従って入力の値を設定することでした。
RPDeshaies 2014

私はこのコードをテストするためにPlnkrを作成しましたが、うまく機能しました:plnkr.co/edit/ZTFOAc2ZGIZr6HjsB5gH
p=

6

HTMLコード内でも使用できます。 ng-init="card.description = 12345"

Angularでは推奨されていません。上記のように、コントローラーのみを使用する必要があります。

しかし、それは動作します:)


4

私のフォームには検証とマスクがいくつかあるので、私は単純なアプローチをとっています。そのため、jqueryを使用して再度値を取得し、検証に「変更」イベントを発生させます。

$('#myidelement').val('123');
$('#myidelement').trigger( "change");

3

他の人が指摘したように、ビューのデータを初期化することは良い習慣ではありません。ただし、コントローラのデータを初期化することをお勧めします。(http://docs.angularjs.org/guide/controllerを参照)

だからあなたは書くことができます

<input name="card[description]" ng-model="card.description">

そして

$scope.card = { description: 'Visa-4242' };

$http.get('/getCardInfo.php', function(data) {
   $scope.card = data;
});

これにより、ビューにデータが含まれなくなり、実際の値がロードされている間にコントローラーが値を初期化します。


3

上記のhttps://stackoverflow.com/a/17823590/584761のKevin Stoneのアプローチが気に入った場合は 、「input」などの特定のタグのディレクティブを作成することで、より簡単なアプローチを検討してください

app.directive('input', function ($parse) {
    return {
        restrict: 'E',
        require: '?ngModel',
        link: function (scope, element, attrs) {
            if (attrs.ngModel) {
                val = attrs.value || element.text();
                $parse(attrs.ngModel).assign(scope, val);
            }
        }
    }; });

このルートを使用する場合、すべてのタグにng-initialを追加することを心配する必要はありません。モデルの値をタグのvalue属性に自動的に設定します。value属性を設定しない場合、デフォルトで空の文字列になります。


3

サーバー中心のアプローチは次のとおりです。

<html ng-app="project">
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
    <script>
        // Create your module
        var dependencies = [];
        var app = angular.module('project', dependencies);

        // Create a 'defaults' service
        app.value("defaults", /* your server-side JSON here */);

        // Create a controller that uses the service
        app.controller('PageController', function(defaults, $scope) {
            // Populate your model with the service
            $scope.card = defaults;
        });
    </script>

    <body>
        <div ng-controller="PageController">
            <!-- Bind with the standard ng-model approach -->
            <input type="text" ng-model="card.description">
        </div>
    </body>
</html>

$ provide.valueがデフォルト値を含むサービスを登録することを除いて、この質問のより一般的な回答と同じ基本的な考え方です。

したがって、サーバー上では、次のようになります。

{
    description: "Visa-4242"
}

そして、あなたが選んだサーバー側の技術を介してあなたのページにそれを入れてください。ここに要点があります:https : //gist.github.com/exclsr/c8c391d16319b2d31a43


1
私の知る限り、これは、最初の$ http要求を行うことができない場合に問題を処理するための最もAngularの方法です。また、後で$ httpにスワップする場合は、変更がはるかに簡単になるという利点もあります。可能な拡張機能の1つは、変換をさらに簡単にするために、代わりにpromiseを返すことです。ここでは、グローバル変数の設定やng-initialの使用を避けています。
リチャード2014年

1

これは、上記のアイデアのより一般的なバージョンです...モデルに値があるかどうかを単にチェックし、ない場合は、値をモデルに設定します。

JS:

function defaultValueDirective() {
    return {
        restrict: 'A',
        controller: [
            '$scope', '$attrs', '$parse',
            function ($scope, $attrs, $parse) {
                var getter = $parse($attrs.ngModel);
                var setter = getter.assign;
                var value = getter();
                if (value === undefined || value === null) {
                    var defaultValueGetter = $parse($attrs.defaultValue);
                    setter($scope, defaultValueGetter());
                }
            }
        ]
    }
}

HTML(使用例):

<select class="form-control"
        ng-options="v for (i, v) in compressionMethods"
        ng-model="action.parameters.Method"
        default-value="'LZMA2'"></select>

これは私にとってはうまくいきました$scopeが、呼び出しに渡した後にのみgetter()-これがこれを試みている他の誰にとっても問題が解決することを願っています!
zesda 2017

0

@Mark Rajcokの提案を試しました。文字列値(Visa-4242)で機能します。このフィドルを参照してください。

フィドルから:

フィドルで行われるのと同じことはng-repeat、誰でも推奨できるを使用して行うことができます。しかし、@ Mark Rajcokの回答を読んだ後、プロファイルの配列を持つフォームに対して同じことを試したかっただけです。$ scope.profiles = [{}、{}];が得られるまで、問題はありません。コントローラのコード。このコードを削除すると、エラーが発生します。しかし、通常のシナリオで$scope.profiles = [{},{}]; は、サーバーからhtmlを印刷またはエコーするときに印刷できません。@Mark Rajcokがのような文字列値に対して行ったのと同様の方法で<input name="card[description]" ng-model="card.description" ng-init="card.description='Visa-4242'">、サーバーからJavaScript部分をエコーする必要なく、上記を実行することは可能ですか?


1
ng-initを使用して配列を初期化し、ng-repeatを使用してフォーム行を出力できます:jsfiddle.net/6tP6x/1
Karen

0

Ryan Montgomeryの「修正」にselect要素のサポートを追加しました

<select class="input-control" ng-model="regCompModel.numberOfEmployeeId" ng-initial>
    <option value="1af38656-a752-4a98-a827-004a0767a52d"> More than 500</option>
    <option value="233a2783-db42-4fdb-b191-0f97d2d9fd43"> Between 250 and 500</option>
    <option value="2bab0669-550c-4555-ae9f-1fdafdb872e5"> Between 100 and 250</option>
    <option value="d471e43b-196c-46e0-9b32-21e24e5469b4"> Between 50 and 100</option>
    <option value="ccdad63f-69be-449f-8b2c-25f844dd19c1"> Between 20 and 50</option>
    <option value="e00637a2-e3e8-4883-9e11-94e58af6e4b7" selected> Less then 20</option>
</select>

app.directive('ngInitial', function () {
return {
    restrict: 'A',
    controller: ['$scope', '$element', '$attrs', '$parse', function ($scope, $element, $attrs, $parse) {
        val = $attrs.sbInitial || $attrs.value || $element.val() || $element.text()
        getter = $parse($attrs.ngModel)
        setter = getter.assign
        setter($scope, val)
    }]
}

});


0

のようmypage/idにURLにinit値がある場合は、角度のあるJSのコントローラーでlocation.pathnameIDを検索して、必要なモデルに割り当てることができます。

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