Knockout array within array - javascript

I'm trying to create a simple spreadsheet using Knockout. I'm trying to make each cell observable, so that on changes, I can evaluate the value and calculate accordingly. So if they person enters 6+7 in a cell, I can evaluate and change the value to the total.
However, I can't get each cell to be observable. Maybe I am going about it wrong.
I have tried to create a fiddle, but am now battling to get jquery loaded. So although I can run it within Visual Studio locally, the fiddle is complaining about $. (Any help fixing that would be great).
http://jsfiddle.net/tr9asadp/1/
I generate my observable array like this:
self.RowCount = ko.observable(0);
self.ColumnCount = ko.observable(0);
self.Columns = ko.observableArray([]);
self.Rows = ko.observableArray([]);
self.Refresh = function () {
for (i = 0; i < self.RowCount(); i++) {
var obj = {
data: i + 1,
calculated: i,
rowNum: i,
colNum: 0,
columns: ko.observableArray([])
};
for (j = 0; j < self.ColumnCount(); j++) {
obj.columns.push(ko.observable({
label: self.Letters[j],
value: j + 1,
colIndex: j,
rowIndex: i
}));
}
self.Rows.push(obj);
}
self.ShowSheet(self.RowCount() > 0 && self.ColumnCount() > 0);
I render a table based on the column and rows entered by the user (For now, limited to 5 by 5, as I using an array to convert 1,2,3 (columns) to A,B,C. But that's temporary and will be fixed.
How can I get each cell to be observable so that I can subscribe and fire an event on change?

You don't seem to have made use of cellObject (from your fiddle). If you add objects of type cellObject to the rows and have an observable in there for value you can subscribe to changes on that.
Fixed code:
var cellObject = function() {
var self = this;
self.data = ko.observable();
self.calculated = ko.observable();
self.rowNum = ko.observable(0);
self.colNum = ko.observable(0);
self.rows = ko.observableArray([]);
self.value = ko.observable();
}
function SpreadsheetViewModel() {
var self = this;
self.ShowSheet = ko.observable(false);
self.ShowSheet(false);
self.Letters = ['A', 'B', 'C', 'D', 'E']
self.RowCount = ko.observable(0);
self.ColumnCount = ko.observable(0);
self.Columns = ko.observableArray([]);
self.Rows = ko.observableArray([]);
function valueChanged(newValue) {
console.log("Value changed to " + newValue);
}
self.Refresh = function() {
for (i = 0; i < self.RowCount(); i++) {
var row = {
cells: ko.observableArray([])
};
for (j = 0; j < self.ColumnCount(); j++) {
var cell = new cellObject();
cell.label = self.Letters[j];
cell.data(i + 1);
cell.calculated(i);
cell.rowNum(i);
cell.colNum(j);
cell.value(j + 1);
cell.value.subscribe(valueChanged);
row.cells.push(cell);
}
self.Rows.push(row);
}
self.ShowSheet(self.RowCount() > 0 && self.ColumnCount() > 0);
}
self.Refresh();
}
var vm = new SpreadsheetViewModel();
ko.applyBindings(vm);
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<div id="spreadsheetSection">
<div class="row">
<div class="col-xs-3 text-right">No. of Columns</div>
<div class="col-xs-2">
<input type="text" class="form-control" placeholder="Columns" data-bind="value: ColumnCount">
</div>
<div class="col-xs-3 text-right">No. of Rows</div>
<div class="col-xs-2">
<input type="text" class="form-control" placeholder="Rows" data-bind="value: RowCount">
</div>
<div class="col-xs-2">
<button class="btn btn-default" data-bind="click: Refresh">Refresh</button>
</div>
</div>
<div class="row">
<!-- ko if: ShowSheet -->
<table class="table table-bordered table-hover table-striped">
<tbody>
<tr data-bind="foreach: Rows()[0].cells">
<td>
<span data-bind="text: label"></span>
</td>
</tr>
</tbody>
<tbody data-bind="foreach: Rows">
<tr data-bind="foreach: cells">
<td>
<input type="text" class="form-control" data-bind="value: value">
</td>
</tr>
</tbody>
</table>
<!-- /ko -->
</div>
</div>
Fixed fiddle: https://jsfiddle.net/tr9asadp/3/

I used a writableComputable http://knockoutjs.com/documentation/computed-writable.html so that if you type 1 + 1 in one of the cells and tab out, it will change to 2. here is the updated fiddle. http://jsfiddle.net/tr9asadp/5/
function column(label, value, colIndex, rowIndex ){
var self = this;
this.label = ko.observable(label);
this.value = ko.observable(value);
this.colIndex = ko.observable(colIndex);
this.rowIndex = ko.observable(rowIndex);
this.writableValue = ko.pureComputed({
read: function () {
return self.value();
},
write: function (v) {
self.value(eval(v))
},
owner: this
});
}

Related

Angularjs, show/hide row's table with ng-repeat

Sorry, i'm new to ng-repeat. How can i show/hide row table that is using ng-repeat? And the most bottom row is shown if dropdown value is 1.
var i ;
$scope.names = [];
$scope.tmp = [];
for(i=0;i<=10;i++){
$scope.tmp[i] = i;
$scope.names[i] = "name "+ i;
}
$scope.isShow = true
html
<select>
<option ng-repeat="x in tmp">{{x}}</option>
</select>
<table>
<tr ng-show='isShow' ng-repeat="name in names">
<td>{{name}}</td>
</tr>
</table>
May be you must add property isShow for each name in names?
Or create array with visible status for each name.
angular.module('app', [])
.directive('appDir', appDir);
angular.bootstrap(
document.getElementById('root'), ['app']
);
function appDir() {
return {
template: `
<table>
<tr
ng-repeat="name in names"
ng-show="name.isShow"
>
<td>
{{name.title}}
</td>
</tr>
</table>
<select
ng-model="selectedName"
ng-options="x as x for x in tmp"
ng-change="hiddenName()"
>
`,
link: appDirLink
}
}
function appDirLink($scope) {
$scope.names = [];
$scope.tmp = [];
$scope.hiddenName = hiddenName;
for (var i = 0; i < 10; i++) {
$scope.names[i] = {
id: i,
title: 'name_' + i,
isShow: true
};
$scope.tmp[i] = i;
}
function hiddenName() {
$scope.names.map((name, index) => {
name.isShow = (index < $scope.selectedName) ? true : false;
});
}
}
<div id="root">
<app-dir></app-dir>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.5/angular.min.js"></script>

.js variables not returning to zero when made equal to 0

I have various .js functions calculating order sub-totals, tax, and final total price in a modal. The calculations are correct, but the variables don't seem to be cleared on modal exit/modal pop-up. This means that every modal past the first one simply adds their (correct) calculations to the previous ones, rather than starting from zero as I'm trying to make it do.
I've set breakpoints in the .js source, and it tells me that the .js numbers are the one's not zeroing even when there is a set to zero on them right after the number is passed back.
Here is a picture set of the first and second modal call to illustrate this issue (bottom right numbers on both):
Here is my html, in case it's actually a tag-based issue:
#{
ViewData["Title"] = "Index";
Layout = "~/Views/_Layout.cshtml";
}
<div class="col-sm-3"> </div>
<div class="panel col-sm-6 col-xs-12">
<div class="panel-title text-center" style="padding-top:20px;">
<h3 style="font-weight:bolder">Cart Contents</h3>
<img src="/img/cart.png" style="height:10%;width:10%;padding-bottom:5%;" />
</div>
<div class="text-center" style="padding-top:10px;">
#{
Dictionary<string, object> cart = Context.Session.Get<Dictionary<string, Object>>("cart");
decimal itemSubTotal = 0;
decimal subTotal = 0;
decimal itemTaxTotal = 0;
decimal taxTotal = 0;
decimal salesTaxRate = 0.13M; //m required for a literal
decimal orderTotal = 0;
}
<table class="table table-striped">
<tr style="font-weight:bolder;">
<th class="col-xs-2 text-center">Product Code</th>
<th class="col-xs-2 text-center">Qty</th>
<th class="col-xs-2 text-center">Item Name</th>
<th class="col-xs-2 text-center">Price</th>
</tr>
#{
if (cart != null)
{
foreach (var key in cart.Keys)
{
ProductViewModel item = JsonConvert.DeserializeObject
<ProductViewModel>
(Convert.ToString(cart[key]));
if (item.Qty > 0)
{
subTotal += item.MSRP * item.Qty;
<tr>
<td class="col-xs-2 text-center">#item.Id</td>
<td class="col-xs-2 text-center">#item.Qty</td>
<td class="col-xs-2 text-left">#item.ProductName</td>
<td class="col-xs-2 text-center">#string.Format("{0:C}", #item.MSRP)</td>
</tr>
}
}
taxTotal += Decimal.Multiply(subTotal, salesTaxRate);
orderTotal += subTotal + taxTotal;
}
}
</table>
<hr />
<table class="table table-striped">
<tr>
<th colspan="4" class="col-xs-4 text-left" style="font-size:large;font-weight:bold;">
Cart
Totals
</th>
</tr>
<tr>
<td class="col-xs-2 text-right">Subtotal: </td>
<td class="col-xs-4 text-left" id="subtotal">#string.Format("{0:C}", #subTotal)</td>
</tr>
<tr>
<td class="col-xs-2 text-right">Tax Total: </td>
<td class="col-xs-4 text-left" id="taxtotal">#string.Format("{0:C}", #taxTotal)</td>
</tr>
<tr>
<td class="col-xs-2 text-right">Order Total: </td>
<td class="col-xs-4 text-left" id="ordertotal">#string.Format("{0:C}", #orderTotal)</td>
</tr>
#{
#subTotal = 0;
#taxTotal = 0;
#orderTotal = 0;
}
</table>
<div class="text-center">
<form asp-controller="Cart" asp-action="AddCart" method="post" role="form">
#if (Context.Session.GetString(SessionVars.User) != null)
{
<button type="submit" class="btn btn-sm btn-primary" id="modalbtn">Add Cart</button>
}
Clear Cart
</form>
</div>
</div>
</div>
And here is the .js file where the calculations are being done:
var link = '/GetOrders';
var detailslink = '/GetOrderDetails/';
var subtotal = 0;
var finalPrice = 0;
var taxAmount = 0;
// register modal component
Vue.component('modal', {
template: '#modal-template',
props: {
item: {},
modalItem: {},
details: []
},
})
new Vue({
el: '#orders',
methods: {
GetOrders: function () {
var self = this
axios.get(link).then(function (response) {
self.orders = response.data;
}, function (error) {
console.log(error.statusText);
});
},
loadAndShowModal: function () {
var self = this
axios.get(detailslink + this.modalItem.id).then(function (response) {
self.details = response.data;
self.showModal = true;
}, function (error) {
console.log(error.statusText);
});
},
},
mounted: function () {
this.GetOrders();
},
data: {
orders: [],
showModal: false,
modalItem: {},
details: []
}
});
function formatDate(date) {
var d;
if (date === undefined) {
d = new Date(); //no date coming from server
}
else {
var d = new Date(Date.parse(date)); // date from server
}
var _day = d.getDate();
var _month = d.getMonth() + 1;
var _year = d.getFullYear();
var _hour = d.getHours();
var _min = d.getMinutes();
if (_min < 10) { _min = "0" + _min; }
return _year + "-" + _month + "-" + _day + " " + _hour + ":" + _min;
}
function calcLinePrice(qtySold, msrp)
{
var linePrice = qtySold * msrp;
subtotal += linePrice;
finalPrice += linePrice;
return linePrice.toFixed(2);
finalPrice = 0;
subtotal = 0;
}
function calcSubtotal()
{
return subtotal.toFixed(2);
subtotal = 0;
finalPrice = 0;
subtotal = 0;
}
function calcTax() {
taxAmount = finalPrice * 0.13
return taxAmount.toFixed(2);
taxAmount = 0;
}
function calcFinalPrice() {
var total = 0;
total = finalPrice + taxAmount;
return total.toFixed(2);
finalPrice = 0;
subtotal = 0;
}
As you can see, I'm zeroing the finalTotal and subtotal in every calculation in an attempt to figure this out. It seems that no matter what I do, they refuse to zero on anything but page reload. Any help?
EDIT: I was attempting to zero at the wrong points. I solved this by altering my calcFinalPrice() function (being the last function) to this:
function calcFinalPrice() {
var total = 0;
total = finalPrice + taxAmount;
taxAmount = 0;
subtotal = 0;
finalPrice = 0;
return total.toFixed(2);
}
You are returning before re-assigning "0" to the variables. Use return after reassigning the values.

