How to set the initial value of a select element using AngularJS ng-options & track by
------------------------------------------------------------------------------------------------------------------------
I have been doing Angular.JS in production projects for months, that it did surprise me recently how I haven’t used drop-downs in it. Well, I mean how I haven’t used them enough to get into several problems I had in my current project, and other friends at the same office had in their project as well.
To save you the pain I went through, I’ll list some problems and solutions here, and then give you a video that shows going through all of them ans the thought process that led to the solutions.
Initial selection
<code><select ng-model="myModel.someObject"
ng-options="someObject.text for someObject in objectList ">
</select>
</code>
Assuming someObject
in the model has the same properties and values as someObject
in the objectList
, it will still not be selected.
It’ll only be selected if someObject
was actually one of the objects in objectList
, like objectList[0]
or whatever. Otherwise, Angular.JS will insert an empty option
tag with no value or text and select that.
Root Cause
Angular.JS uses native JavaScript comparison for comparing the objects. In JavaScript, unrelated to Angular.JS or anything, comparing objects (object literals) is “by reference”, so it doesn’t factor the similarity of the objects. Only checks if the two references compared point to the same object in memory or not.
Solution
An un-documented (AFAIK) feature in ng-options
is that you can use some bits from the ng-repeat
directive with it, like track by
. This allows us to choose some property as the comparison key.
<code><select ng-model="myModel.someObject"
ng-options="someObject.text for someObject in objectList track by someObject.key">
</select>
</code>
If the key
property is a simple type, like Number
or string
, JavaScript will consider it equal to any other object that has the same value, so we don’t have to use the same objects.
Invalid Value Sent On Server-Side Submit
When Angular.JS writes the <option>
tags from an ng-options
directive pointing to an array, the value
of the option
is always the index of the element it maps to in the array. This is not important if you process the selection on client side because you only deal with the result of ng-model
anyway, you can use this later to create an AJAX request or whatever.
However, if you intend to submit the form using a normal server submission, and only use Angular.JS for say validation or managing complex form interaction (client-side tables containing sub-items with add/remove/sort for example), this may be a road blocker to using Angular.
Root Cause
By default Angular.JS uses the index of the array to track which object maps to which <option>
element.
Solution
Similar to the previous problem, use the track by
syntax. Angular.JS will use the track by
property value as the <option>
‘s value
. Most of the time your tracked property is the key property you want to send to the server anyway, so, this should be good enough.
Simple Properties Scenario When Combined With Server-Side Submit
Let’s say you want something as simple as this:
<code><select
ng-model="person.genderId"
ng-options="gender.id as gender.text for gender in genders">
</select>
</code>
This syntax will work very well, if you only use this value from JavaScript, you are all set. But if you plan to send it directly to the server (a normal non-AJAX form submit), you’ll want to consider using the track by
syntax, like track by gender.id
.
However, if you do this, you’ll notice that the select
is no longer usable. No initial selection, and changing selection although updates the model, it does not show the new selected value.
Root Cause
The track by
syntax expects an object, with the property you use to track. It does not honor the key part used in the key as text
syntax (which in our example is g.id as g.text
), so, it wants the ng-model
to point to an object with the tracked property, it cannot be the key itself directly.
Workaround
I didn’t call this a solution, because it’s pretty much a hack.
<code><select
ng-init="person._gender = {id: person.genderId}"
ng-change="person.genderId = person._gender.id"
ng-model="person._gender"
ng-options="gender.id as gender.text
for gender in genders track by gender.id">
</select>
</code>
We created a new property (which I liked to prefix with _
to show it doesn’t normally belong to the model object), initialized it to a new object that contains only our key property id
set to the original simple value genderId
, and then used that as the model (as in ng-model
).
We created and assigned the property in ng-init
, then synchronized the changes to the simple property via ng-change
. This allows the code everywhere else in the application (like the controller, or other parts of the markup) to only interact with the property we want (genderId
in this example), without knowing about our hack. This makes things a bit cleaner, although it still remains a hack rather than a solution.
You can view an example of using this hack here.
Adding extra selection items to the dropdown
One thing you notice if you are affected by the “initial selection” problem, is that the empty <option>
tag that Angular.JS adds when it can’t match the ng-model
to the array from ng-options
disappears when the use changes their selection. We have gone through how to avoid showing the empty option
by mistake already.
But if you do want to have that option, it’s easy, just, um, add it!
<code><select ng-model="myModel.someObject"
ng-options="someObject.text for someObject in
objectList track by someObject.key">
<option value="">-- Select an option--</option>
</select>
</code>
Update:
If you are using Angular 1.4+, check the much smaller 2nd part of this article, about how to use
track by
correctly.
Using track by correctly with Angular 1.4 select ng-options – Why can’t I select this option?
The Video
If you want to dig these problems really deep and see what they look like in action, and what was the thought process for solving them like and in some cases other possible solutions, I have put all this in a (rather long) video here:
How to set the initial value of a select element using AngularJS ng-options & track by的更多相关文章
- 使控件具有 Tilt 效果
步骤1:添加类: /* Copyright (c) 2010 Microsoft Corporation. All rights reserved. Use of this sample source ...
- Django之Form组件
Django之Form组件 本节内容 基本使用 form中字段和插件 自定义验证规则 动态加载数据到form中 1. 基本使用 django中的Form组件有以下几个功能: 生成HTML标签 验证用户 ...
- ABAP 订单-交货单-发货过账自动完成 案例
*&---------------------------------------------------------------------* *& Report ZSDR006 ...
- SAP 订单状态跟踪
*&--------------------------------------------------------------------- *& Program name: *& ...
- 【Django】--Form组件
Django的Form主要具有一下几大功能: 生成HTML标签 验证用户数据(显示错误信息) HTML Form提交保留上次提交数据 初始化页面显示内容 例子: 1.创建Form类 from djan ...
- ABAP 订单转交货单
*& Report ZSDR025 *& *&---------------------------------------------------------------- ...
- jqGrid配置属性说明
Property Type Description Default1) ajaxGridOptions object This option allows to set global ajax set ...
- django--forms
forms模块的功能 1 表单提交验证 2 生成HTML标签 其他 提交后保留页面数据 创建forms类 首先从django中引入forms,一般会在application中新建一个文件专门保存for ...
- javascript 手势缩放 旋转 拖动支持:hammer.js
原文: https://cdn.rawgit.com/hammerjs/hammer.js/master/tests/manual/visual.html /*! Hammer.JS - v2.0.4 ...
随机推荐
- Ubuntu超简单文书编辑器:nano
nano 的使用很简单,可以直接加上档名就能够开启一个旧档或新档! 直接在终端输入指令:nano text.txt,如下图所示打开的是已有的文档! 第一部分反白部分,是nano的版本与档名 第二部分可 ...
- luogu3370 【模板】字符串哈希
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> ...
- Leetcode 419.甲板上的战舰
甲板上的战舰 给定一个二维的甲板, 请计算其中有多少艘战舰. 战舰用 'X'表示,空位用 '.'表示. 你需要遵守以下规则: 给你一个有效的甲板,仅由战舰或者空位组成. 战舰只能水平或者垂直放置.换句 ...
- Clarke and five-pointed star
Clarke is a patient with multiple personality disorder. One day, Clarke turned into a learner of geo ...
- 【转】Linux多命令顺序执行连接符(; || && |)
当我们需要一次执行多个命令的时候,命令之间需要用连接符连接,不同的连接符有不同的效果.下面我们总结一下,加以区分. (1) ; 分号,没有任何逻辑关系的连接符.当多个命令用分号连接时,各命令之间的 ...
- Set容器——HashSet及常用API
Set容器特点: ① Set容器是一个不包含重复元素的Collection,并且最多包含一个null元素,它和List容器相反,Set容器不能保证其元素的顺序; ② 最常用的两个Set接口的实 ...
- 【Luogu】P3177树上染色(树形DP)
题目链接 题没想出来很烦+一堆细节要注意很烦. 当然更可能是我智商被osu吃了. 考虑一条边会有什么贡献?它一边的黑点数*另一边的黑点数*边权. +它一边的白点数*另一边的白点数*边权. 这样一来就成 ...
- [解决方案]Window 2008 R2 + IIS7.5 + VS2013 错误代码 0x80070002
HTTP 错误 404.0 - Not Found 您要找的资源已被删除.已更名或暂时不可用.详细错误信息模块 IIS Web Core通知 MapRequest Handler处理程序 Static ...
- debug模式总是自动跳到ThreadPoolExecutor
debug模式下eclipse总是自动跳到ThreadPoolExecutor解决方案 debug模式下eclipse总是自动跳到ThreadPoolExecutor解决方案 在eclipse中点击W ...
- Java面试题之Java中==和equals()和hashCode()的区别
“==”: ==是运算符,用来比较两个值.两个对象的内存地址是否相等: “equals()”: equals是Object类的方法,默认情况下比较两个对象是否是同一个对象,内部实现是通过“==”来实现 ...