การทำงานกับการเลือกโดยใช้ ng-options ของ AngularJS


442

ฉันได้อ่านเกี่ยวกับเรื่องนี้ในโพสต์อื่น ๆ แต่ฉันไม่สามารถเข้าใจได้

ฉันมีชุด

$scope.items = [
   {ID: '000001', Title: 'Chicago'},
   {ID: '000002', Title: 'New York'},
   {ID: '000003', Title: 'Washington'},
];

ฉันต้องการให้มันเป็น:

<select>
  <option value="000001">Chicago</option>
  <option value="000002">New York</option>
  <option value="000003">Washington</option>
</select>

และฉันต้องการเลือกตัวเลือกด้วย ID = 000002

ฉันได้อ่านตัวเลือกแล้วลอง แต่ฉันไม่สามารถเข้าใจได้


ฉันขอแนะนำให้ใช้Select2เพราะจะช่วยคุณได้ มีแม้กระทั่งเป็นคำสั่งสำหรับ AngularJS
ศิลา


ที่จริงมีวิธีการแก้ปัญหา AngularJS บริสุทธิ์พัฒนาโดยQuantumUI คุณสามารถค้นหาตัวอย่างเพิ่มเติมและเอกสารที่http://quantumui.org/
Ramon Drunce

คำตอบ:


799

สิ่งหนึ่งที่ควรทราบคือจำเป็นต้องใช้ ngModel เพื่อให้ ngOptions ทำงาน ... โปรดสังเกตสิ่งng-model="blah"ที่กำลังพูดว่า "set $ scope.blah เป็นค่าที่เลือก"

ลองสิ่งนี้:

<select ng-model="blah" ng-options="item.ID as item.Title for item in items"></select>

นี่คือเพิ่มเติมจากเอกสารของ AngularJS (ถ้าคุณยังไม่เห็น):

สำหรับแหล่งข้อมูลอาเรย์:

  • ป้ายชื่อสำหรับค่าในอาร์เรย์
  • เลือกเป็นป้ายกำกับสำหรับค่าในอาร์เรย์
  • กลุ่มป้ายกำกับตามกลุ่มสำหรับค่าในอาร์เรย์ = เลือกเป็นกลุ่มป้ายกำกับตามกลุ่มสำหรับค่าในอาร์เรย์

สำหรับแหล่งข้อมูลวัตถุ:

  • ป้ายกำกับสำหรับ (คีย์ค่า) ในวัตถุ
  • เลือกเป็นป้ายกำกับสำหรับ (คีย์ค่า) ในวัตถุ
  • กลุ่มป้ายกำกับตามกลุ่มสำหรับ (คีย์ค่า) ในวัตถุ
  • เลือกเป็นกลุ่มป้ายกำกับตามกลุ่มสำหรับ (คีย์ค่า) ในวัตถุ

สำหรับการชี้แจงบางอย่างเกี่ยวกับค่าแท็กตัวเลือกใน AngularJS:

เมื่อคุณใช้ng-options, ค่าของแท็กตัวเลือกที่เขียนออกมาโดย ng ตัวเลือกจะเป็นดัชนีของรายการอาร์เรย์แท็กตัวเลือกที่เกี่ยวข้องกับ นี่เป็นเพราะ AngularJS ช่วยให้คุณสามารถเลือกวัตถุทั้งหมดด้วยตัวควบคุมแบบเลือกไม่ใช่เฉพาะชนิดดั้งเดิม ตัวอย่างเช่น:

app.controller('MainCtrl', function($scope) {
   $scope.items = [
     { id: 1, name: 'foo' },
     { id: 2, name: 'bar' },
     { id: 3, name: 'blah' }
   ];
});
<div ng-controller="MainCtrl">
   <select ng-model="selectedItem" ng-options="item as item.name for item in items"></select>
   <pre>{{selectedItem | json}}</pre>
</div>

ด้านบนจะช่วยให้คุณสามารถเลือกวัตถุทั้งหมดเป็น$scope.selectedItemโดยตรงประเด็นคือด้วย AngularJS คุณไม่จำเป็นต้องกังวลเกี่ยวกับสิ่งที่อยู่ในแท็กตัวเลือกของคุณ ให้ AngularJS จัดการสิ่งนั้น คุณควรใส่ใจว่ามีอะไรในโมเดลของคุณในขอบเขตของคุณ