Display a comparison table

I have a html table with each column displaying a company and each row has a feature that offers this company.
Let's say
Company A has features 1,2,3 and features 8,9 optional.
Company B has features 1,3 and features 7,8 optional.
Company C has features 3,4 and features 9,10 optional.
//A B C
//-------------
//1 | Y Y X
//2 | Y X X
//3 | Y Y Y
//4 | X X Y
//7 | X O X
//8 | O O X
//9 | O X O
//10 | X X O
I dont display features 5,6 because they are missing in all companies.
I want the table to display "fa-check"(Y) when the company contains the feature, "fa-times"(X) when is missing and input type="checkbox" when the feature is optional. Each optional feature has a price so the total price for a company is recalculated when is checked
<table>
<thead>
<tr>
<!-- ko foreach : companies -->
<th data-bind="text: name"></th>
<!-- /ko -->
</tr>
</thead>
<tbody>
<!-- ko foreach : UnionOfFeatures-->
<tr>
<!-- ko foreach : companies -->
<td data-bind="if: mandatory"><i class="fa fa-check"></i></td>
<td data-bind="ifnot: mandatory"><input type="checkbox" data-bind="checked: Checked"></td>
#*<td data-bind="when is Missing"><i class="fa fa-times"></i></td>*#
<!-- /ko -->
</tr>
<!-- /ko -->
</tbody>
<script type="text/javascript">
function feature(id, mandatory) {
var self = this;
self.id = id;
self.mandatory = ko.observable(mandatory);
}
function company(name, features) {
var self = this;
self.name = name;
self.features = ko.observableArray(features);
}
var viewModel = function () {
self.companies = ko.observableArray(
[
new company("Company 1", [
new feature(1, true),
new feature(2, true),
new feature(3, true),
new feature(8, false),
new feature(9, false)
]),
new company("Company 2", [
new feature(1, true),
new feature(3, true),
new feature(7, false),
new feature(8, false)
]),
new company("Company 3", [
new feature(3, true),
new feature(4, true),
new feature(9, false),
new feature(10, false)
]),
]);
self.UnionFeaturesIds = ko.computed(function () {
return 0; //????;
});
}
ko.applyBindings(new viewModel());
You're missing var self = this inside your viewModel.
I've had a quick play with this, and I decided to do it in slightly different way. Feel free to change any of my code, I'm just having fun ;p
I've produced an array of all features, which I can always reference. Then I created a new helper logic inside each company that will loop through all features.
If a given feature is found, it's then added, otherwise we create a dummy object that we know is a missing feature (I've added ID of -1)
var allFeatures = ko.observableArray();
function feature(id, mandatory) {
var self = this;
self.id = id;
self.mandatory = ko.observable(mandatory);
}
function company(name, features) {
var self = this;
self.name = name;
self.features = ko.observableArray(features);
}
var viewModel = function() {
var self = this;
self.companies = ko.observableArray(
[
new company("Company 1", [
new feature(1, true),
new feature(2, true),
new feature(3, true),
new feature(8, false),
new feature(9, false)
]),
new company("Company 2", [
new feature(1, true),
new feature(3, true),
new feature(7, false),
new feature(8, false)
]),
new company("Company 3", [
new feature(3, true),
new feature(4, true),
new feature(9, false),
new feature(10, false)
])
]);
self.setFeatures = function(features) {
var featuresToChange = features;
var tempFeatures = [];
// loop through all features, so we can create all rows in the HTML
for (var i = 0; i < allFeatures().length; i++) {
var currentFeature = featuresToChange()[i];
// see if current feature exists in a given company
var featureWithIdFound = ko.utils.arrayFirst(featuresToChange(), function(item) {
return item.id === allFeatures()[i];
});
// if the feature was found, and we are currently looping through its ID, push it to temporary array
if (featureWithIdFound !== null && featureWithIdFound.id === allFeatures()[i]) {
tempFeatures.push(featureWithIdFound);
} else {
// otherwise push a feature that's missing, by giving it's negative ID
tempFeatures.push(new feature(-1));
}
}
// push to existing features array in that company
featuresToChange(tempFeatures);
}
self.createAllFeaturesList = function() {
var _allFeatures = [];
// loop through all companies to get unique features
for (var i = 0; i < self.companies().length; i++) {
var curCompany = self.companies()[i];
// push all unique items to temporary array called _allFeatures
ko.utils.arrayFirst(curCompany.features(), function(item) {
if (_allFeatures.indexOf(item.id) < 0 && item.id !== -1) {
// only push items that don't exist in the array, so we don't end up with duplicated
_allFeatures.push(item.id);
}
});
}
// sort IDs
_allFeatures.sort(function(a, b) {
return a > b ? 1 : -1
});
allFeatures(_allFeatures);
ko.utils.arrayForEach(self.companies(), function(item) {
// apply them to table
self.setFeatures(item.features);
});
};
// instantiate features
self.createAllFeaturesList();
}
ko.applyBindings(new viewModel());
.fa-check {
color: green;
}
.fa-times {
color: red;
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.1/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<table>
<thead>
<tr data-bind="foreach: companies">
<th data-bind="text: name"></th>
</tr>
</thead>
<tbody>
<tr data-bind="foreach: companies">
<td>
<table>
<tbody data-bind="foreach: features">
<tr>
<td>
<i class="fa fa-check" data-bind="visible: mandatory"></i>
<input type="checkbox" data-bind="visible: !mandatory() && id > 0"/>
<i class="fa fa-times" data-bind="visible: id < 0"></i>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
I hope that this helps.

Insert multiple rows and columns to a table in html dynamically using javascript code

I am trying to insert multiple rows and columns to create a table in html dynamically by selecting the number of rows and columns in dropdown list using javascript code like in MS Word.
For example if I select number of rows as 5 and number of columns as 5 from the dropdown list of numbers. 5 rows and 5 columns should get displayed.
My question is how can I add multiple rows and columns dynamically to create a table by selecting the number of rows and columns from the drop down list.
Since, <table> element is the one of the most complex structures in HTML, HTML DOM provide new interface HTMLTableElement with special properties and methods for manipulating the layout and presentation of tables in an HTML document.
So, if you want to accomplish expected result using DOM standards you can use something like this:
Demo old
Demo new
HTML:
<ul>
<li>
<label for="column">Add a Column</label>
<input type="number" id="column" />
</li>
<li>
<label for="row">Add a Row</label>
<input type="number" id="row" />
</li>
<li>
<input type="button" value="Generate" id="btnGen" />
<input type="button" value="Copy to Clipboard" id="copy" />
</li>
</ul>
<div id="wrap"></div>
JS new:
JavaScript was improved.
(function (window, document, undefined) {
"use strict";
var wrap = document.getElementById("wrap"),
setColumn = document.getElementById("column"),
setRow = document.getElementById("row"),
btnGen = document.getElementById("btnGen"),
btnCopy = document.getElementById("btnCopy");
btnGen.addEventListener("click", generateTable);
btnCopy.addEventListener("click", copyTo);
function generateTable(e) {
var newTable = document.createElement("table"),
tBody = newTable.createTBody(),
nOfColumns = parseInt(setColumn.value, 10),
nOfRows = parseInt(setRow.value, 10),
row = generateRow(nOfColumns);
newTable.createCaption().appendChild(document.createTextNode("Generated Table"));
for (var i = 0; i < nOfRows; i++) {
tBody.appendChild(row.cloneNode(true));
}
(wrap.hasChildNodes() ? wrap.replaceChild : wrap.appendChild).call(wrap, newTable, wrap.children[0]);
}
function generateRow(n) {
var row = document.createElement("tr"),
text = document.createTextNode("cell");
for (var i = 0; i < n; i++) {
row.insertCell().appendChild(text.cloneNode(true));
}
return row.cloneNode(true);
}
function copyTo(e) {
prompt("Copy to clipboard: Ctrl+C, Enter", wrap.innerHTML);
}
}(window, window.document));
JS old:
(function () {
"use strict";
var wrap = document.getElementById("wrap"),
setColumn = document.getElementById("column"),
setRow = document.getElementById("row"),
btnGen = document.getElementById("btnGen"),
copy = document.getElementById("copy"),
nOfColumns = -1,
nOfRows = -1;
btnGen.addEventListener("click", generateTable);
copy.addEventListener("click", copyTo);
function generateTable(e) {
var newTable = document.createElement("table"),
caption = newTable.createCaption(),
//tHead = newTable.createTHead(),
//tFoot = newTable.createTFoot(),
tBody = newTable.createTBody();
nOfColumns = parseInt(setColumn.value, 10);
nOfRows = parseInt(setRow.value, 10);
caption.appendChild(document.createTextNode("Generated Table"));
// appendRows(tHead, 1);
// appendRows(tFoot, 1);
appendRows(tBody);
(wrap.hasChildNodes() ? wrap.replaceChild : wrap.appendChild).call(wrap, newTable, wrap.firstElementChild);
}
function appendColumns(tElement, count) {
var cell = null,
indexOfRow = [].indexOf.call(tElement.parentNode.rows, tElement) + 1,
indexOfColumn = -1;
count = count || nOfColumns;
for (var i = 0; i < count; i++) {
cell = tElement.insertCell(i);
indexOfColumn = [].indexOf.call(tElement.cells, cell) + 1;
cell.appendChild(document.createTextNode("Cell " + indexOfColumn + "," + indexOfRow));
}
}
function appendRows(tElement, count) {
var row = null;
count = count || nOfRows;
for (var i = 0; i < count; i++) {
row = tElement.insertRow(i);
appendColumns(row);
}
}
function copyTo(e) {
prompt("Copy to clipboard: Ctrl+C, Enter", wrap.innerHTML);
}
}());
If you want to copy generated result to clipboard you can look at answer of Jarek Milewski - How to copy to the clipboard in JavaScript?
You can use this function to generate dynamic table with no of rows and cols you want:
function createTable() {
var a, b, tableEle, rowEle, colEle;
var myTableDiv = document.getElementById("DynamicTable");
a = document.getElementById('txtRows').value; //No of rows you want
b = document.getElementById('txtColumns').value; //No of column you want
if (a == "" || b == "") {
alert("Please enter some numeric value");
} else {
tableEle = document.createElement('table');
for (var i = 0; i < a; i++) {
rowEle = document.createElement('tr');
for (var j = 0; j < b; j++) {
colEle = document.createElement('td');
rowEle.appendChild(colEle);
}
tableEle.appendChild(rowEle);
}
$(myTableDiv).html(tableEle);
}
}
Try something like this:
var
tds = '<td>Data'.repeat(col_cnt),
trs = ('<tr>'+tds).repeat(row_cnt),
table = '<table>' + trs + '</table>;
Then place the table in your container:
document.getElementById('tablePreviewArea').innerHTML = table;
Or with JQuery:
$('#tablePreviewArea').html(table);
Here is the JSFiddle using native js.
Here is the JSFiddle using jQuery.
About the string repeat function
I got the repeat function from here:
String.prototype.repeat = function( num )
{
return new Array( num + 1 ).join( this );
}
I had one sample code...try this and modify it according to your requirement. May it helps you out.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Untitled Document</title>
<style type="text/css">
#mytab td{
width:100px;
height:20px;
background:#cccccc;
}
</style>
<script type="text/javascript">
function addRow(){
var root=document.getElementById('mytab').getElementsByTagName('tbody')[0];
var rows=root.getElementsByTagName('tr');
var clone=cloneEl(rows[rows.length-1]);
root.appendChild(clone);
}
function addColumn(){
var rows=document.getElementById('mytab').getElementsByTagName('tr'), i=0, r, c, clone;
while(r=rows[i++]){
c=r.getElementsByTagName('td');
clone=cloneEl(c[c.length-1]);
c[0].parentNode.appendChild(clone);
}
}
function cloneEl(el){
var clo=el.cloneNode(true);
return clo;
}
</script>
</head>
<body>
<form action="">
<input type="button" value="Add a Row" onclick="addRow()">
<input type="button" value="Add a Column" onclick="addColumn()">
</form>
<br>
<table id="mytab" border="1" cellspacing="0" cellpadding="0">
<tr>
<td> </td>
<td> </td>
</tr>
<tr>
<td> </td>
<td> </td>
</tr>
<tr>
<td> </td>
<td> </td>
</tr>
<tr>
<td> </td>
<td> </td>
</tr>
</table>
</body>
</html>
Instead of button , you can use select menu and pass the value to variable. It will create row ,column as per the value.

