Angular js init ng-model từ các giá trị mặc định


126

Giả sử bạn có một biểu mẫu có các giá trị được tải từ cơ sở dữ liệu. Làm thế nào để bạn khởi tạo ng-model?

Thí dụ:

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

Trong bộ điều khiển của tôi, $ scope.card ban đầu không được xác định. Có cách nào ngoài việc làm một cái gì đó như thế này?

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

Câu trả lời:


136

Đây là một lỗi phổ biến trong các ứng dụng Angular mới. Bạn không muốn viết các giá trị của mình vào HTML trên máy chủ nếu bạn có thể tránh nó. Nếu thực tế, nếu bạn có thể thoát khỏi việc máy chủ của bạn hiển thị HTML hoàn toàn, thì tốt hơn hết.

Lý tưởng nhất là bạn muốn gửi các mẫu HTML Angular của mình, sau đó kéo xuống các giá trị của bạn thông qua $ http trong JSON và đặt chúng trong phạm vi của bạn.

Vì vậy, nếu có thể, hãy làm điều này:

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

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

Nếu bạn hoàn toàn PHẢI kết xuất các giá trị của mình vào HTML từ máy chủ của mình, bạn có thể đặt chúng vào một biến toàn cục và truy cập chúng bằng cửa sổ $:

Trong tiêu đề của trang bạn sẽ viết ra:

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

Và sau đó trong bộ điều khiển của bạn, bạn sẽ có được nó như vậy:

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

Tôi hy vọng điều đó sẽ giúp.


4
Vâng, nó giúp. Tôi đoán tôi chỉ sốc khi các anh chàng Angular đưa ra quyết định này.
Calvin Froedge

125
Đừng sốc ... Việc hiển thị HTML đang chuyển khỏi máy chủ và trình duyệt. Có hàng tá khung MVC trong JavaScript hiện nay và máy chủ chỉ hiệu quả hơn khi lưu trữ dữ liệu JSON / XML cho các ứng dụng JavaScript so với hiển thị mỗi trang trên máy chủ. Nó bù đắp rất nhiều công việc cho máy của khách hàng hơn là khiến máy chủ bị tấn công. Chưa kể nó tiết kiệm băng thông. Để hoàn thiện tất cả, bạn có thể có một ứng dụng di động gốc (hoặc bất cứ thứ gì thực sự) tiêu thụ cùng một JSON qua HTTP. Đây là tương lai.
Ben Lesh

3
@bledh: Câu trả lời tuyệt vời. Cảm ơn rất nhiều. Tôi đồng ý rằng đây là con đường phía trước và đã bắt đầu áp dụng phương pháp này cho mình; Bạn có bất kỳ liên kết hỗ trợ yêu cầu này? Tôi cũng lo lắng rằng việc di chuyển kết xuất html từ máy chủ sang máy khách có thể khiến thời gian tải trang chậm hơn, đặc biệt là trong các thiết bị di động nơi bạn có thể hiển thị rất nhiều HTML, ví dụ như cho cây điều hướng.
GFoley83

19
Tôi không đồng ý rằng đây là một 'sai lầm.' Trong một số trường hợp, kết xuất phía máy khách là giải pháp tốt nhất. Trong những người khác, kết xuất phía máy chủ là cách để đi. Bạn có thể sử dụng góc với cả hai kỹ thuật và kết xuất phía máy khách không nên được coi là "cách góc", bởi vì không phải vậy. Angular linh hoạt hơn thế.
hunterloftis

8
@bledh, chắc chắn - bất kỳ trường hợp nào SEO quan trọng nhưng chi phí nội dung thay thế cho trình thu thập thông tin cao hơn chi phí triển khai góc với dữ liệu nhúng - bất kỳ ứng dụng nào đặt giá trị rất cao về tốc độ nhận biết hoặc thời gian kết xuất cho trải nghiệm người dùng - nhiều trang web nội dung và trang web thương mại điện tử sẽ thuộc một trong những danh mục này. Các kỹ sư tại twitter, người đã thử kết xuất máy khách sau đó chuyển về máy chủ để cung cấp trải nghiệm người dùng tốt hơn, cũng đã phác thảo lý do của họ: blog.twitter.com/2012/improving-performance-twittercom
hunterloftis

236

Nếu bạn không thể làm lại ứng dụng của mình để thực hiện những gì @bledh đề xuất (kéo dữ liệu JSON xuống với $ http hoặc $ resource và điền vào $ scope), bạn có thể sử dụng ng-init thay thế:

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

Xem thêm AngularJS - Thuộc tính giá trị trên hộp văn bản đầu vào bị bỏ qua khi có mô hình ng được sử dụng?


