Automatically update field in current row of an observablearray from textInput field - javascript

I have a table bound to a view model. When I select the table row, a field (notes) is updated from this:
<tbody data-bind="foreach: namespace.PersonResults.model">
<tr data-bind="click: $root.selectItem, css: {selected: $root.isSelected($data)}">
<td data-bind="text: Forename"></td>
<td data-bind="text: Surname"></td>
<td data-bind="text: PostCode"></td>
<td data-bind="text: Notes" style="display: none"></td>
</tr>
</tbody>
The field in the same div as the table (this is a single text area that should be updated when selecting the row on the table above, and update the table by the time the user chooses another row).
<textarea data-bind="textInput: editNotes"></textarea>
the viewModel is currently doing this:
var resultsViewModel = function() {
var self = this;
self.model = ko.observableArray();
self.editNotes = ko.observable();
self.selectItem = function(record) {
self.selectedItem(record);
self.editNotes(record.Notes);
}
self.getData () {
// some ajax stuff to populate the table
}
}
This works fine for displaying the notes in the textarea, but how do I get this to go the other way, and populate the field in the observableArray if the user has altered the contents of the textarea?

You can just bind a td to the same property as the textarea. E.g.:
var resultsViewModel = function() {
var self = this;
self.editNotes = ko.observable('initial value');
}
var vm = {
selectedResult: ko.observable(null),
results: [new resultsViewModel(), new resultsViewModel()]
};
vm.selectResult = function(result) { vm.selectedResult(result); };
ko.applyBindings(vm);
.selected { background-color: pink; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<table><tbody data-bind="foreach: results">
<tr data-bind="css: { selected: $root.selectedResult() === $data }">
<td data-bind="text: editNotes"></td>
<td><button data-bind="click: $root.selectResult">Select</button></td>
</tr>
</tbody></table>
<!-- ko with: selectedResult -->
<textarea data-bind="textInput: editNotes"></textarea>
<!-- /ko -->

You need to bind to the value of a textarea:
<td>
<textArea data-bind="value: $data.Notes"></textArea>
</td>
I've knocked up a quick and simplified demo in this fiddle
Edited to add:
Here's an updated fiddle that's more in line with what you want: fiddle

Related

Show/Hide div within a single row in a foreach loop - KnockoutJS

I've tried several ways of doing this with no success. Would love some advice!
Goal: I have a table where each row is an order, but where within that row, if changes need to be made, a div appears underneath (in red). This needs to show/hide when a button on that row is clicked/toggled (Button is: Make Changes)
Issue: I have all the buttons working apart from the make changes toggle. Tried the visible observable, but the closest I could get was toggling the div's visibility for the whole table, not per row.
//Class to represent a row in the table
function orderDetail(order, orderChange) {
var self = this;
self.order = ko.observable(order);
self.orderChange = ko.observable(orderChange);
}
//Overall viewmodel, plus initial state
function FoodViewModel() {
var self = this;
self.foodTypes = [
{ foodType: "Please Select"},
{ foodType: "Veg"},
{ foodType: "Meat"}
];
self.orders = ko.observableArray([
new orderDetail(self.foodTypes[0], self.foodTypes[0])
]);
// Add and remove rows
self.addOrder = function() {
self.orders.push(new orderDetail(self.foodTypes[0], self.foodTypes[0]));
}
self.removeOrder = function(order) { self.orders.remove(order) }
}
ko.applyBindings(new FoodViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table>
<thead>
<tr>
<th>Orders</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: orders">
<tr>
<td>
<div><select data-bind="options: $root.foodTypes, value: order, optionsText: 'foodType'" id="foodList"></select></div>
<div><select data-bind="options: $root.foodTypes, optionsText: 'foodType', value: orderChange" id="foodListChange" style="color: red;"></select></div>
</td>
<td>
<button class="button button2" >Make Changes</button>
</td>
<td>
<button class="button button1" href="#" data-bind="click: $root.removeOrder">Remove</button>
</td>
</tr>
</tbody>
</table>
<button data-bind="click: addOrder" class="button">Add Order</button>
Thanks in advance!
If you want that the user interface reacts to something in Knockout, make an observable.
In this case you want to display part of the UI conditionally (apparently to toggle an edit mode), so let's create:
an observable editMode that is either true or false, to store the UI state
a function toggleEditMode that toggles between the two states, to bind it to the button
an if: editMode and an ifnot: editMode binding, to show different parts of the UI accordingly
function OrderDetail(params) {
var self = this;
params = params || {};
self.order = ko.observable(params.order);
self.orderChange = ko.observable(params.orderChange);
self.editMode = ko.observable(true);
self.buttonCaption = ko.pureComputed(function () {
return self.editMode() ? "Done" : "Edit";
});
self.toggleEditMode = function () {
self.editMode(!self.editMode());
}
}
function OrderList(params) {
var self = this;
params = params || {};
self.foodTypes = ko.observableArray(params.foodTypes);
self.orders = ko.observableArray();
self.addOrder = function(foodType) {
self.orders.push(new OrderDetail());
}
self.removeOrder = function(order) {
self.orders.remove(order);
}
}
var vm = new OrderList({
foodTypes: [
{foodType: "Veg"},
{foodType: "Meat"}
]
});
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table>
<thead>
<tr>
<th style="width: 150px;">Orders</th>
<th>Actions</th>
</tr>
</thead>
<tbody data-bind="foreach: orders">
<tr>
<td>
<div data-bind="ifnot: editMode">
<!-- ko with: order -->
<span data-bind="text: foodType"></span>
<!-- /ko -->
</div>
<div data-bind="if: editMode">
<select data-bind="
options: $root.foodTypes,
value: order,
optionsText: 'foodType',
optionsCaption: 'Please select…'
"></select>
</div>
</td>
<td>
<button class="button button2" data-bind="
click: toggleEditMode,
text: buttonCaption,
enable: order
"></button>
<button class="button button1" href="#" data-bind="
click: $root.removeOrder
">Remove</button>
</td>
</tr>
</tbody>
</table>
<button data-bind="click: addOrder" class="button">Add Order</button>
<hr>
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>
Notes
Don't make "Please Select" part of your food types. That's what the optionsCaption binding is for.
I've parameterized the viewmodels (see the params object). This will work better than hard-coding values or using long argument lists, especially if you want to use a mapping plugin later.
The "Done" button is disabled as long as no order is selected, via the enable: order binding, i.e. if the order property is empty, the enable binding will keep the button disabled.
The with: order binding serves a similar purpose. It will only display its contents when there actually is an order value to display. This will prevent rendering errors with incomplete OrderDetail instances.

knockout js bindng issue when using template binding

lets say i have a following code
var Names = ['test1','test2','test3'];
function ViewModel() {
var self = this;
self.RegistraionInfo = ko.observableArray(Names);
self.ChangeSelection = function (data,event) {
fnChangeSelection(data);
}
self.tableRows = ko.observableArray([]);
self.addNewRow = function () {
self.tableRows.push(new tableRow('', self));
}
self.addNewRow();
}
function tableRow(number, ownerViewModel) {
var self = this;
self.number = ko.observable(number);
self.remove = function () {
ownerViewModel.tableRows.destroy(this);
}
ko.applyBindings(new ViewModel());
and in html
<table >
<thead>
<tr class="active">
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="template:{name:'data-tableRow', foreach: tableRows}"></tbody>
<tfoot>
<tr>
<td >
<img id="btnAddRowProcedure1" src=#Url.Content("~/Content/img/plus.png") data-bind="click: addNewRow" />
</td>
</tr>
</tfoot>
</table>
<script id="data-tableRow" type="text/html">
<tr>
<td >
<img id="btnDelete" src=#Url.Content("~/Content/img/close.png") data-bind="click: function(){ $data.remove(); }" />
</td>
<td><select data-bind="options:$root.RegistraionInfo, , event:{ change:$root.ChangeSelection}"></select></td>
</tr>
</script>
what im trying to do here is when user clicks the #btnAddRowProcedure1 link a new tablerow will be added. and inside the table row there is a dropdown that is bound to the Names array.when the user changes the dropdown selection the ChangeSelection function is called.
The Problem is that i want knock out to send currently selected Names array element as Data but knockout sending the tableRow as data.Is there a way to overcome this problem.Im still not sure why this works like this.
UPDATE
After the #Roy J changes i was able to get this to work.but now i have stumbled upon another problem (sorry im a noob at knockout).I want to add a button when ever the user changes the selection so i have added the following code to the ViewModel
self.displayAddBtn = ko.computed(function () {
if (self.tableRows.length == 0)
{
return false;
}
return self.tableRows[self.tableRows.length-1].selected() !="";
}, self);
this code is only executed once.when the user changes the selection this is code is not executed.can some one tell me how i can make this work
You need to have a value binding on your select. That's where the new value will show up when your change function is called.
var Names = ['test1', 'test2', 'test3'];
function ViewModel() {
var self = this;
self.RegistraionInfo = ko.observableArray(Names);
self.ChangeSelection = function(data, event) {
console.debug("Changed to:", data.selected());
}
self.tableRows = ko.observableArray([]);
self.addNewRow = function() {
self.tableRows.push(new tableRow('', self));
}
self.addNewRow();
}
function tableRow(number, ownerViewModel) {
var self = this;
self.number = ko.observable(number);
self.remove = function() {
ownerViewModel.tableRows.destroy(this);
};
self.selected = ko.observable(); // new! bound to select
}
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<table>
<thead>
<tr class="active">
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="template:{name:'data-tableRow', foreach: tableRows}"></tbody>
<tfoot>
<tr>
<td>
<img id="btnAddRowProcedure1" src=#Url.Content( "~/Content/img/plus.png") data-bind="click: addNewRow" />
</td>
</tr>
</tfoot>
</table>
<template id="data-tableRow">
<tr>
<td>
<img id="btnDelete" src=#Url.Content( "~/Content/img/close.png") data-bind="click: function(){ $data.remove(); }" />
</td>
<td>
<select data-bind="options:$root.RegistraionInfo, value:selected, event:{ change:$root.ChangeSelection}"></select>
</td>
</tr>
</template>

Knockout: Using select in foreach loop selects the same exact value in all selects

I just recently started (actually my first project with it) to use Knockout and absolutely love it.
However I've run into an issue, that I seems unable to resolve on my own.
I have a select drop down that runs inside another foreach loop.
Everything looks ok, but the moment I select in one of the dropdowns, it automatically selects same value in all of them.
For example if I select value 'Remove' then all the dropdowns in that foreach will become selected on 'Remove' value.
I would really appreciate help with this one.
Here is the relevant JavaScript (There is more going on in FoldersFileBrowserViewModel but I have removed the excess code) and HTML code
Thank you in advance.
/// <reference path="jquery-2.1.4.min.js" />
/// <reference path="knockout-3.3.0.debug.js" />
$(document).ready(function () {
function FoldersFileBrowserViewModel() {
var self = this;
//actions drop down
self.itemActions = ko.observableArray([{ ActionName: 'Remove' }, { ActionName: 'Move' }]);
self.selectedAction = ko.observable();
var subscription = self.selectedAction.subscribe(function (newValue) {
console.log(newValue.ActionName);
//alert(self.selectedAction().ActionName);
/* do stuff */
});
// ...then later...
//subscription.dispose(); // I no longer want notification
}
ko.applyBindings(new FoldersFileBrowserViewModel());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<tbody data-bind="foreach: filesInFolder" style="border-top:none;">
<tr>
<td></td>
<td class="color-blue">
<input type="checkbox" />
<img src="~/Images/icons/Document-copy-icon.png" alt="file" />
<span style="font-weight:500; color:#555;" data-bind="text: FileName"></span>
#*<input type="hidden" data-bind="text: FilePath">*#
</td>
<td></td>
<td>
#*value: $root.selectedAction,*#
<select data-bind="options: $root.itemActions, optionsText: 'ActionName', value: $root.selectedAction, optionsCaption: '- Select Action -'"></select>
<button data-bind="click: $parent.RemoveFile" style="background-color:transparent; border:none;">
<img src="~/Images/icons/window-app-list-close-icon.png" alt="delete file" />
</button>
</td>
</tr>
</tbody>
You need something to wrap each selected action for each fileInfolder
Something like this based in your code:
$(document).ready(function () {
var File = function (file) {
var self = this;
/* some fields*/
self.FileName = ko.observable( file ? file.FileName : '');
self.FilePath = ko.observable( file ? file.FilePath : '');
self.selectedAction = ko.observable(file ? file.selectedAction : undefined);
var subscription = self.selectedAction.subscribe(function (newValue) {
console.log(newValue); // Log selectedAction here comes the optionsValue field
//alert(self.selectedAction().ActionName);
/* do stuff */
});
// ...then later...
//subscription.dispose(); // I no longer want notification
}
function FoldersFileBrowserViewModel() {
var self = this;
//actions drop down are ok here load them only once if are the same :)
self.filesInFolder = ko.observableArray();
self.itemActions = ko.observableArray([{ ActionName: 'Remove' }, { ActionName: 'Move' }]);
self.filesInFolder.push(new File({ FileName : 'File1' }));// just to add some stuff to test
}
ko.applyBindings(new FoldersFileBrowserViewModel());
});
HTML:
<table>
<tbody data-bind="foreach: { data: filesInFolder , as: 'file' }" style="border-top:none;">
<tr>
<td></td>
<td class="color-blue">
<span style="font-weight:500; color:#555;" data-bind="text: FileName"></span>
<input type="hidden" data-bind="text: FilePath">
</td>
<td></td>
<td>
<select data-bind="options: $root.itemActions, optionsText: 'ActionName', optionsValue: 'ActionName', value: selectedAction, optionsCaption: '- Select Action -'"></select>
</td>
</tr>
</tbody>
Sorry, I'm really bad using this editor always >.<

Trigger controller action when selecting item in table with KODataTable MVC

I'm presenting data in a table by calling an Action(that returns a list in Json) through an AJAX call.
Output:
http://imageshack.com/a/img673/3484/zxZIqy.png
What i would like to do is to make each user (rows in table) linkable to an edit page (Admin/Edit/Id). Either by simply clicking on them, or by having an Edit-link at the end of each row.
I don't know how to achieve this. It would be easy with ordinary razor syntax. But this template seams to be nice to work with to achieve this sort of dynamic datatable.
I'm working with a template called KODataTable to make this table with both searching and sorting ability.
View..
<div id="kodt">
<div>
<input type="text" data-bind="value: searchText, valueUpdate: 'afterkeydown'" />
<select data-bind="value: selectedColumn, options: columns"></select>
<button data-bind="click: search">Search</button>
</div>
<div>
<table class="table table-striped table-hover">
<thead>
<tr data-bind="foreach: columns">
<th data-bind="text: $data, click: function() { $parent.sort($index()) }" style="cursor: pointer"></th>
</tr>
</thead>
<tbody data-bind="foreach: currentRows">
<tr data-bind="foreach: $parent.columns, click: function () { $root.selectRow($data); }, css: { 'success': $root.selectedRow() == $data }">
<td data-bind="text: $parent[$data]" style="cursor: pointer; text-align: center"></td>
</tr>
</tbody>
</table>
</div>
<div>
<button data-bind="click: firstPage">First</button>
<button data-bind="click: prevPage">Prev</button>
Page <span data-bind="text: currentPage() + 1"></span> of <span data-bind="text: pageCount"></span>
<button data-bind="click: nextPage">Next</button>
<button data-bind="click: lastPage">Last</button>
</div>
</div>
Script..
<script>
var users = new Object();
$.getJSON("/Admin/GetUsers", function (data) {
users = data;
var TableDataVM = new KODataTable({
columns: ["Id", "Username", "RoleId", "CompanyId", ""],
rows: users,
});
ko.applyBindings(TableDataVM, document.getElementById("kodt"));
});
</script>
Right now, it looks like when a user clicks a row, $root.selectRow($data); is invoked, which passes the row object's data over to some function in the ViewModel called selectRow(). If this function exists, you could use it to $.post the row (representing the user object) to the MVC controller in this function, and use the response to redirect to the Edit view.
var vm = function() {
var self = this;
var selectRow = function(rowClicked) {
var data = {
Id = rowClicked.Id,
Username = rowCicked.Username,
// etc.
};
// post the data to some MVC controller - something remotely like this
$.ajax({
url: '/Admin/Edit/' + rowClicked.Id,
data: ko.toJSON(data),
type: 'POST',
success: function (result) {
window.location.href = result.url;
}
});
};
};

How can I append text with table tr using knockout

I have got a assignment to implement knockout js to my application. I have a table like
<table>
<tr>
<th>
Name
</th>
<th>
Category
</th>
<th>
Price
</th>
<th></th>
</tr>
<tr>
<td>
Iphone
</td>
<td>
SmartPhone
</td>
<td>
50000
</td>
</tr>
</table>
There are three textbox for creation of this field.
<div id="create">
<input data-bind="value: Name" id="name"/>
<input data-bind="value: Category" id="category"/>
<input data-bind="value: Prize" id="prize"/>
</div>
When I am typing on this textboxes i want to show this on the table as a new tr.. How can I do this? DEMO
Reference Link
What you want to do is define a viewmodel that contains the data for an individual item, and another viewmodel that contains the rest of the interactions (list of items, how to add new ones, etc).
var Item = function (Name, Category, Price) {
var self = this;
self.Name = ko.observable(Name);
self.Category = ko.observable(Category);
self.Price = ko.observable(Price);
}
var ViewModel = function () {
var self = this;
self.ItemToAdd = ko.observable(new Item());
self.Items = ko.observableArray([]);
self.addItem = function (item) {
self.Items.push(item);
self.ItemToAdd(new Item());
}
};
var vm = new ViewModel();
vm.addItem(new Item('Iphone', 'SmartPhone', 50000));
ko.applyBindings(vm);
In your html, your table body will look like this:
<tbody data-bind="foreach: Items">
<tr>
<td data-bind="text: Name"/>
<td data-bind="text: Category"/>
<td data-bind="text: Price"/>
</tr>
</tbody>
what this does is loops through each item in the Itemlist and creates a <tr> for each one and binds the values of the Item object in the observableArray to the <td> elements.
to add new items to the table in your markup:
<div data-bind="with: ItemToAdd">
<input data-bind="value: Name" id="name"/>
<input data-bind="value: Category" id="category"/>
<input data-bind="value: Price" id="price"/>
<button data-bind="click: $parent.addItem">Add</button>
</div>
this sets the context of the div element to a new Item object, and when you click the Add button, it calls the parent context's (ViewModel) addItem function, and automatically passes the context item for the div element (ItemToAdd). Then its just a matter of pushing it on to the observableArray and the table will update with the new item.
Updated Fiddle: http://jsfiddle.net/BJQgw/4/
if this was a for-real application, you would perform some sort of validation prior to adding the item to the list (preferably using knockout-validation)

Categories

Resources