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)
Related
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.
trying to figure out how to set up the radio button bindings in a table in knockout. I would like when the radio button on the table is selected. the entire selected record is available in the model. not quite sure how to set up the binding on the radio input. I assume I need to use $parent and a function for the value binding?
here is the fiddle. (the selected record does not do anything right now I would like it to be populated when the radio button is selected)
https://jsfiddle.net/othkss9s/5/
HTML
<table>
<thead>
<tr>
<th>Select</th>
<th>First</th>
<th>Last</th>
<th>Dept</th>
</tr>
</thead>
<tbody data-bind='foreach: employees'>
<tr>
<td><input type="radio" name="employees"></td>
<td data-bind='text: first'></td>
<td data-bind='text: last'></td>
<td data-bind='text: dept'></td>
</tr>
</tbody>
</table>
<div data-bind="with: selectedRow">
<h2>
Selected Record
</h2>
<p>
First: <span data-bind="first" ></span>
</p>
<p>
Last: <span data-bind="last" ></span>
</p>
<p>
Dept: <span data-bind="dept" ></span>
</p>
</div>
JS
function employee(first, last, dept) {
this.first = ko.observable(first);
this.last = ko.observable(last);
this.dept = ko.observable(dept);
}
function model() {
var self = this;
this.employees = ko.observableArray("");
this.selectedRow = ko.observable({});
};
var myViewModel = new model();
$(document).ready(function() {
ko.applyBindings(myViewModel);
myViewModel.employees.push(
new employee("Bob","Jones", "Hr")
);
myViewModel.employees.push(
new employee("Sarah","Smith", "It")
);
myViewModel.employees.push(
new employee("John","Miller", "It")
);
});
You have to perform two steps to get your code working.
First, apply bindings to radio buttons:
<input type="radio" data-bind="checked: $root.selectedRow, checkedValue: $data" />
checkedValue should contain actual value that corresponds the current radio button. In this case we refer $data variable that is whole employee object rather than simple (scalar) value.
checked binding should refer to an observable containing currently selected employee.
Second, correct the line where the selectedRow property is defined:
this.selectedRow = ko.observable();
Do not pass empty object as default value. Otherwise with binding won't work correctly.
By fixing the syntax of bindings in your second block that displays selected employee you will get something like this.
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
Somehow I can't get my head around this in angular- I have a Table and want to add the row on-click, but this row should have empty input boxes and a button to remove itself.
<tbody>
<tr>
<th>Test 1</th>
<th>200</th>
<th>Add new row</th>
</tr>
//I want to Add this part dynamically everytime when I click on Add new row
<tr>
<td>
<input type="text" placeholder="Please Enter the Address">
</td>
<td>
<input type="text" placeholder="Number of Quantity">
</td>
//On Delete it should delete just that particular row
<td>Delete</td>
</tr>
</tbody>
I have created plunker http://plnkr.co/edit/DDj5Z99tw1QuN8xlxZ7V?p=preview just for showing what I am trying to achieve. If anyone would be able to give a hint or link me to tutorial would be great!
Please have look at this plunker.
http://plnkr.co/edit/ogvezWz6WDwDhCnm2bDU
Idea is to use a row at the end whose visibility can be controlled. And using ngRepeat you can iteratively display your added product items.
<tr ng-repeat="row in rows">
<td>
{{row.product}}
</td>
<td>
{{row.quantity}}
</td>
<td>
<button ng-click="deleteRow($index)">Delete</button>
<button ng-click="addNewRow()">New</button>
</td>
</tr>
<tr ng-show="addrow">
<td>
<input type="text" placeholder="Please Enter the Address" ng-model="product"/>
</td>
<td>
<input type="text" placeholder="Number of Quantity" ng-model="quantity"/>
</td>
<td><button ng-click="save()">Save</button> <button ng-click="delete()">Delete</button></td>
</tr>
And the Controller code
angular.module('AddRow', [])
.controller('MainCtrl', [
'$scope', function($scope){
$scope.rows = [ { "product": "Test 1", "quantity": "200"}];
$scope.addrow = false;
$scope.addNewRow = function(){
$scope.addrow = true;
};
$scope.deleteRow = function(index){
//delete item from array
$scope.rows.splice(index,1);
};
$scope.save = function(){
//add item to array
$scope.rows.push({"product": $scope.product, "quantity": $scope.quantity});
//reset text input values
$scope.product = "";
$scope.quantity = "";
//hide the add new row
$scope.addrow = false;
};
$scope.delete = function(){
$scope.addrow = false;
};
}]);
I need a little help.
I have created a table that gets values from JSON response, but for this example lets create a hardcoded html table like following:
<table id="devtable">
<tr>
<th>ID</th>
<th>Name</th>
<th>Status</th>
</tr>
<tr>
<td>001</td>
<td>Jhon</td>
<td>Single</td>
</tr>
<tr>
<td>002</td>
<td>Mike</td>
<td>Married</td>
</tr>
<tr>
<td>003</td>
<td>Marrie</td>
<td>Complicated</td>
</tr>
</table>
ID : <input type="text" name="ID" data-bind="value: ID" disabled/>
<br>
Name : <input type="text" name="Name" data-bind="value: Name" disabled/>
<br>
Status : <input type="text" name="Status" data-bind="value: Status" disabled/>
<br>
<input type="button" value="Send" disabled/>
Requirement is: when I select a row of table, values of columns goes to the input boxes and enable button as well. As I am trying to learn Knockout.js by doing this exercise. I think I have to make a viewmodel like this:
var rowModel = function (id, name, status) {
this.ID = ko.observable(id);
this.Name = ko.observable(name);
this.Status = ko.observable(status);
}
Link of project is here: http://jsfiddle.net/qWmat/
Here's an example of how you could do it:
http://jsfiddle.net/qWmat/3/
function MyVM(data) {
var self = this;
self.items = ko.observableArray(data.map(function (i) {
return new rowModel(i.id, i.name, i.status);
}));
self.select = function(item) {
self.selected(item);
};
self.selected = ko.observable(self.items()[0]);
}
And you bind your textboxes to the properties in the selected property:
<input type="text" name="ID" data-bind="value: selected().ID" disabled/>
And you bind the click handler in your tr like so:
<tr data-bind="click: $parent.select">
Updated to include enable binding (http://jsfiddle.net/qWmat/8/). Add a property for whether or not to edit:
self.enableEdit = ko.observable(false);
Then update your select function to turn it to true:
self.select = function(item) {
self.selected(item);
self.enableEdit(true);
};
If / when you save or cancel you could the set it back to false if you want.
Update your bindings on the input boxes:
<input type="text" name="Status" data-bind="value: selected().Status, enable: enableEdit" />
I've created a demo for you, but to know how it works, you should investigate knockout documentation.
ViewModel:
<table id="devtable">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Status</th>
</tr>
</thead>
<tbody data-bind="foreach: Items" >
<tr data-bind='click: $parent.setEditItem'>
<td data-bind="text: ID"></td>
<td data-bind="text: Name"></td>
<td data-bind="text: Status"></td>
</tr>
</tbody>
</table>
<!-- ko with: SelectedItem -->
ID :
<input type="text" name="ID" data-bind="value: ID, attr: {disabled: !$parent.IsEditMode()}" />
<br>Name :
<input type="text" name="Name" data-bind="value: Name, attr: {disabled: !$parent.IsEditMode()}"/>
<br>Status :
<input type="text" name="Status" data-bind="value: Status, attr: {disabled: !$parent.IsEditMode()}"/>
<br>
<input type="button" value="Send" data-bind="attr: {disabled: !$parent.IsEditMode()}"/>
<!-- /ko -->
Html:
function ItemModel(id, name, status) {
var self = this;
self.ID = ko.observable(id);
self.Name = ko.observable(name);
self.Status = ko.observable(status);
}
function ViewModel() {
var self = this;
self.Items = ko.observableArray([
new ItemModel('001', 'Jhon', 'Single'),
new ItemModel('002', 'Mike', 'Married'),
new ItemModel('003', 'Marrie', 'Complicated')
]);
self.SelectedItem = ko.observable(new ItemModel());
self.IsEditMode = ko.observable();
self.setEditItem = function(item) {
self.SelectedItem(item);
self.IsEditMode(true);
}
}
var viewModel = new ViewModel();
ko.applyBindings(viewModel);
Demo