+1 cho cách góc (của thực tiễn ít được ưa thích). Ví dụ: jsfiddle.net/9ymB3
John Lehmann

2
Tôi đang sử dụng Angular với các mẫu web C # và tôi thấy rằng việc sử dụng ng-initkhá hữu ích khi đặt giá trị từ mã phía sau / postback Eg <input name="phone" data-ng-model="frm.phone" data-ng-init="frm.phone= '<%: Model.Phone %>'" data-ng-pattern="/^[0-9() \-+]+$/" type="tel" required />. Bit xấu xí? Có, nhưng thủ thuật & giải quyết vấn đề đau đầu tích hợp trong quy trình.
GFoley83

13
+1, thanh lịch! Mọi người ủng hộ hành vi nhanh nhẹn và nhanh chóng, nhưng rất nhiều người ở đây nói rằng "hãy làm tất cả phía khách hàng.", Giống như hàng triệu cuộc gọi http là tốt cho các trang web linh hoạt. Ghép bên máy chủ dữ liệu vào các mẫu cho phép các chiến lược lưu trữ. Tĩnh, hoặc động / một phần với Memcached. Đây là những gì twitter làm. Đôi khi nó hữu ích, những lần khác thì không. Muốn căng thẳng mà. Chỉ cần thêm nó, không phải là bạn nói khác, @Mark.
oma

1
Trên thực tế tôi lấy lại điều đó, điều này hoạt động tốt nhất với tôi, tuyệt vời: stackoverflow.com/a/20522955/329367
Darren

1
FWIW: Tôi không thích gán biến trong các biểu thức mẫu vì nó không thể kiểm tra được.
Ben Lesh

60

Đây rõ ràng là một thiếu sót, nhưng dễ dàng thêm sửa chữa cho AngularJS. Chỉ cần viết một lệnh nhanh để đặt giá trị mô hình từ trường đầu vào.

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

Đây là phiên bản của tôi:

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);
      }
    ]
  };
});

Chỉ thị nhanh đẹp. Bất kỳ lý do chính đáng nào để không đóng gói này vào ng-model để giá trị = và ng-model = có thể hoạt động cùng nhau như hầu hết mọi người mong đợi?
hunterloftis

Khá đẹp! Điều này rất hữu ích.
santiagobasulto

3
Câu trả lời chính xác! Tôi vừa thêm một "bản sửa lỗi" để làm cho nó cũng hoạt động với textareas - gist.github.com/rmonteimery429/6191275
Ryan Montgomery

tại sao bạn xác định controllercho chỉ thị của mình và tại sao không sử dụng linkphương pháp này?. Tôi là người mới chơi angularjs
ebram khalil

1
cần thiết để thay đổi để val = $attrs.ngInitial || $attrs.value || $element.val()có nó hoạt động với các yếu tố được chọn.
fjsj

12

IMHO giải pháp tốt nhất là chỉ thị @Kevin Stone, nhưng tôi đã phải nâng cấp nó để hoạt động trong mọi điều kiện (chọn lọc, textarea), và điều này chắc chắn sẽ hoạt động:

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

Và những gì về lựa chọn?
jwize

7

Bạn có thể sử dụng một chỉ thị tùy chỉnh (có hỗ trợ cho textarea, chọn, radio và hộp kiểm), xem bài đăng trên blog này https://glaucocustodio.github.io/2014/10/20/init-ng-model-from-form- các thuộc tính trường / .


1
Đây là một bài viết tuyệt vời. Câu trả lời yêu thích của tôi vì câu hỏi là đặt giá trị của đầu vào theo các giá trị ban đầu.
RPDeshaies

Tôi đã tạo một Plnkr để kiểm tra mã này và nó hoạt động rất tốt: plnkr.co/edit/ZTFOAc2ZGIZr6HjsB5gH?p=preview
RPDeshaies 2/12/14

6

Bạn cũng có thể sử dụng trong mã HTML của mình: ng-init="card.description = 12345"

Nó không được đề xuất bởi Angular, và như đã đề cập ở trên, bạn nên sử dụng độc quyền bộ điều khiển của mình.

Nhưng nó đã có tác dụng :)


4

Tôi có một cách tiếp cận đơn giản, bởi vì tôi có một số xác nhận và mặt nạ nặng trong các hình thức của tôi. Vì vậy, tôi đã sử dụng jquery để lấy lại giá trị của mình và kích hoạt sự kiện "thay đổi" thành xác nhận:

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

3

