I'm new to KnockoutJS. I want to know if there is a better way to accomplish the functionality below.
An object's properties show up as text in a table row. I can click each span of text individually to make a textbox appear so I can edit the text. It's important to note that other properties related to the object do NOT become editable. Only one property is editable at a time. To accomplish this, during KO Mapping I actually overwrite each property with an object that has two properties: A 'value' property that holds the original property's value, and a 'hasFocus' property that I use to keep track of the visibility of the textbox.
Here's a JSFiddle to show how I currently have it programmed. Be sure to click on the Item names and prices to see the textbox appear.
Further explanation
I have a table where each row represents a TransactionItem.
In the starting state, each field is text. When clicked, the text disappears and a textbox appears. When the textbox loses focus, the textbox disappears and the modified text reappears.
The following steps show what I did to accomplish this:
Map the data from the server using KO Mapping plugin ('myData' in this example),
var myData = [ { TransactionItems: [ { Name: "Item1", Price: "1.00" }, { Name: "Item2", Price: "2.00" }, { Name: "Item3", Price: "3.00" }, ]}, ]; var mappingOptions = { 'TransactionItems': { create: function (options) { return new TransactionItem(options.data); } } } var viewModel = {}; var self = viewModel; viewModel.transactions = ko.mapping.fromJS(myData, mappingOptions); ko.applyBindings(viewModel);Within the TransactionItems constructor function, grab the current values of the TransactionItem properties ('name', 'price'), and store them in temp variables. Overwrite the properties with new objects. These new objects contain two values: the 'value' of the original property, and a new 'hasFocus' property
//Grab the current values of the properties var itemValue = this.Name(); var priceValue = this.Price(); //Recreate properties as objects, give them a 'hasFocus' property this.Name = { value: itemValue, hasFocus: ko.observable(false)}; this.Price = { value: priceValue, hasFocus: ko.observable(false) };
For example, the 'name' property becomes the object Name: { value: 'Item1', hasFocus: false }.
In HTML, data-bind the 'hasFocus' of each property to control when to show/hide the text/textbox.
When text is clicked, the 'hasFocus' property is set to true, which will hide the text, and displays the textbox.
- When textbox blurs, the 'hasFocus' property is set to false, which hides the textbox, and displays the text.
I want to know if there is a better approach to accomplish this functionality. Have I gone way off track? Thanks!
Larger snippet of code:
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<!-- ko foreach: transactions -->
<!-- ko template: { name: 'listItems', foreach: TransactionItems } -->
<!-- /ko -->
<!-- /ko -->
</tbody>
</table>
<script type="text/html" id="listItems">
<tr>
<td>
<!-- Either show this -->
<span data-bind="visible: !Name.hasFocus(),
text: Name.value,
click: editItem.bind($data, Name)"></span>
<!-- Or show this -->
<input data-bind="visible: Name.hasFocus,
value: Name.value,
hasfocus: Name.hasFocus,
event: {
focus: editItem.bind($data, Name),
blur: hideItem.bind($data, Name)
}"
/><!-- end input -->
</td>
<td>
<!-- Either show this -->
<span data-bind="visible: !Price.hasFocus(),
text: Price.value, click: editItem.bind($data, Price)"></span>
<!-- Or show this -->
<input data-bind="visible: Price.hasFocus,
value: Price.value,
hasfocus: Price.hasFocus,
event: {
focus: editItem.bind($data, Price),
blur: hideItem.bind($data, Price)
}"
/><!--input end -->
</td>
</tr>
</script>
<!-- END OF HTML -->
<script >
function TransactionItem(data) {
ko.mapping.fromJS(data, {}, this);
this.editable = ko.observable(false);
this.hasFocus = ko.observable(false);
//Grab the current values of the properties
var itemValue = this.Name();
var priceValue = this.Price();
//Recreate properties as objects, give them a 'hasFocus' property
this.Name = { value: itemValue, hasFocus: ko.observable(false)};
this.Price = { value: priceValue, hasFocus: ko.observable(false) };
this.editItem = function (objProperty) {
this.editable(true);
objProperty.hasFocus(true);
}
this.hideItem = function (objProperty) {
this.editable(false);
objProperty.hasFocus(false);
}
}
//MAPPING
var mappingOptions = {
'TransactionItems': {
create: function (options) {
return new TransactionItem(options.data);
}
}
}
//DATA
var myData = [
{
TransactionItems: [
{
Name: "Item1",
Price: "1.00"
},
{
Name: "Item2",
Price: "2.00"
},
{
Name: "Item3",
Price: "3.00"
},
]},
];
//VIEWMODEL
var viewModel = {};
var self = viewModel;
viewModel.transactions = ko.mapping.fromJS(myData, mappingOptions);
ko.applyBindings(viewModel);
</script>