นี่คือพลั่วเกอร์ที่แสดงพฤติกรรมด้านบนและแสดง HTML ที่เขียนออกมา


การจัดการกับตัวเลือกเริ่มต้น:

มีบางสิ่งที่ฉันไม่ได้กล่าวถึงข้างต้นเกี่ยวกับตัวเลือกเริ่มต้น

การเลือกตัวเลือกแรกและลบตัวเลือกที่ว่างเปล่า:

คุณสามารถทำได้โดยการเพิ่มวิอย่างง่ายng-initที่กำหนดโมเดล (จากng-model) เป็นองค์ประกอบแรกในรายการที่คุณทำซ้ำng-options:

<select ng-init="foo = foo || items[0]" ng-model="foo" ng-options="item as item.name for item in items"></select>

หมายเหตุ: สิ่งนี้อาจจะบ้าไปหน่อยถ้าfooมีการเริ่มต้นอย่างถูกต้องกับบางสิ่งที่ "ผิดพลาด" ในกรณีนี้คุณจะต้องจัดการการเริ่มต้นfooในคอนโทรลเลอร์ของคุณส่วนใหญ่

การกำหนดตัวเลือกเริ่มต้นเอง:

มันแตกต่างกันเล็กน้อย สิ่งที่คุณต้องทำก็คือเพิ่มแท็กตัวเลือกเป็นรายการย่อยที่คุณเลือกพร้อมแอตทริบิวต์ค่าว่างจากนั้นปรับแต่งข้อความภายใน:

<select ng-model="foo" ng-options="item as item.name for item in items">
   <option value="">Nothing selected</option>
</select>

หมายเหตุ: ในกรณีนี้ตัวเลือก "ว่าง" จะยังคงอยู่แม้หลังจากที่คุณเลือกตัวเลือกอื่น นี่ไม่ใช่กรณีของพฤติกรรมเริ่มต้นของตัวเลือกภายใต้ AngularJS

ตัวเลือกเริ่มต้นที่กำหนดเองซึ่งซ่อนหลังจากทำการเลือก:

หากคุณต้องการให้ตัวเลือกเริ่มต้นที่กำหนดเองของคุณหายไปหลังจากเลือกค่าคุณสามารถเพิ่มแอตทริบิวต์ ng-hide ให้กับตัวเลือกเริ่มต้นของคุณ:

<select ng-model="foo" ng-options="item as item.name for item in items">
   <option value="" ng-if="foo">Select something to remove me.</option>
</select>

3
ปรากฎว่าสิ่งเหล่านั้นเป็นดัชนีของค่านี่คือวิธีที่เชิงมุมช่วยให้คุณสามารถใช้วัตถุเป็นค่าของกล่องที่คุณเลือก Angular กำลังทำสิ่งต่าง ๆ มากมายสำหรับคุณที่อยู่เบื้องหลังด้วยกล่องที่เลือกและคุณไม่ควรกังวลเกี่ยวกับแอตทริบิวต์ค่าในตัวเลือกของคุณ
เบ็นเลช

1
เอกสารอยู่ภายใต้ "select" บนเว็บไซต์ของพวกเขา: docs.angularjs.org/api/ng.directive:select
Ben Lesh

25
"... ngModel เป็นสิ่งจำเป็นสำหรับ ngOptions ในการทำงาน ... " เป็นปมปัญหาสำหรับฉัน คำตอบที่ดี.
tristanm

1
เป็นไปได้หรือไม่ที่จะหลีกเลี่ยงตัวเลือกแรกที่ว่างเปล่า ( <option value="?" selected="selected"></option>)?
swenedo

4
ฉันกำลังพยายามใช้ตัวพิมพ์สุดท้าย ( ตัวเลือกเริ่มต้นที่กำหนดเองซึ่งซ่อนไว้หลังจากทำการเลือก ) แต่ฉันพบปัญหาบางอย่าง แทนที่จะng-if="foo"ฉันต้องใช้ng-if=!fooเพื่อซ่อนตัวเลือกเริ่มต้นที่ว่างเปล่าเมื่อเลือกตัวเลือกอื่น นอกจากนี้ตัวเลือกที่ว่างเปล่าเริ่มต้นจะปรากฏขึ้นที่ด้านล่างของรายการคำสั่งผสมเสมอ ฉันจะวางไว้ที่จุดเริ่มต้นของรายการคำสั่งผสมได้อย่างไร
Fran Herrero