Như những người khác đã chỉ ra, việc khởi tạo dữ liệu trên các khung nhìn là không tốt. Tuy nhiên, nên khởi tạo dữ liệu trên Bộ điều khiển. (xem http://docs.angularjs.org/guide/controll )

Vì vậy bạn có thể viết

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

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

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

Bằng cách này, các khung nhìn không chứa dữ liệu và bộ điều khiển khởi tạo giá trị trong khi các giá trị thực đang được tải.


3

Nếu bạn thích cách tiếp cận của Kevin Stone ở trên https://stackoverflow.com/a/17823590/584761 hãy xem xét một cách tiếp cận dễ dàng hơn bằng cách viết các chỉ thị cho các thẻ cụ thể như 'đầu vào'.

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

Nếu bạn đi theo con đường này, bạn sẽ không phải lo lắng về việc thêm ng-ban đầu vào mỗi thẻ. Nó tự động đặt giá trị của mô hình thành thuộc tính giá trị của thẻ. Nếu bạn không đặt thuộc tính value, nó sẽ mặc định là một chuỗi rỗng.


3

Đây là một cách tiếp cận tập trung vào máy chủ:

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

Đó là ý tưởng cơ bản giống như các câu trả lời phổ biến hơn cho câu hỏi này, ngoại trừ $ cung cấp. Giá trị cấp. Giá trị đăng ký một dịch vụ có chứa các giá trị mặc định của bạn.

Vì vậy, trên máy chủ, bạn có thể có một cái gì đó như:

{
    description: "Visa-4242"
}

Và đưa nó vào trang của bạn thông qua công nghệ phía máy chủ mà bạn chọn. Đây là một ý chính: https://gist.github.com/exclsr/c8c391d16319b2d31a43


1
Theo như tôi có thể nói, đây là phương pháp Angular nhất để xử lý vấn đề nếu việc thực hiện một yêu cầu $ http ban đầu không phải là một lựa chọn. Nó cũng có lợi ích là dễ dàng sửa đổi hơn nhiều nếu bạn muốn đổi sang $ http sau này. Một cải tiến có thể là việc trả lại một lời hứa thay vì làm cho việc thay đổi trở nên dễ dàng hơn. Tôi sẽ rất ngại khi thiết lập các vars toàn cầu hoặc sử dụng ng-init ở đây.
Richard

1

Đây là phiên bản chung hơn của các ý tưởng được đề cập ở trên ... Nó chỉ đơn giản kiểm tra xem có bất kỳ giá trị nào trong mô hình hay không, và nếu không, nó sẽ đặt giá trị cho mô hình.

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 (ví dụ sử dụng):

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

Điều này làm việc cho tôi, nhưng chỉ sau khi chuyển $scopeđến cuộc gọi đến getter()- hy vọng điều này sẽ làm sáng tỏ mọi thứ cho bất cứ ai khác đang thử điều này!
zesda

0

Tôi đã thử những gì @Mark Rajcok gợi ý. Nó hoạt động cho các giá trị Chuỗi (Visa-4242). Vui lòng tham khảo fiddle này .

Từ câu đố:

Điều tương tự được thực hiện trong fiddle có thể được thực hiện bằng cách sử dụng ng-repeat, mà mọi người có thể đề nghị. Nhưng sau khi đọc câu trả lời được đưa ra bởi @Mark Rajcok, tôi chỉ muốn thử tương tự cho một hình thức với một loạt các hồ sơ. Mọi thứ hoạt động tốt cho đến khi tôi có $ scope.profiles = [{}, {}]; mã trong bộ điều khiển. Nếu tôi loại bỏ mã này, tôi nhận được lỗi. Nhưng trong các kịch bản bình thường, tôi không thể in $scope.profiles = [{},{}]; khi tôi in hoặc lặp lại html từ máy chủ. Liệu có thể thực hiện các thao tác trên, theo cách tương tự như @Mark Rajcok đã làm cho các giá trị chuỗi như thế nào <input name="card[description]" ng-model="card.description" ng-init="card.description='Visa-4242'">, mà không phải lặp lại phần JavaScript từ máy chủ.


1
Bạn có thể sử dụng ng-init để khởi tạo mảng và sau đó sử dụng ng-repeat để xuất các dòng biểu mẫu: jsfiddle.net/6tP6x/1
Karen Zilles 22/03/13

0

Chỉ cần thêm hỗ trợ cho phần tử được chọn để Ryan Montgomery "sửa"

<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

Nếu bạn có giá trị init trong URL như thế mypage/id, thì trong trình điều khiển của JS góc, bạn có thể sử dụng location.pathnameđể tìm id và gán nó cho mô hình bạn muốn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.