I have a table of read-only phone numbers. A phone number has a number and a type (e.g. Mobile or Home). Each phone number has an "Edit" button that, when clicked, allows you to edit the phone number and type in a modal dialog. I'm using Knockoutjs for the read-only table and the editor. The table binds to an observableArray of PhoneVMs
and the editor works on a single PhoneVM
. Because I want the user to have to click OK on the modal before any of their changes are applied, the modal works on a copy of the selected PhoneVM
and when they click OK, it replaces the originally clicked PhoneVM
in the observableArray that the table is bound to. That's all working great.
Now I have a need to allow the first phone in the list to be edited on the same page as the read only table (without a modal). The idea is for it to be easier to enter the first phone earlier in the workflow. So you would enter your phone on the page and it would automatically appear in the read only list below where you could also edit it in the modal as normal. I thought Knockout would make this easy but I hit a snag. From this point it will be easier to just show an example of what is going wrong. Do the following in this fiddle: https://jsfiddle.net/ph4mhsof/
- Edit the phone number and tab out of the textbox. Notice the first phone in the All Phones list updates too.
- Change the Phone Type in the dropdown. Notice both the Type ID and the Type Name change appropriately in the All Phones table.
- Click Remove First Phone. The 2nd phone becomes the new first phone.
- Edit the phone number and tab out of the textbox. Notice the first phone in the All Phones list updates as expected.
- Change the Phone Type in the dropdown. Notice only the Type ID updates in the All Phones list. The Type Name does not update.
I am using a custom binding to bind the type name to the select's text. It seems the valueAccessor in that binding's init function must be pointing specifically to the original first PhoneVM's PhoneTypeName
property but what I need it to do is point to the firstPhone
computed property's PhoneTypeName
. Is there any way to fix this?
Copy of original jsfiddle:
function phoneListVM() {
var _self = this;
this.phones = ko.observableArray([
new phoneVM(1, "Mobile", "123-234-3456"),
new phoneVM(2, "Home", "654-343-3211")
]);
this.firstPhone = ko.computed(function() {
return _self.phones()[0];
});
}
function phoneVM(typeID, typeName, Number) {
this.PhoneTypeID = ko.observable(typeID);
this.PhoneTypeName = ko.observable(typeName);
this.PhoneNumber1 = ko.observable(Number);
}
ko.bindingHandlers.selectedTextValue = {
init: function(element, valueAccessor) {
var value = valueAccessor();
$(element).change(function() {
value($("option:selected", this).text());
});
},
update: function(element, valueAccessor) {}
};
$(document).ready(function() {
var phoneList = new phoneListVM()
ko.applyBindings(phoneList);
$("button").click(function() {
phoneList.phones.shift();
});
});
.editor{
background-color:rgba(200,200,250, 0.2);
border: 1px solid rgba(0,0,0, 0.2);
padding: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<h4>
Edit First Phone
</h4>
<div class="editor">
<p>Phone Number:
<input data-bind="value: firstPhone().PhoneNumber1" />
</p>
<p>Phone Type:
<select data-bind="value:firstPhone().PhoneTypeID, selectedTextValue:firstPhone().PhoneTypeName">
<option value="0"></option>
<option value="1">Mobile</option>
<option value="2">Home</option>
</select>
</p>
</div>
<h4>
All Phones
</h4>
<table>
<thead>
<tr>
<th>Type ID</th>
<th>Type Name</th>
<th>Number</th>
</tr>
</thead>
<tbody data-bind="foreach:phones">
<tr>
<td><span data-bind="text:PhoneTypeID"></span></td>
<td><span data-bind="text:PhoneTypeName"></span></td>
<td><span data-bind="text:PhoneNumber1"></span></td>
</tr>
</tbody>
</table>
<button type="button">
Remove First Phone
</button>