Кутова js init ng-модель від значень за замовчуванням


126

Скажімо, у вас форма, яка має значення, завантажені з бази даних. Як ви ініціалізуєте ng-модель?

Приклад:

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

У моєму контролері $ range.card спочатку не визначено. Чи є спосіб окрім зробити щось подібне?

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

Відповіді:


136

Це поширена помилка в нових програмах Angular. Ви не хочете записувати свої значення у свій HTML на сервері, якщо зможете цього уникнути. Якщо насправді, якщо ви можете піти від того, щоб ваш сервер повністю виводив HTML, тим краще.

В ідеалі ви хочете надіслати свої кутові шаблони HTML, а потім перенести свої значення через $ http у JSON та помістити їх у свій обсяг.

Тож якщо це можливо, зробіть це:

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
Так, це допомагає. Напевно, я просто шокований тим, що кутові хлопці прийняли це рішення.
Кальвін Фродж

125
Не шокуйтесь ... Візуалізація HTML відходить від серверів та браузера. У JavaScript в наші дні існує десятки фреймів MVC, і для сервера набагато ефективніше просто розміщувати дані JSON / XML в програмах JavaScript, ніж робити кожну сторінку на сервері. Це компенсує багато роботи на машині клієнта, а не на тому, щоб сервер прийняв удар. Не кажучи вже про це економить пропускну здатність. На додаток до цього, у вас може бути власний мобільний додаток (або що-небудь справді), яке використовує той же JSON через HTTP. Це майбутнє.
Бен Леш

3
@blesh: Чудова відповідь. Дуже дякую. Я погоджуюсь, що це шлях вперед, і я сам почав застосовувати цей підхід; чи є у вас посилання, які підтримують цю претензію? Мене просто хвилює, що переміщення візуалізації html з сервера на клієнта може призвести до уповільнення часу завантаження сторінки, особливо на мобільних пристроях, де ви можете надати багато HTML, наприклад, для дерева навігації.
GFoley83

19
Я не згоден, що це "помилка". У деяких випадках найкраще рішення - надання на стороні клієнта. В інших спосіб подання на стороні сервера - це шлях. Ви можете використовувати кутовий з обома методами, і відображення на стороні клієнта не повинно сприйматися як "кутовий спосіб", оскільки це не так. Кутова більш гнучка від цього.
hunterloftis

8
@blesh, звичайно - будь-який випадок, коли SEO важливий, але вартість альтернативного контенту для сканерів вище, ніж вартість впровадження кутових із вбудованими даними - будь-яка програма, яка надає дуже високе значення сприйнятої швидкості чи часу для візуалізації для досвід користувачів - багато контентних сайтів та сайтів електронної комерції потрапляють до однієї з цих категорій. Інженери з twitter, які намагалися рендерінг клієнтів потім переїхали назад на сервер, щоб забезпечити кращу роботу користувачів, також виклали свої міркування: blog.twitter.com/2012/improving-performance-twittercom
hunterloftis

236

Якщо ви не можете переробити додаток, щоб робити те, що пропонує @blesh (потягніть дані JSON вниз за допомогою $ http або $ resource та заповніть $ range), замість цього можна використовувати ng-init :

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

Див. Також AngularJS - атрибут значення у вхідному текстовому полі ігнорується, коли використовується ng-модель?


+1 для кутового способу (менш бажана практика). Приклад: jsfiddle.net/9ymB3
Джон Леманн

2
Я використовую Angular із веб-формами C #, і я вважаю, що використання 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

13
+1, елегантно! Люди виступають за спритну та швидку поведінку, але так багато тут, у програмі SO, говорять: "зробіть це з усієї сторони клієнта", як мільйони http-дзвінків добре для швидких сайтів. Посів сторони сервера даних у шаблони дозволяє кешувати стратегії кешування. Статичний або динамічний / частковий з Memcached. Це те, що робить щебетання. Іноді це корисно, інше - ні. Хотів наголосити на цьому. Просто довелося додати це, а не те, що ви сказали інакше, @Mark.
ома

1
Насправді я повертаю це назад, це найкраще працює для мене, дивовижно: stackoverflow.com/a/20522955/329367
Даррен

1
FWIW: мені не подобається призначення змінних у виразах шаблонів, оскільки це не перевіряється.
Бен Леш

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-модель, щоб value = і ng-model = могли працювати разом, як очікували більшість людей?
hunterloftis

Дуже приємно! Це корисно.
santiagobasulto

3
Чудова відповідь! Я щойно додав "виправлення", щоб з цим також працювати з textareas - gist.github.com/rmontgomery429/6191275
Ryan Montgomery

чому ви визначилися controllerдля своєї директиви, а чому не використали linkметод ?. Я новачок у angularjs
ebram khalil

1
потрібно змінити на, val = $attrs.ngInitial || $attrs.value || $element.val()щоб він працював з обраними елементами.
fjsj

12

IMHO найкращим рішенням є директива @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);
                    }
                }
            }
        }
    });

А як щодо вибору?
jwize

7

Ви можете скористатись спеціальною директивою (із підтримкою textarea, select, radio і checkbox), ознайомтесь із цим дописом у блозі https://glaucocustodio.github.io/2014/10/20/init-ng-model-from-form- поля-атрибути / .


1
Це чудовий пост. Моєю улюбленою відповіддю, оскільки питання було встановити значення вхідного сигналу відповідно до початкових значень.
RPDeshaies

Я створив Plnkr для тестування цього коду, і він прекрасно працює: plnkr.co/edit/ZTFOAc2ZGIZr6HjsB5gH?p=preview
RPDeshaies

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, розгляньте простіший підхід, написавши директиви для конкретних тегів, таких як "введення".

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 до кожного тегу. Він автоматично встановлює значення моделі атрибуту значення тегу. Якщо ви не встановите атрибут значення, він буде за замовчуванням порожнім рядком.


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 не є варіантом. Це також має перевагу набагато простіше змінити, якщо ви хочете пізніше перейти на $ http. Одне можливе покращення - повернення обіцянки замість того, щоб зробити перехід ще простішим. Я б дуже ухилявся від встановлення глобальних змін або використання тут ng-Initial.
Річард

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

0

Я спробував те, що запропонував @Mark Rajcok. Його працює для значень String (Visa-4242). Будь ласка, зверніться до цієї загадки .

З загадки:

Те саме, що робиться у скрипці, можна зробити, використовуючи ng-repeat, що кожен міг порекомендувати. Але прочитавши відповідь, надану @Mark Rajcok, я просто захотів спробувати те саме для форми з масивом профілів. Речі працюють добре до тих пір, коли у мене є $ range.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-повтор для виведення рядків форми: jsfiddle.net/6tP6x/1
Karen Zilles

0

Щойно додана підтримка вибраного елемента до "виправлення" Райана Монтгомері

<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

Якщо у вас є значення init у такій URL-адресі mypage/id, то в контролері кутового JS ви можете location.pathnameзнайти ідентифікатор та призначити його потрібній моделі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.