ความยากของ ng-model, ng-repeat และ inputs


116

ฉันพยายามที่จะช่วยให้ผู้ใช้สามารถแก้ไขรายชื่อของรายการโดยใช้และngRepeat ngModel( ดูซอนี้) อย่างไรก็ตามทั้งสองวิธีที่ฉันได้พยายามนำไปสู่พฤติกรรมที่แปลกประหลาด: วิธีหนึ่งไม่อัปเดตโมเดลและอีกวิธีหนึ่งจะเบลอแบบฟอร์มในแต่ละคีย์ดาวน์

ฉันทำอะไรผิดที่นี่? นี่ไม่ใช่กรณีการใช้งานที่รองรับหรือไม่?

นี่คือรหัสจากซอคัดลอกเพื่อความสะดวก:

<html ng-app>
    <head>
        <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/css/bootstrap-combined.min.css" rel="stylesheet">
    </head>
    <body ng-init="names = ['Sam', 'Harry', 'Sally']">
        <h1>Fun with Fields and ngModel</h1>
        <p>names: {{names}}</p>
        <h3>Binding to each element directly:</h3>
        <div ng-repeat="name in names">
            Value: {{name}}
            <input ng-model="name">                         
        </div>
        <p class="muted">The binding does not appear to be working: the value in the model is not changed.</p>
        <h3>Indexing into the array:</h3>
        <div ng-repeat="name in names">
            Value: {{names[$index]}}
            <input ng-model="names[$index]">                         
        </div>
        <p class="muted">Type one character, and the input field loses focus. However, the binding appears to be working correctly.</p>
    </body>
</html>


1
สิ่งนี้คล้ายกับstackoverflow.com/questions/12977894/…แต่แนวทางที่ 2 เป็นวิธีใหม่ที่นี่ดังนั้นจึงไม่ซ้ำกันอย่างแน่นอน ฉันให้คำตอบโดยละเอียด (คล้ายกับของ Artem) ที่นั่นอธิบายว่าขอบเขตของเด็ก ng-repeat ทำงานอย่างไร
Mark Rajcok

เนื่องจากฉันต้องเสียค่า googling เป็นจำนวนพอสมควรก่อนที่จะพบเธรดนี้ในที่สุดฉันขอเปลี่ยนชื่อเป็น sth เช่น "Parent Model not updated from ngRepeat inputs" หรือ "Model not updated when using ngRepeat" หรือ "ngRepeat inputs not bound to (parent) จำลอง "? บางทีคุณอาจมีแนวคิดที่ดีกว่าสำหรับชื่อเรื่องนี้
vucalur

คำตอบ:


120

สิ่งนี้ดูเหมือนจะเป็นปัญหาที่ผูกมัด

คำแนะนำคืออย่าผูกมัดกับสิ่งดั้งเดิมไม่ได้ผูกกับวิทยาการ

คุณngRepeatกำลังวนซ้ำสตริงภายในคอลเลกชั่นเมื่อมันควรจะวนซ้ำบนวัตถุ เพื่อแก้ไขปัญหาของคุณ

<body ng-init="models = [{name:'Sam'},{name:'Harry'},{name:'Sally'}]">
    <h1>Fun with Fields and ngModel</h1>
    <p>names: {{models}}</p>
    <h3>Binding to each element directly:</h3>
    <div ng-repeat="model in models">
        Value: {{model.name}}
        <input ng-model="model.name">                         
    </div>

jsfiddle: http://jsfiddle.net/jaimem/rnw3u/5/


3
ขอขอบคุณสำหรับการเชื่อมโยงหลายมิติครั้งแรกเนื่องจากอธิบายปัญหาการเบลอ / สูญเสียโฟกัส / การกะพริบ ฉันสงสัยเสมอว่าทำไมถึงเกิดขึ้น
Mark Rajcok

2
ขอบคุณสำหรับสิ่งนั้นช่วยได้มาก ถึงกระนั้นการผูกมัดกับสิ่งดั้งเดิมก็ควรจะอยู่ที่นั่น ...
Daniel Gruszczyk