90

ฉันกำลังเรียนรู้ AngularJS และกำลังดิ้นรนกับการคัดเลือกเช่นกัน ฉันรู้ว่าคำถามนี้ได้รับคำตอบแล้ว แต่ฉันต้องการแบ่งปันรหัสเพิ่มเติมอีกบางส่วน

ในการทดสอบของฉันฉันมีสองกล่องรายการ: ยี่ห้อรถและรุ่นรถ รายการรุ่นจะถูกปิดใช้งานจนกว่าจะมีการเลือกยี่ห้อบางยี่ห้อ หากการเลือกใน listbox ทำให้ถูกรีเซ็ตในภายหลัง (ตั้งค่าเป็น 'Select Make') จากนั้น listbox รุ่นจะปิดใช้งานอีกครั้งและการเลือกจะถูกรีเซ็ตเช่นกัน (เป็น 'Select Model') ทำให้ถูกดึงมาเป็นทรัพยากรในขณะที่แบบจำลองนั้นเป็นโค้ดที่ยาก

ทำให้ JSON:

[
{"code": "0", "name": "Select Make"},
{"code": "1", "name": "Acura"},
{"code": "2", "name": "Audi"}
]

services.js:

angular.module('makeServices', ['ngResource']).
factory('Make', function($resource){
    return $resource('makes.json', {}, {
        query: {method:'GET', isArray:true}
    });
});

ไฟล์ HTML:

<div ng:controller="MakeModelCtrl">
  <div>Make</div>
  <select id="makeListBox"
      ng-model="make.selected"
      ng-options="make.code as make.name for make in makes"
      ng-change="makeChanged(make.selected)">
  </select>

  <div>Model</div>
  <select id="modelListBox"
     ng-disabled="makeNotSelected"
     ng-model="model.selected"
     ng-options="model.code as model.name for model in models">
  </select>
</div>

controllers.js:

function MakeModelCtrl($scope)
{
    $scope.makeNotSelected = true;
    $scope.make = {selected: "0"};
    $scope.makes = Make.query({}, function (makes) {
         $scope.make = {selected: makes[0].code};
    });

    $scope.makeChanged = function(selectedMakeCode) {
        $scope.makeNotSelected = !selectedMakeCode;
        if ($scope.makeNotSelected)
        {
            $scope.model = {selected: "0"};
        }
    };

    $scope.models = [
      {code:"0", name:"Select Model"},
      {code:"1", name:"Model1"},
      {code:"2", name:"Model2"}
    ];
    $scope.model = {selected: "0"};
}

27
เรียนผู้ใช้แบบสุ่ม ในขณะที่คำถามนี้และคำตอบนั้นค่อนข้างง่ายและไม่ซับซ้อนองค์กรของรหัสของคุณลงในตำแหน่งที่เหมาะสมและเหมาะสม (ตัวควบคุมบริการแม่แบบข้อมูล) แสดงความสง่างามของ AngularJS ในรูปแบบที่ง่ายที่สุดและเป็นค่าเริ่มต้น ตัวอย่างที่ดีเลิศ
Atticus

1
นี่ไม่ใช่วิธีการใช้งาน ng-modelควรชี้ไปที่อีกmakeตัวแปรกับขอบเขตของคุณไม่เกี่ยวข้องกับ ดูตัวอย่างในdocs.angularjs.org/api/ng/directive/ngOptions
Dmitri Zaitsev

@DmitriZaitsev ฉันคิดว่าคุณผิดเพราะตัวอย่างเอกสารเชิงมุมแสดงวิธีที่คุณอธิบายไม่ได้หมายความว่าเป็นวิธีเดียว แสดงให้เราเห็นว่าทำไมคุณถึงคิดว่าไม่ควรใช้วิธีนี้แทนที่จะเป็นตัวอย่างที่ดีสำหรับมือใหม่
JRT

39

ด้วยเหตุผลบางอย่าง AngularJS ทำให้ฉันสับสน เอกสารของพวกเขาค่อนข้างน่ากลัวในเรื่องนี้ ตัวอย่างที่ดีของรูปแบบที่หลากหลายจะได้รับการต้อนรับ

