Angular js init ng-model จากค่าดีฟอลต์


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 ของคุณจากนั้นดึงค่าของคุณผ่าน $ 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
เออมันช่วยได้ ฉันเดาว่าฉันแค่ตกใจที่พวก Angular ตัดสินใจแบบนี้
Calvin Froedge

125
อย่าเพิ่งตกใจ ... การแสดงผลของ HTML กำลังย้ายออกจากเซิร์ฟเวอร์และเบราว์เซอร์ ทุกวันนี้มีเฟรมเวิร์ก MVC มากมายใน JavaScript และมีประสิทธิภาพมากกว่าสำหรับเซิร์ฟเวอร์ในการโฮสต์ข้อมูล JSON / XML ไปยังแอป JavaScript มากกว่าที่จะแสดงผลทุกๆหน้าบนเซิร์ฟเวอร์ มันชดเชยงานจำนวนมากไปยังเครื่องของลูกค้าแทนที่จะให้เซิร์ฟเวอร์เข้าโจมตี ไม่ต้องพูดถึงมันประหยัดแบนด์วิดท์ เหนือสิ่งอื่นใดคุณสามารถมีแอปมือถือแบบเนทีฟ (หรืออะไรก็ได้) ที่ใช้ JSON เดียวกันบน HTTP นี่คืออนาคต
Ben Lesh

3
@blesh: คำตอบที่ดี ขอบคุณมาก. ฉันยอมรับว่านี่คือหนทางข้างหน้าและได้เริ่มใช้แนวทางนี้ด้วยตัวเอง คุณมีลิงก์ที่สนับสนุนการอ้างสิทธิ์นี้หรือไม่ ฉันแค่กังวลว่าการย้ายการแสดงผล html จากเซิร์ฟเวอร์ไปยังไคลเอนต์อาจส่งผลให้เวลาในการโหลดหน้าเว็บช้าลงโดยเฉพาะในอุปกรณ์มือถือที่คุณสามารถแสดง HTML ได้มากเช่นสำหรับแผนผังการนำทาง
GFoley83

19
ฉันไม่เห็นด้วยว่านี่เป็น 'ความผิดพลาด' ในบางกรณีการแสดงผลฝั่งไคลเอ็นต์เป็นทางออกที่ดีที่สุด ในส่วนอื่น ๆ การแสดงผลฝั่งเซิร์ฟเวอร์เป็นวิธีที่จะไป คุณสามารถใช้เชิงมุมกับทั้งสองเทคนิคได้และการแสดงผลฝั่งไคลเอ็นต์ไม่ควรถือเป็น "วิธีเชิงมุม" เนื่องจากไม่ใช่ Angular มีความยืดหยุ่นมากกว่านั้น
hunterloftis

8
@blesh แน่นอน - ทุกกรณีที่ SEO มีความสำคัญ แต่ต้นทุนของเนื้อหาทางเลือกสำหรับโปรแกรมรวบรวมข้อมูลนั้นสูงกว่าค่าใช้จ่ายในการใช้งานเชิงมุมกับข้อมูลที่ฝังตัว - แอปพลิเคชันใด ๆ ที่ให้ความสำคัญกับความเร็วในการรับรู้หรือเวลาในการแสดงผลสำหรับ ประสบการณ์ของผู้ใช้ - ไซต์เนื้อหาและไซต์อีคอมเมิร์ซจำนวนมากจะอยู่ในหมวดหมู่เหล่านี้ วิศวกรที่ twitter ที่ทดลองใช้งานไคลเอนต์แล้วย้ายกลับไปที่เซิร์ฟเวอร์เพื่อมอบประสบการณ์การใช้งานที่ดีขึ้นได้ระบุเหตุผลของพวกเขาเช่นกัน: blog.twitter.com/2012/improving-performance-twittercom
hunterloftis

236

หากคุณไม่สามารถปรับเปลี่ยนแอปของคุณให้ทำตามที่ @blesh แนะนำได้ (ดึงข้อมูล JSON ลงด้วยทรัพยากร $ http หรือ $ และเติมข้อมูล $ ขอบเขต) คุณสามารถใช้ng-initแทน:

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