1
โพสต์เก่า แต่ thnx ใช้เวลาพอสมควรในการค้นหาปัญหา 'ไม่เปลี่ยนโมเดลภายใน ngRepeat' และเป็นคำแนะนำของคุณที่จะไม่ผูกติดกับแบบดั้งเดิม
Stefanos Chrs

นอกจากนี้: อย่าแก้ไขรายการทั้งหมดในขณะที่พิมพ์ซึ่งเป็นกับดักที่ฉันตกลงไป ฉันกำลังดูคอลเล็กชันสำหรับการเปลี่ยนแปลงและแทนที่ด้วยสำเนาที่เหมือนกันดังนั้นแม้ว่าฉันจะไม่ได้ผูกมัดกับดั้งเดิม แต่องค์ประกอบก็ถูกสร้างขึ้นใหม่
z0r

71

ใช้เชิงมุมรุ่นล่าสุด (1.2.1) $indexและการติดตามโดย ปัญหานี้ได้รับการแก้ไขแล้ว

http://jsfiddle.net/rnw3u/53/

<div ng-repeat="(i, name) in names track by $index">
    Value: {{name}}
    <input ng-model="names[i]">                         
</div>

ได้งาน; ไม่มีการสูญเสียข้อมูลอ้างอิงอีกต่อไป
Dan Ochiana

ฉันค้นหามานานมากแล้ว .... ง่ายมาก ... ให้สิ่งนี้เป็นคำตอบ!
Andrea

oooooh ดังนั้นการเพิ่ม "track by $ index" ใน ng-repeat จะช่วยแก้ปัญหา "ริบหรี่" ได้
เช่น

43

คุณได้รับในสถานการณ์ที่ยากลำบากเมื่อมันเป็นสิ่งจำเป็นที่จะเข้าใจว่าขอบเขต , ngRepeatและngModelกับNgModelControllerทำงาน ลองใช้เวอร์ชัน 1.0.3 ด้วย ตัวอย่างของคุณจะทำงานแตกต่างกันเล็กน้อย

คุณสามารถใช้โซลูชันที่จัดทำโดย jm-

แต่ถ้าคุณต้องการจัดการกับสถานการณ์ให้ลึกซึ้งยิ่งขึ้นคุณต้องเข้าใจ:

  • วิธี AngularJS งาน ;
  • ขอบเขตมีโครงสร้างตามลำดับชั้น
  • ngRepeat สร้างขอบเขตใหม่สำหรับทุกองค์ประกอบ
  • ngRepeat สร้างแคชของรายการที่มีข้อมูลเพิ่มเติม (hashKey); ในแต่ละสายนาฬิกาสำหรับรายการใหม่ทุก (ที่ไม่อยู่ในแคช) ngRepeat สร้างขอบเขตใหม่องค์ประกอบ DOM ฯลฯรายละเอียดเพิ่มเติม
  • จาก 1.0.3 ngModelController rerenders อินพุตด้วยค่าโมเดลจริง

ตัวอย่าง "การเชื่อมโยงกับแต่ละองค์ประกอบโดยตรง" ของคุณทำงานอย่างไรสำหรับ AngularJS 1.0.3:

  • คุณป้อนตัวอักษร'f'ลงในอินพุต
  • ngModelControllerการเปลี่ยนแปลงรูปแบบการขอบเขตรายการ (ชื่ออาร์เรย์จะไม่เปลี่ยนแปลง) => name == 'Samf', names == ['Sam', 'Harry', 'Sally'];
  • $digest เริ่มลูป
  • ngRepeatแทนที่ค่าโมเดลจากขอบเขตรายการ ( 'Samf') ตามค่าจากอาร์เรย์ชื่อที่ไม่เปลี่ยนแปลง ( 'Sam');
  • ngModelControllerป้อนข้อมูลอีกครั้งด้วยค่าโมเดลจริง ( 'Sam')