อย่างไรก็ตามฉันมีการเปลี่ยนแปลงเล็กน้อยในคำตอบของ Ben Lesh

การรวบรวมข้อมูลของฉันมีลักษณะเช่นนี้:

items =
[
   { key:"AD",value:"Andorra" }
,  { key:"AI",value:"Anguilla" }
,  { key:"AO",value:"Angola" }
 ...etc..
]

ตอนนี้

<select ng-model="countries" ng-options="item.key as item.value for item in items"></select>

ยังคงส่งผลให้ค่าตัวเลือกที่จะเป็นดัชนี (0, 1, 2, ฯลฯ )

เพิ่มการติดตามโดยแก้ไขให้ฉัน:

<select ng-model="blah" ng-options="item.value for item in items track by item.key"></select>

ฉันคิดว่ามันเกิดขึ้นบ่อยครั้งที่คุณต้องการเพิ่มอาร์เรย์ของวัตถุลงในรายการที่เลือกดังนั้นฉันจะจำอันนี้ได้!

โปรดทราบว่าจาก AngularJS 1.4 คุณไม่สามารถใช้ตัวเลือก ng ได้อีก แต่คุณต้องใช้ng-repeatกับแท็กตัวเลือกของคุณ:

<select name="test">
   <option ng-repeat="item in items" value="{{item.key}}">{{item.value}}</option>
</select>

3
คำตอบนี้ดูเหมือนจะเป็นสิ่งที่คำถามถาม คำตอบอื่น ๆ บอกผู้อ่านว่าไม่ต้องกังวลว่า HTML จะถูกสร้างขึ้นมาอย่างไร แต่ถ้าองค์ประกอบที่เลือกอยู่ในรูปแบบฉันก็กังวลมาก คำตอบที่ดี!
Keith

3
มันควรจะชี้ให้เห็นว่าเรื่องนี้เก็บวัตถุที่เลือกเต็มรูปแบบในรูปแบบไม่เพียง แต่ที่สำคัญ หากคุณต้องการเพียงคีย์ในโมเดลคุณต้องการใช้ "item.key เป็น item.value" สิ่งนี้ทำให้ฉันสับสนในขณะที่ฉันวางสายในสิ่งที่ HTML ดูเหมือนไม่ใช่ข้อมูลที่ฉันต้องการในแบบจำลองซึ่งเป็นสิ่งสำคัญสำหรับฉัน
mhenry1384

@ mhenry1384 ใช่คุณพูดถูกเกี่ยวกับการเก็บวัตถุทั้งหมด ฉันชอบคุณลักษณะนี้เพราะให้คุณเข้าถึงมากกว่า ID (ถ้าคุณต้องการ) ทำงานได้ดีสำหรับฉันเมื่อฉันเติมรายการที่มีคอลเลกชัน mongo และฉันต้องการคุณสมบัติบางอย่างจากรายการที่เลือก
Mattijs

2
และเราจะตรวจจับการเปลี่ยนแปลงด้วย "ng-change" ภายในได้optionอย่างไร?
Konstantinos Natsios

2
เกี่ยวกับการอัปเดตสิ่งนี้ไม่ถูกต้อง ng-options ยังคงใช้งานได้ดีใน 1.5 มันยังแสดงอยู่ในเอกสาร docs.angularjs.org/api/ng/directive/select
Jeremy A. West

15

คำถามได้ตอบแล้ว (BTW คำตอบที่ดีและครอบคลุมโดย Ben) แต่ฉันต้องการเพิ่มองค์ประกอบอื่นเพื่อความสมบูรณ์ซึ่งอาจมีประโยชน์มาก

ในตัวอย่างที่เบ็นแนะนำ:

<select ng-model="blah" ng-options="item.ID as item.Title for item in items"></select>

มีการใช้แบบฟอร์มngOptionsต่อไปนี้:select as label for value in array .

ป้ายกำกับคือนิพจน์ซึ่งผลลัพธ์จะเป็นป้ายกำกับสำหรับ<option>องค์ประกอบ ในกรณีนั้นคุณสามารถทำการต่อสตริงได้เพื่อให้มีเลเบลตัวเลือกที่ซับซ้อนมากขึ้น