ดูเพิ่มเติมAngularJS - แอตทริบิวต์ค่าบนกล่องข้อความอินพุตถูกละเว้นเมื่อมีการใช้ ng-model?


+1 สำหรับวิธีเชิงมุม (ของวิธีปฏิบัติที่ไม่ค่อยต้องการ) ตัวอย่าง: jsfiddle.net/9ymB3
John Lehmann

2
ฉันใช้เชิงมุมกับ C # รูปแบบของเว็บและฉันพบว่าการใช้ng-initเป็นประโยชน์มากเมื่อตั้งค่าจาก behind รหัส / postback <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 นี่คือสิ่งที่ twitter ทำ บางครั้งมันก็มีประโยชน์ แต่บางครั้งก็ไม่มีประโยชน์ อยากจะเครียดขนาดนั้น แค่ต้องเพิ่มไม่ใช่ว่าคุณพูดอย่างอื่น @Mark
oma

1
อันที่จริงฉันเอามันกลับมาสิ่งนี้ดีที่สุดสำหรับฉันยอดเยี่ยม: stackoverflow.com/a/20522955/329367
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 เพื่อให้ค่า = และ 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 directive แต่ฉันต้องอัปเกรดให้ทำงานในทุกเงื่อนไข (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 เลือกวิทยุและช่องทำเครื่องหมาย) ตรวจสอบโพสต์บล็อกนี้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

หากคุณชอบแนวทางของ Kevin Stone ข้างต้นhttps://stackoverflow.com/a/17823590/584761 ลอง พิจารณาแนวทางที่ง่ายกว่าโดยการเขียนคำสั่งสำหรับแท็กเฉพาะเช่น '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 ค่าเริ่มต้นจะเป็นสตริงว่าง


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>

เป็นแนวคิดพื้นฐานเช่นเดียวกับคำตอบที่เป็นที่นิยมมากกว่าสำหรับคำถามนี้ยกเว้น$ give.value จะลงทะเบียนบริการที่มีค่าเริ่มต้นของคุณ

ดังนั้นบนเซิร์ฟเวอร์คุณสามารถมีสิ่งต่างๆเช่น:

{
    description: "Visa-4242"
}

และใส่ลงในเพจของคุณผ่านทางเทคโนโลยีฝั่งเซิร์ฟเวอร์ที่คุณเลือก นี่คือส่วนสำคัญ: https://gist.github.com/exclsr/c8c391d16319b2d31a43


1
เท่าที่ฉันสามารถบอกได้นี่เป็นวิธีเชิงมุมที่ดีที่สุดในการจัดการปัญหาหากการขอ $ http เริ่มต้นไม่ใช่ตัวเลือก นอกจากนี้ยังมีประโยชน์ในการปรับเปลี่ยนได้ง่ายกว่ามากหากคุณต้องการเปลี่ยนเป็น $ http ในภายหลัง การปรับปรุงที่เป็นไปได้อย่างหนึ่งคือการคืนสัญญาแทนเพื่อให้การเปลี่ยนแปลงง่ายยิ่งขึ้น ฉันจะอายมากที่จะตั้งค่า global vars หรือใช้ ng-initial ที่นี่
richard

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 ฉันแค่อยากจะลองทำแบบเดียวกันกับรูปแบบที่มีโปรไฟล์มากมาย ทุกอย่างทำงานได้ดีจนกระทั่งฉันมี $ scope.profiles = [{}, {}]; รหัสในตัวควบคุม ถ้าฉันลบรหัสนี้ฉันจะได้รับข้อผิดพลาด แต่ในสถานการณ์ปกติฉันไม่สามารถพิมพ์$scope.profiles = [{},{}]; ขณะที่ฉันพิมพ์หรือ echo 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 Zilles

0

เพิ่งเพิ่มการสนับสนุนสำหรับองค์ประกอบที่เลือกให้ Ryan Montgomery "fix"

<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เพื่อค้นหา id และกำหนดให้กับโมเดลที่คุณต้องการ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.