ตัวอย่าง "การจัดทำดัชนีในอาร์เรย์" ของคุณทำงานอย่างไร:

  • คุณป้อนตัวอักษร'f'ลงในอินพุต
  • ngModelControllerเปลี่ยนรายการในชื่อarray=> `ชื่อ == ['Samf', 'Harry', 'Sally'];
  • เริ่มการวนซ้ำ $ Digest;
  • ngRepeatไม่พบ'Samf'ในแคช
  • ngRepeat สร้างขอบเขตใหม่เพิ่มองค์ประกอบ div ใหม่ด้วยอินพุตใหม่ (นั่นคือสาเหตุที่ช่องอินพุตสูญเสียโฟกัส - div เก่าที่มีอินพุตเก่าจะถูกแทนที่ด้วย div ใหม่ด้วยอินพุตใหม่)
  • ค่าใหม่สำหรับองค์ประกอบ DOM ใหม่จะแสดงผล

นอกจากนี้คุณสามารถลองใช้AngularJS Batarangและดูการเปลี่ยนแปลง $ id ของขอบเขตของ div ด้วยอินพุตที่คุณป้อน


ขอบคุณสำหรับคำอธิบาย 1.0.3 เป็นที่น่าสนใจว่าใน 1.0.3 กรณี "ผูกโดยตรง" ช่องอินพุตดูเหมือนจะเสียในแง่ที่ดูเหมือนว่าจะไม่ยอมรับการเปลี่ยนแปลง / อินพุตที่พิมพ์ (อย่างน้อยใน Chrome) ฉันแน่ใจว่าเราจะเห็นโพสต์ SO เกี่ยวกับเรื่องนี้ในอนาคตอันใกล้ :) ฉันคิดว่าวิธีใหม่นี้ดีกว่าเพราะจะเห็นได้ชัดขึ้นว่ามีบางอย่างผิดปกติ
Mark Rajcok

6

หากคุณไม่ต้องการให้โมเดลอัปเดตด้วยทุกจังหวะการกดเพียงเชื่อมโยงnameและอัปเดตรายการอาร์เรย์ในเหตุการณ์เบลอ:

<div ng-repeat="name in names">
    Value: {{name}}
    <input ng-model="name" ng-blur="names[$index] = name" />
</div>

ทำไมไม่ใช้ng-model="names[$index]"... ฉันรู้ว่ามันเป็นวิธีแก้ปัญหา แต่ใช้งานได้ ;-)
DraškoKokić


2

ปัญหาน่าจะอยู่ในวิธีการที่ng-modelจะทำงานร่วมกับinputและเขียนทับวัตถุทำให้มันหายไปnameng-repeat

วิธีแก้ปัญหาหนึ่งสามารถใช้รหัสต่อไปนี้:

<div ng-repeat="name in names">
    Value: {{name}}
    <input ng-model="names[$index]">                         
</div>

หวังว่าจะช่วยได้


1

ฉันลองวิธีแก้ปัญหาข้างต้นสำหรับปัญหาของฉันแล้วมันได้ผลเหมือนมีเสน่ห์ ขอบคุณ!

http://jsfiddle.net/leighboone/wn9Ym/7/

นี่คือเวอร์ชันของฉัน:

var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
    $scope.models = [{
        name: 'Device1',
        checked: true
    }, {
        name: 'Device1',
        checked: true
    }, {
        name: 'Device1',
        checked: true
    }];

}

และ HTML ของฉัน

<div ng-app="myApp">
    <div ng-controller="MyCtrl">
         <h1>Fun with Fields and ngModel</h1>
        <p>names: {{models}}</p>
        <table class="table table-striped">
            <thead>
                <tr>
                    <th></th>
                    <th>Feature 1</td>
                    <th>Feature 2</th>
                    <th>Feature 3</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>Device</td>
                   <td ng-repeat="modelCheck in models" class=""> <span>
                                    {{modelCheck.checked}}
                                </span>

                    </td>
                </tr>
                <tr>
                    <td>
                        <label class="control-label">Which devices?</label>
                    </td>
                    <td ng-repeat="model in models">{{model.name}}
                        <input type="checkbox" class="checkbox inline" ng-model="model.checked" />
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
</div>

-5

ทำสิ่งที่ชอบ:

<select ng-model="myModel($index+1)">

และในองค์ประกอบผู้ตรวจสอบของฉันเป็น:

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