ตัวอย่าง:

  • ng-options="item.ID as item.Title + ' - ' + item.ID for item in items" ให้ป้ายกำกับเช่น Title - ID
  • ng-options="item.ID as item.Title + ' (' + item.Title.length + ')' for item in items"จะช่วยให้คุณเช่นป้ายTitle (X)ที่Xมีความยาวของสตริงชื่อเรื่อง

คุณยังสามารถใช้ตัวกรองได้เช่น

  • ng-options="item.ID as item.Title + ' (' + (item.Title | uppercase) + ')' for item in items"ให้ป้ายกำกับที่คุณต้องการTitle (TITLE)โดยที่ค่าหัวเรื่องของคุณสมบัติหัวเรื่องและ TITLE เป็นค่าเดียวกัน แต่แปลงเป็นอักขระตัวพิมพ์ใหญ่
  • ng-options="item.ID as item.Title + ' (' + (item.SomeDate | date) + ')' for item in items"ให้ป้ายกำกับเช่นTitle (27 Sep 2015)ถ้าโมเดลของคุณมีคุณสมบัติSomeDate

7

ใน CoffeeScript:

#directive
app.directive('select2', ->
    templateUrl: 'partials/select.html'
    restrict: 'E'
    transclude: 1
    replace: 1
    scope:
        options: '='
        model: '='
    link: (scope, el, atr)->
        el.bind 'change', ->
            console.log this.value
            scope.model = parseInt(this.value)
            console.log scope
            scope.$apply()
)
<!-- HTML partial -->
<select>
  <option ng-repeat='o in options'
          value='{{$index}}' ng-bind='o'></option>
</select>

<!-- HTML usage -->
<select2 options='mnuOffline' model='offlinePage.toggle' ></select2>

<!-- Conclusion -->
<p>Sometimes it's much easier to create your own directive...</p>

1
อย่าลืมคุณ radixสำหรับparseInt: http://davidwalsh.name/parseint-radix
GFoley83

6

หากคุณต้องการชื่อที่กำหนดเองสำหรับแต่ละตัวเลือกng-optionsจะใช้ไม่ได้ ใช้ng-repeatกับตัวเลือกแทน:

<select ng-model="myVariable">
  <option ng-repeat="item in items"
          value="{{item.ID}}"
          title="Custom title: {{item.Title}} [{{item.ID}}]">
       {{item.Title}}
  </option>
</select>

3

ฉันหวังว่าสิ่งต่อไปนี้จะได้ผลสำหรับคุณ

<select class="form-control"
        ng-model="selectedOption"
        ng-options="option.name + ' (' + (option.price | currency:'USD$') + ')' for option in options">
</select>

1

มันจะมีประโยชน์ การผูกไม่ได้ผลเสมอไป

<select id="product" class="form-control" name="product" required
        ng-model="issue.productId"
        ng-change="getProductVersions()"
        ng-options="p.id as p.shortName for p in products"></select>

ตัวอย่างเช่นคุณเติมโมเดลแหล่งรายการตัวเลือกจากบริการ REST ทราบค่าที่เลือกก่อนกรอกรายการและตั้งค่าแล้ว หลังจากดำเนินการร้องขอ REST ด้วย $ http ตัวเลือกรายการจะทำ

แต่ตัวเลือกที่เลือกไม่ได้ตั้งไว้ ด้วยเหตุผลที่ไม่ทราบสาเหตุ AngularJS ในเงาการดำเนินการย่อยของ $ $ ไม่ได้เลือกสิ่งที่มันควรจะเป็น ฉันต้องใช้ jQuery เพื่อตั้งค่าที่เลือก มันสำคัญนะ! AngularJS ในเงาเพิ่มส่วนนำหน้าให้กับค่าของ attr "value" สำหรับสร้างโดยตัวเลือก ng-repeat สำหรับ int มันคือ "number:"

$scope.issue.productId = productId;
function activate() {
    $http.get('/product/list')
       .then(function (response) {
           $scope.products = response.data;

           if (productId) {
               console.log("" + $("#product option").length);//for clarity
               $timeout(function () {
                   console.log("" + $("#product option").length);//for clarity
                   $('#product').val('number:'+productId);

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