How to increase quantity of array item with Knockout - javascript

I've got two tables. Left side containing all items, right side showing the ordered items. (See picture)
What I want to do:
If an item from the left table is already added on the right table, instead of adding it again, just increase the quantity by one instead. If that's not the case, just add it..
Code:
HTML
<form id="OrderedItemsWithoutBatch" class="orderedDataWithoutBatch">
<table class="orderFormArticlesTable" style="width: 47%;float: right; font-size: 9pt;">
<thead>
<tr>
<th>SKU</th>
<th>Product</th>
<th style="width: 15%">Qty</th>
<th></th>
</tr>
</thead>
<tbody id="orderedItemsVM" data-bind="foreach: orderedItems">
<tr class="clickable" data-bind="css: { alternate: $index()%2 }">
<td data-bind="text: MateriaalSku"> </td>
<td data-bind="text: MateriaalDescription"> </td>
#*<td class="onelineData"><input style="width:2em" type="text" /> [pieces] </td>*#
<td><input class="orderedQty" style="max-width: 15%" data-bind="value: materiaalAantal"/>[pieces]</td>
<td data-bind="click: function() { $parent.orderedItems.remove($data); }">Remove</td>
</tr>
</tbody>
</table>
JS
$(document).ready(function () {
var dataTableForm = $("form.dataWithoutBatch");
var orderedItemsTable = $("form.orderedDataWithoutBatch");
var dataReloading = false;
var currentPage = 0;
//viewModel
var viewModel = {
orderedItems: ko.observableArray(),
items: ko.observableArray(),
sortColumn: ko.observable(),
total: ko.observable()
}
var request = $.post(url, postData);
request.fail(function () {
alert('Failed!');
});
request.always(function () {
listItemsParameterInputs.prop('disabled', false);
$(document.body).removeClass("loading");
dataReloading = false;
if (callback) {
callback();
}
});
request.done(function (data) {
viewModel.total(data.Total);
viewModel.items.removeAll();
for (var index = 0; index < data.Items.length; index++) {
var item = data.Items[index];
if (item.hasOwnProperty("qty")) {
item.qty = ko.observable(item.qty);
}
else {
item.qty = ko.observable("");
}
item.addItem = function () {
//Check if item is already in the table on the right
if(viewModel.orderedItems.indexOf(this) < 0){
viewModel.orderedItems.push(this);
}
else{
// Increase quantity by one.
}
}
viewModel.items.push(item);
}

Instead of using two arrays and removing elements from the second, why don't you use the same array, just add qty variable and show lines only if they satisfy qty()>0
That way, you could just increment the value of qty by one in the first table with this.qty(this.qty()+1); and set the value to 0 in the second when clicking on remove.
Code for your second table should look something like this:
<tbody id="orderedItemsVM" data-bind="foreach: items">
<!-- ko if: qty()>0 -->
<tr class="clickable" >
<td data-bind="text: MateriaalSku"> </td>
<td data-bind="text: MateriaalDescription"> </td>
<td>
<input style="width:2em" type="text" data-bind="value:qty"/>
[pieces]
</td>
<td><a data-bind="click: function() { qty(""); }">Remove</a></td>
</tr>
<!-- /ko -->
</tbody>
more on the if binding at http://knockoutjs.com/documentation/if-binding.html

Cant you just increment the quantity of your observable as follows:
...
else {
// Increase quantity by one.
this.qty(this.qty() += 1)
}

Related

AngularJs - Populate the tables by checking header names

I have two objects one contains selected records and another contains selected fields. Now I need to populate the table dynamically by checking the column header name or value.
I referred Populate table with ng-repeat and check matching header data
Completed:
I able to add the headers dynamically to the table/columns.
Working code is in Plunker
HTML
<table class="dataTable no-footer leadTable">
<thead>
<tr role="row">
<th>
<input type="checkbox" ng-model="selectedSObject[key]" class="select_tertiary_selectAll" />
</th>
<th ng-repeat="k in sFields[key]" ng-value="{{k.name}}" style="font-weight: 400; border-right: none; background-color: #0070d2; padding: 10px 18px;">
<div> {{k.label}} </div>
</th>
</tr>
</thead>
<tbody>
<tr role="row" ng-class="odd" ng-repeat="record in value.filtered">
<td>
<input type="checkbox" ng-model="record.checked" />
</td>
<td ng-repeat="(key_1, value_1) in record" style="padding: 10px 18px;">
<div> {{value_1}} </div>
</td>
</tr>
</tbody>
</table>
JS
$scope.sFields = getSFields($scope.sObjectCSVData);
$scope.iterableRecords = getiterableRecords($scope.sObjectCSVData, allRecords)
function getiterableRecords(sCSVData, allRecords) {
var sObjectRecords = {};
if (allRecords) {
Object.entries(sCSVData).forEach(function(key) {
if (sCSVData[key[0]].filtered.length != 0) {
if (sObjectRecords[key[0]]) {
sObjectRecords[key[0]].push(sCSVData[key[0]].filtered)
} else {
sObjectRecords[key[0]] = []
sObjectRecords[key[0]].push(sCSVData[key[0]].filtered)
}
}
});
}
return sObjectRecords
}
function getSFields(sCSVData) {
var fields = {};
Object.entries(sCSVData).forEach(function(key) {
if (sCSVData[key[0]].filtered.length != 0) {
for (var i = 0; i < sCSVData[key[0]].sObjectFields.length; i++) {
if (fields[key[0]]) {
fields[key[0]].push(sCSVData[key[0]].sObjectFields[i])
} else {
fields[key[0]] = []
fields[key[0]].push(sCSVData[key[0]].sObjectFields[i])
}
}
}
});
return fields
}
Need to do:
Now I have to populate the table with the respective values by checking the column name.
I'm working in AngularJS 1.6.9
By adding small changes, Now I can able to populate the table,
The code I followed is as below,
<tbody>
<tr role="row" ng-class="odd" ng-repeat="record in value[0]">
<td>
<input type="checkbox" ng-model="record.checked" />
</td>
<td ng-repeat="k in sFields[key]" style="padding: 10px 18px;">
{{record [k.name]}} </div>
</td>
</tr>
The working code is in the below mentioned plunker,
Populate the table by checking the header name
For ref.

Knockout.js button only visible on maximum index of observableArray

I have a table where I want the last row to to display a button, but only on the last row. The code below works on showing and hiding the button, but it appears on every row.
How would I go about only displaying it on the last row?
Viewmodel:
var loadCustomFileName = function () {
for (i = 0; i < self.GetCanSeam().length; i++) {
var obj = {
appendFileName: parseFileName(i),
displayFileName: parseDisplayName(i)
if (i == self.GetCanSeam().length - 1) {
self.isMax(true);
}
};
self.GetCustomFile.push(obj);
}
};
View:
<div class="csFormField" data-bind="visible: GetCustomFile().length > 0">
<table style="width: 100%;">
<thead>
<tr>
<th>File Name Template</th>
<th>File Name Append</th>
</tr>
</thead>
<tbody data-bind='foreach: GetCustomFile()'>
<tr>
<td><p class="cs-label"><label data-bind="text: displayFileName" /></p></td>
<td><p><input class="cs-input" data-bind="textInput: appendFileName" />
<button class="addFormat" data-bind="visible: isMax">+</button></p></td>
</tr>
</tbody>
</table>
</div>
I think your isMax is initialized on parent context. Maybe try this
var obj = {
appendFileName: parseFileName(i),
displayFileName: parseDisplayName(i)
};
obj.isMax = ko.observable(i==(self.GetCanSeam().length - 1));
self.GetCustomFile.push(obj);
You can compare the index of your forloop. If it gets to the last element then visible the button.
Example :https://jsfiddle.net/kyr6w2x3/12/
self.ArrayLength = ko.observable()
self.ArrayLength(self.GetCustomFile().length);
HTML :
<button class="addFormat" data-bind="visible: $index() == $parent.ArrayLength() -1">+</button></p></td>

Javascript: Delete a specific table cell when it is clicked

I was working on a university project. They told us to make 2 arrays. The first will have 3 cells with 3 images, and the second will be empty with 1 row.
I need to remove the image from the cell clicked each time in the first table and copy it to the second table!
My problem is that deleteCell() function will only delete the first element each time. I don't know how to delete the CLICKED cells from my table row!
My JS:
var table1 = document.getElementById("myTable");
var table2 = document.getElementById("myTable2");
function DL1() {
var row = document.getElementById("myRow1");
row.deleteCell();
}
function CR2() {
var row = document.getElementById("myRow2");
}
My HTML:
<table id="myTable" class="auto-style1">
<tr id="myRow1">
<td onclick="DL1()"><img src="../../2.jpg" /></td>
<td onclick="DL1()"><img src="../../1.gif" /></td>
<td onclick="DL1()"><img src="../../3.png" /></td>
</tr>
</table>
<table id="my2Table">
<tr id="myRow2"></tr>
</table>
var table1=document.getElementById("myTable");
var table2=document.getElementById("myTable2");
function DL1(elem){
var row = document.getElementById("myRow1");
for(i=0;i<row.children.length;i++) {
if(row.children[i]==elem) {
row.deleteCell(i);
row2=document.getElementById("myRow2");
row2.appendChild(elem);
}
}
}
<td onclick="DL1(this)"><img src="http://placehold.it/100x100"/></td>
<td onclick="DL1(this)"><img src="http://placehold.it/150x100"/></td>
<td onclick="DL1(this)"><img src="http://placehold.it/200x100"/></td>
Demo: https://jsfiddle.net/Lt2cyw0g/2/
So, you need to get index of clicked element (pass it to the function, and check index, and use it in deleteCell() function), then add element to the second table row...
Just pass clicked element to the function:
var table1 = document.getElementById("myTable");
var table2 = document.getElementById("myTable2");
function DL1(td) {
td.parentNode.removeChild(td);
}
function CR2() {
var row = document.getElementById("myRow2");
}
<table id="myTable" class="auto-style1">
<tr id="myRow1">
<td onclick="DL1(this)">
<img src="../../2.jpg" />
</td>
<td onclick="DL1(this)">
<img src="../../1.gif" />
</td>
<td onclick="DL1(this)">
<img src="../../3.png" />
</td>
</tr>
</table>
<table id="my2Table">
<tr id="myRow2"></tr>
</table>
Hope it helps, no need ID:
var a = document.querySelectorAll("table tr");
for(var b in a){
var c = a[b];
if(typeof c == "object"){
c.onclick = function (){
this.offsetParent.deleteRow(this.rowIndex);
}
}
}
<table >
<tr><td>1</td><td>2</td><td>3</td></tr>
<tr><td>1a</td><td>2a</td><td>3a</td></tr>
<tr><td>1b</td><td>2b</td><td>b</td></tr>
</table>
<table>
<tr><td>a</td><td>aa</td><td>aa</td></tr>
<tr><td>b</td><td>bb</td><td>bb</td></tr>
<tr><td>c</td><td>cc</td><td>cc</td></tr>
</table>

Accessing child array by index and updating view when index increments

I have been working on a sample Knockout.js application where there is a list of offices and each office is associated with 1 or more sets of opening hours.
When I click on select in the table the opening hours are shown underneath for the first set of opening hours.
I want to be able to cycle through the array by clicking on the button to show the opening hours for Rota A, then B, then C and back to Rota A again.
With my current solution, if I press on the >> button currently the index is incremented but the opening hours table is not updated until I re-press the select button.
The jsfiddle is here https://jsfiddle.net/58p57dv3/
How, do I make the opening hours in the table update automatically when I press the >>> button
HTML
<div id="container">
<div id="officeInfo">
<table>
<thead>
<tr>
<th>Office ID</th>
<th>Office Name</th>
</tr>
</thead>
<tbody data-bind="foreach: offices">
<tr>
<td data-bind="text: OfficeID"></td>
<td data-bind="text: OfficeName"></td>
<td>Select</td>
</tr>
</tbody>
</table>
</div>
<div id="openingHours" data-bind="with: selectedOffice">
<input type="button" value=">>>" data-bind="click: $parent.nextOpeningHourType">
<table>
<thead>
<tr>
<th>Day</th>
<th>Opening Time</th>
<th>Closing Time</th>
</tr>
</thead>
<tbody data-bind="with: OpeningHours()[$parent.index]">
<tr><td>Monday</td><td data-bind="text: MondayStartTime"></td><td data-bind="text: MondayEndTime"></tr>
<tr><td>Tueday</td><td data-bind="text: TuesdayStartTime"></td><td data-bind="text: TuesdayEndTime"></tr>
<tr><td>Wednesday</td><td data-bind="text: WednesdayStartTime"></td><td data-bind="text: WednesdayEndTime"></tr>
<tr><td>Thursday</td><td data-bind="text: ThursdayStartTime"></td><td data-bind="text: ThursdayEndTime"></tr>
<tr><td>Friday</td><td data-bind="text: FridayStartTime"></td><td data-bind="text: FridayEndTime"></tr>
<tr><td>Saturday</td><td data-bind="text: SaturdayStartTime"></td><td data-bind="text: SaturdayEndTime"></tr>
<tr><td>Sunday</td><td data-bind="text: SundayStartTime"></td><td data-bind="text: SundayEndTime"></tr>
</tbody>
</table>
</div>
</div>
JS
var OfficeViewModel = function() {
var self = this;
self.offices = officeList;
self.selectedOffice = ko.observable();
self.index = ko.observable();
self.index = 0;
self.nextOpeningHourType = function() {
if (self.index < 2) {
self.index++;
} else {
self.index = 0;
}
}
self.selectOffice = function (office) {
self.selectedOffice(office);
}
}
I guess it will work fine if you just make index an observable
self.index = ko.observable(0);
self.nextOpeningHourType = function() {
if (self.index() < 2) {
self.index(self.index()+1);
} else {
self.index(0);
}
}
self.selectOffice = function (office) {
self.index(0);
self.selectedOffice(office);
}
and change your markup as
<tbody data-bind="with: OpeningHours()[$parent.index()]">
JsFiddle: https://jsfiddle.net/newuserjs/58p57dv3/2/

How can I get the corresponding table column (td) from a table header (th)?

I want to get the entire column of a table header.
For example, I want to select the table header "Address" to hide the address column, and select the "Phone" header to show the correspondent column.
<table>
<thead>
<tr>
<th id="name">Name</th>
<th id="address">Address</th>
<th id="address" class='hidden'>Address</th>
</tr>
</thead>
<tbody>
<tr>
<td>Freddy</td>
<td>Nightmare Street</td>
<td class='hidden'>123</td>
</tr>
<tr>
<td>Luis</td>
<td>Lost Street</td>
<td class='hidden'>3456</td>
</tr>
</tbody>
I want to do something like http://www.google.com/finance?q=apl (see the related companies table) (click the "add or remove columns" link)
Something like this would work -
$('th').click(function() {
var index = $(this).index()+1;
$('table td:nth-child(' + index + '),table th:nth-child(' + index + ')').hide()
});
The code above will hide the relevant column if you click on the header, the logic could be changed to suit your requirements though.
Demo - http://jsfiddle.net/LUDWQ/
With a couple simple modifications to your HTML, I'd do something like the following (framework-less JS):
HTML:
<input class="chk" type="checkbox" checked="checked" data-index="0">Name</input>
<input class="chk" type="checkbox" checked="checked" data-index="1">Address</input>
<input class="chk" type="checkbox" checked="checked" data-index="2">Phone</input>
<table id="tbl">
<thead>
<tr>
<th>Name</th>
<th>Address</th>
<th>Phone</th>
</tr>
</thead>
<tbody>
<tr>
<td>Freddy</td>
<td>Nightmare Street</td>
<td>123</td>
</tr>
<tr>
<td>Luis</td>
<td>Lost Street</td>
<td>3456</td>
</tr>
</tbody>
Javascript:
var cb = document.getElementsByClassName("chk");
var cbsz = cb.length;
for(var n = 0; n < cbsz ; ++n) {
cb[n].onclick = function(e) {
var idx = e.target.getAttribute("data-index");
toggleColumn(idx);
}
}
function toggleColumn(idx) {
var tbl = document.getElementById("tbl");
var rows = tbl.getElementsByTagName("tr");
var sz = rows.length;
for(var n = 0; n < sz; ++n) {
var el = n == 0 ? rows[n].getElementsByTagName("th")[idx] : rows[n].getElementsByTagName("td")[idx];
el.style.display = el.style.display === "none" ? "table-cell" : "none";
}
}
http://jsfiddle.net/dbrecht/YqUNz/1/
I added the checkboxes as it doesn't make sense to bind the click to the column headers as you won't be able to toggle the visibility, only hide them.
You can do something with CSS, like:
<html>
<head>
<style>
.c1 .c1, .c2 .c2, .c3 .c3{
display:none;
}
</style>
</head>
<body>
<table class="c2 c3">
<thead>
<tr>
<th id="name" class="c1">Name</th>
<th id="address" class="c2">Address</th>
<th id="phone" class="c3">Phone</th>
</tr>
</thead>
<tbody>
<tr>
<td class="c1">Freddy</td>
<td class="c2">Nightmare Street</td>
<td class="c3">123</td>
</tr>
<tr>
<td class="c1">Luis</td>
<td class="c2">Lost Street</td>
<td class="c3">3456</td>
</tr>
</tbody>
</table>
</body>
</html>
To hide a column, you add with Javascript the corresponding class to the table. Here c2 and c3 are hidden.
You could add dynamically the .c1, .c2,... in a style tag, or define a maximum number.
The easiest way to do this would be to add a class to each td that matches the class of the header. When you click the , it checks the class, then hides every td with that class. Since only the s in that column would hide that class, it would effectively hide the column.
<table>
<thead>
<th>Name</th>
<th>Address</th>
</thead>
<tbody>
<tr>
<td class="Name">Joe</td>
<td class="Address">123 Main St.
</tbody>
</table>
And the script something like:
$('th').click( function() {
var col = $(this).html(); // Get the content of the <th>
$('.'+col).hide(); // Hide everything with a class that matches the col value.
});
Something like that, anyway. That's probably more verbose than it needs to be, but it should demonstrate the principle.
Another way would be to simply count how many columns over the in question is, and then loop through each row and hide the td that is also that many columns over. For instance, if you want to hide the Address column and it is column #3 (index 2), then you would loop through each row and hide the third (index 2).
Good luck..
Simulating the Google Finance show/hide columns functionality:
http://jsfiddle.net/b9chris/HvA4s/
$('#edit').click(function() {
var headers = $('#table th').map(function() {
var th = $(this);
return {
text: th.text(),
shown: th.css('display') != 'none'
};
});
var h = ['<div id=tableEditor><button id=done>Done</button><table><thead><tr>'];
$.each(headers, function() {
h.push('<th><input type=checkbox',
(this.shown ? ' checked ' : ' '),
'/> ',
this.text,
'</th>');
});
h.push('</tr></thead></table></div>');
$('body').append(h.join(''));
$('#done').click(function() {
var showHeaders = $('#tableEditor input').map(function() { return this.checked; });
$.each(showHeaders, function(i, show) {
var cssIndex = i + 1;
var tags = $('#table th:nth-child(' + cssIndex + '), #table td:nth-child(' + cssIndex + ')');
if (show)
tags.show();
else
tags.hide();
});
$('#tableEditor').remove();
return false;
});
return false;
});
jQuery('thead td').click( function () {
var th_index = jQuery(this).index();
jQuery('#my_table tbody tr').each(
function(index) {
jQuery(this).children('td:eq(' + th_index + ');').each(
function(index) {
// do stuff here
}
);
}
);
});
here's a working fiddle of this behaviour:
http://jsfiddle.net/tycRW/
of course, hiding the column with out hiding the header for it will have some strange results.

Categories