How to filter using a dropdown in Knockout

I have just started using Knockout and I want to filter my data that I am displaying in my UI by selecting an item from a dropdown. I have got so far, but I cannot get the selected value from my dropdown yet, and then after that I need to actually filter the data displayed based upon that value. Here is my code so far :
#model Models.Fixture
#{
ViewBag.Title = "Fixtures";
Layout = "~/Areas/Development/Views/Shared/_Layout.cshtml";
}
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript" src="#Url.Content("~/Scripts/knockout-2.1.0.js")"></script>
<script type="text/javascript">
function FixturesViewModel() {
var self = this;
var baseUri = '#ViewBag.ApiUrl';
self.fixtures = ko.observableArray();
self.teams = ko.observableArray();
self.update = function (fixture) {
$.ajax({ type: "PUT", url: baseUri + '/' + fixture.Id, data: fixture });
};
self.sortByAwayTeamScore = function () {
this.fixtures.sort(function(a, b)
{ return a.AwayTeamScore < b.AwayTeamScore ? -1 : 1; });
};
self.select = function (team) {
};
$.getJSON("/api/fixture", self.fixtures);
$.getJSON("/api/team", self.teams);
}
$(document).ready(function () {
ko.applyBindings(new FixturesViewModel());
});
</script>
<div class="content">
<div>
<table><tr><td><select data-bind="options: teams, optionsText: 'TeamName', optionsCaption: 'Select...', optionsValue: 'TeamId', click: $root.select"> </select></td></tr></table>
<table class="details ui-widget-content">
<thead>
<tr><td>FixtureId</td><td>Season</td><td>Week</td><td>AwayTeam</td><td><a id="header" data-bind='click: sortByAwayTeamScore'>AwayTeamScore</a></td><td>HomeTeam</td><td>HomeTeamScore</td></tr>
</thead>
<tbody data-bind="foreach: fixtures">
<tr>
<td><span data-bind="text: $data.Id"></span></td>
<td><span data-bind="text: $data.Season"></span></td>
<td><span data-bind="text: $data.Week"></span></td>
<td><span data-bind="text: $data.AwayTeamName"></span></td>
<td><input type="text" data-bind="value: $data.AwayTeamScore"/></td>
<td><span data-bind="text: $data.HomeTeamName"></span></td>
<td><input type="text" data-bind="value: $data.HomeTeamScore"/></td>
<td><input type="button" value="Update" data-bind="click: $root.update"/></td>
</tr>
</tbody>
</table>
</div>
</div>
EDIT : Figured this out :
<script type="text/javascript">
function FixturesViewModel() {
var self = this;
var baseUri = '#ViewBag.ApiUrl';
self.fixtures = ko.observableArray();
self.teams = ko.observableArray();
self.TeamName = ko.observable('');
self.filteredItems = ko.computed(function () {
var TeamName = self.TeamName();
if (!TeamName || TeamName == "None") {
return self.fixtures();
} else {
return ko.utils.arrayFilter(self.fixtures(), function (i) {
return i.AwayTeamName == TeamName;
});
}
});
self.update = function (fixture) {
$.ajax({ type: "PUT", url: baseUri + '/' + fixture.Id, data: fixture });
};
self.sortByAwayTeamScore = function () {
this.fixtures.sort(function(a, b)
{ return a.AwayTeamScore < b.AwayTeamScore ? -1 : 1; });
};
$.getJSON("/api/fixture", self.fixtures);
$.getJSON("/api/team", self.teams);
}
$(document).ready(function () {
ko.applyBindings(new FixturesViewModel());
});
<select data-bind="options: teams, optionsText: 'TeamName', optionsCaption: 'Select...', optionsValue: 'TeamName', value:TeamName"> </select>
Filtering in knockout is generally done with a computed observable. Here is a basic ViewModel that could filter based on a dropdown of types (including a filter option for "none").
var ViewModel = function(data) {
var self = this;
self.filters = ko.observableArray(data.filters);
self.filter = ko.observable('');
self.items = ko.observableArray(data.items);
self.filteredItems = ko.computed(function() {
var filter = self.filter();
if (!filter || filter == "None") {
return self.items();
} else {
return ko.utils.arrayFilter(self.items(), function(i) {
return i.type == filter;
});
}
});
};
Here is that code in a fiddle, so you can play with it.

Categories

Resources