Knockout JS: Validation of Textboxes inside the table for-each - javascript

I have html table whose columns are dynamically generated, where each column is a textbox. I am struggling to work out is how to validate these textboxes.
Here is my jsfiddle:
https://jsfiddle.net/aman1981/4djn2zee/
So on the save button click I want to validate user input.
The requirement is:
I want to make all but one column required field.
I want to apply regex to certain columns (based on column name) as some columns are chars only whereas some only numbers.
Here is my construct of the texbox:
<tbody data-bind="foreach: { data: valuesData, as: 'rowData'}">
<tr data-bind="foreach: { data: $parent.columns, as: 'column' }">
<!-- NEW: bind to the corresponding property/observable in ValuesData -->
<td><input type="text" class="form-control textbox" data-bind="textInput: rowData[column.Property]" /> </td>
</tr>
<tr>
<td>
<input type="button" value="Remove Row" data-bind="click: $parent.removeRow" class="btn btn-danger" />
</td>
</tr>
</tbody>
Its a little complex requirement and have been struggling to gets head up on this.
Any pointers to this ?
Anyone/??

Related

Accessing an individual column in a row which is selected by default with a radio button on page load

With the code below, I am trying to access a particular column "quantity" from a row in a table. What is happening is one of the rows is selected by default when page loads while the rest of the rows can be selected when user chooses. I created a click event handler to handle manual selection.
When accessing the column with a class name, it returns nothing. I need to assign this value to an input box in the same form. I would attach the image of the row
Table Markup:
<tr valign="top" class="row6">
<td>
{if $tpl_order_details[lineitems].quantity > 1}
{if $radio_flag == "false"}
<input type="radio" name="line_item" class="radio_class" id="line_item" value="{$tpl_order_details[lineitems].mSku}" checked onclick="handleClick(this);"/>
{assign var=radio_flag value='true'}
{else}
<input type="radio" name="line_item" class="radio_class" id="line_item" value="{$tpl_order_details[lineitems].mSku}" onclick="handleClick(this);" />
{/if}
{/if}
</td>
<td>
{$tpl_order_details[lineitems].sku}
</td>
<td>
</td>
<td>{$tpl_order_details[lineitems].item_description}</td>
<td class="quantity_class" >{$tpl_order_details[lineitems].quantity}</td>
<td>{$tpl_order_details[lineitems].item_status}</td>
Markup with the Input field outside the loop:
<table>
<tr>
<td><label for="new_quantity">Enter New Quantity</label></td>
<td><input type="number" id="split_quantity" name="split_quantity"
min="1" max="6"></td>
<td><button type="submit" value="Save"
name="submit_action">Submit</button></td>
<td><button type="submit" value="Cancel"
name="submit_action">Cancel</button></td>
</tr>
</table>
JavaScript:
// This is to handle the radio button selected by default on page load.
$( document ).ready(function() {
var firstRadioValue = 0;
firstRadioValue = $("input[name='line_item']:checked").val();
$('input[name="split_quantity"]').attr('max', firstRadioValue);
var quantity = $(".radio_class").parent().find(".quantity_class").val();
alert(quantity);
});
// This is to handle the radio button that user actually chooses.
var currentRadioValue = 0;
function handleClick(line_item) {
alert('New value: ' + line_item.value);
currentRadioValue = line_item.value;
$('input[name="split_quantity"]').attr('max', currentRadioValue);
}
You're not going far enough up the tree to find the class. You have:
var quantity = $(".radio_class").parent().find(".quantity_class").val();
which gets you to the parent <td> The element you're looking for is a sibling of this:
<td class="quantity_class" >...
What you want to do is go one element higher (the table row), then find the class you're looking for from there, so use closest(). Note that .quantity_class doesn't have a value so you have to get the text in the table cell:
var quantity = $(".radio_class").closest('tr').find(".quantity_class").text();
In addition, I do not see any markup with the max attribute or any markup with the name of split_quantity.
EDIT - based on a conversation with the user it was found that there needed to be a number of changes. First, the table holding split_quantity needed to be identified so it could be targeted in the grander markup:
<table id="split_quantity_id">
<tr>
<td><label for="new_quantity">Enter New Quantity</label></td>
<td><input type="number" id="split_quantity" name="split_quantity" min="1" max="6"></td>
<td><button type="submit" value="Save" name="submit_action">Submit</button></td>
<td><button type="submit" value="Cancel" name="submit_action">Cancel</button></td>
</tr>
</table>
Then we got rid of the onclick="handleClick(this) inline JavaScript in favor of letting jQuery handle the click event. Finally we refactored the functions:
$(function() {
var firstRadioValue = 0;
firstRadioValue = $("input[name='line_item']:checked").closest('tr').find('.quantity_class').text();
$('input[name="split_quantity"]').attr('max', firstRadioValue);
var quantity = $(".radio_class").closest('tr').find(".quantity_class").text();
console.log(quantity);
$('table').delegate('.line_item', 'click', function(){
currentRadioValue = $(this).closest('tr').find('.quantity_class').text();
console.log(currentRadioValue);
$('#split_quantity_id').find('[name="split_quantity"]').attr('max', currentRadioValue);
});
});
NOTE: It was also discovered that the OP is using Smarty 2 which is an older version of Smarty using an older version of jQuery, so .delegate() is used instead of on().

how to dynamically add id to the table rows so that they can be identified uniquely

I have a table structure in manage.component.html which gets populated with the data from the server.
I have a 'edit' button which can be used to edit a cell in the column.
Requirement:I want to uniquely identify each cell which can be further be used to edit and send back the value back value to the server for update.
I tried to append "counter" to the "id" of the so this can be used to uniquely identify each row cell, but the "counter" is not setting on the "id"
manage.component.html
<tbody>
<tr *ngFor="let work of workflows$">
<td>{{work.req_id}}</td>
<td><input id="btnn{{count$}}" type="text" value="{{work.client_ip}}" maxlength="4" placeholder="name" onload="addID()"/></td>
<td>{{work.client_type}}</td>
<td>{{work.project}}</td>
<td><button (click)="edit()" type="button" class="btn btn-primary">Edit</button></td>
</tr>
</tbody>
manage.component.ts
export class ManageComponent implements OnInit {
count$ = 0;
edit(event){
document.getElementById('btnn').focus();
}
addID(){
this.count$ = 5;
}
}
when i click the "edit" button the focus should be on the element of the rows and after editing when i click "send" button the change in the value should be accessible on the .ts file so that the value can be send back to the server.
There are various method to do it. But since you wanted to get it by id or class, check out the below code
<tbody>
<tr *ngFor="let work of workflows$; let i = index">
<td>{{work.req_id}}</td>
<td><input id="btnn-{{i}}" type="text" value="{{work.client_ip}}" maxlength="4" placeholder="name" onload="addID(i)"/></td>
<td>{{work.client_type}}</td>
<td>{{work.project}}</td>
<td>
<!-- Pass the index i as a parameter in edit() method -->
<button (click)="edit(i)" type="button" class="btn btn-primary">Edit</button>
</td>
</tr>
</tbody>
and in your typescript
edit(_index){
document.getElementById('btnn-'+_index).focus(); //get the element by id
}
Hope that helps

How to calculate value from a dynamically generated table and its rows using angular js?

I am trying to find the value for net worth from the table.
Net Worth = Sum(Stock Price * Shares Held)
Below is the table which is created as per user selection.
For ex: from the above table, Net Worth = (842.21*1 + 230.16*2 + 1002.2*2).
I am trying with ng-change as the networth can change every time the user adds or deletes stocks. Besides, I am also facing the css issue as networth goes down when a row is getting added to table.
Here is the plnkr.
<section class="pickstocks">
<label class="basiclabel">Manage Portfolio</label>
<div class="pickstocks-align">
<table>
<thead>
<tr>
<th>STOCK</th>
<th>PRICE</th>
<th>SHARES</th>
<th>WEIGHT</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="stock in stocksArray" class="portfoliokey">
<td>{{stock.key}}</td>
<td class="portfoliovalue">{{"₹" + stock.value}}</td>
<td class="changer">
<input type="button" id="num1" ng-click="decrementValue( stock.index )" value="-">
<input type="button" id="number{{ stock.index }}" value="1" />
<input type="button" ng-click="incrementValue( stock.index )" value="+" />
</td>
<td>{{stock.weight}}</td>
</tr>
</tbody>
</table>
<div class="networth">
<h3>Networth</h3>
</div>
</div>
</section>
Here is the Plunker for reference
The first thing to do is to store the weight field just like another field in the stock object.
mystock.weight = function(){return this.value*this.shares};
Making it into a function of the shares and value field lets us update the other two fields dynamically changing the networth. Also adding shares as a field to the stock object lets us update it easier later, so we only have to increment/decrement the field like so.
$scope.stocksArray[$scope.stocksArray.indexOf(stock)].shares++;
The biggest part of changing the code was objectifying it. Taking the stocks and representing it with objects allows us to easily modify and access the values later.
Once that was done all you have to do is make a method that returns the sum of all of the weights like so.
$scope.getNetWorth = function(){
let total = 0;
for(let i = 0; i < $scope.stocksArray.length; i++){
total += $scope.stocksArray[i].weight()
}
return total.toFixed(2);
}
And then reference it in the html like so {{"₹" +getNetWorth()}}
In the future try to use OOP practices to simplify your code and turn your data into object representations, it will save you time and headache trying to manipulate and access your data later.
you need to calculate sum at each instance
<tr ng-repeat="stock in stocksArray" class="portfoliokey">
<td>{{stock.key}}</td>
<td ng-init="totalAmount = totalAmount + (stock.value*stock.index)" class="portfoliovalue">{{"₹" + stock.value}}</td>
<td class="changer">
<input type="button" id="num1" ng-click="decrementValue( stock.index )" value="-">
<input type="button" id="number{{ stock.index }}" value="1" />
<input type="button" ng-click="incrementValue( stock.index )" value="+" />
</td>
<td>{{stock.weight}}</td>
</tr>
and total will be
<div class="networth">
<h3>{{totalAmount }}</h3>
</div>

Populating dropdown input in view with scope object

I have a dropdown menu/textbox which is used for when users are typing in something to save. Also, if it is saved it will load on the page here as well.
<tbody data-ng-repeat="(wholesalerIndex,wholesaler) in wholesalers" >
<tr >
<td >
<label class="control-label">{{wholesaler.ORDER}}</label>
</td>
<td class="col-md-3">
<div>
<input type="text" class="form-control dropdown-toggle" data-toggle="dropdown" data-ng-disabled="formReadOnly" data-ng-model="wholesaler.WHSLE_NAME" data-ng-keyup="comboWholesalerBoxOptions(wholesaler.WHSLE_NAME, 'wholeSalerNameOptions')" />
<ul>
<li data-ng-repeat="wholeSalerOption in wholeSalerNameOptions | unique: 'NAME'" style="width:100%"><a data-ng-click="changeWholesalerData(wholeSalerOption, wholesaler)">{{wholeSalerOption.NAME}}</a></li>
</ul>
</div>
</td>
<td >
<button data-ng-click="upWholesaler(wholesaler)">Up</button>
<button data-ng-click="downWholesaler(wholesaler)">Down</button>
</td>
</tr>
</tbody>
There is an order of these rows being created below and it can be moved up or down. {{wholesaler.ORDER}} is the current order of the row. I want the input dropdown to be populated with {{wholesaler.NAME}} when a button is pressed.
I can't figure out how to get it to populate. Putting it next to {{wholeSalerOption.NAME}} just makes the name add onto the end of the dropdown search results. value="wholeSalerOption.NAME" also doesn't do anything.
How do I get this field to update like other ones without interfering with its ability to do it's search?

What are HTML DOM #text elements?

I'm learning knockout.js and trying to use an afterRender callback to apply behaviour to elements.
I don't understand what these #text elements are that show up in my console.log().
So UI looks like this:
Knockout binding like this:
<div id='categoriesTree' style="color:black">
<table class='categoriesEditor'>
<tbody data-bind="template: { name: 'itemTmpl', foreach:children, afterRender: myPostProcessingLogic2 }"></tbody>
</table>
</div>
Template:
<script id="itemTmpl" type="text/html">
<tr>
<td>
<div class="input-group cat-block" style="margin-left: 3px; margin-bottom: 12px;">
<label id="may-search-tags-lbl" style="background-color:beige;visibility:hidden;margin:0;">Category Name</label>
<input data-bind='value: Name' id="maynavsearchphrase" type="text" class="form-control"
placeholder="Category name" name="maynavsearchphrase"
value="" />
<div class="input-group-btn btn-grp-70 cat-meth-off">
<button id="may-nav-tags-search-btn" class="btn btn-default btnIcon may-tipped"
type="button" data-toggle="tooltip" title="Delete Category">
<i class="glyphicon glyphicon-remove"></i>
</button>
<button id="may-nav-search-search-btn" class="btn btn-default btnIcon may-tipped"
data-toggle="tooltip" title="Add subcategories"
data-bind='click: $root.addCategory'
type="button">
<i class="glyphicon glyphicon-expand"></i>
</button>
</div>
</div>
</td>
<td data-bind="visible: children().length">
<table>
<tbody data-bind="template: { name: 'itemTmpl', foreach: children }"></tbody>
</table>
</td>
</tr>
</script>
Callback function:
self.myPostProcessingLogic2 = function (elements) {
console.log(elements);
}
And then chrome dev tools console output:
What are the "text" elements in text, tr, text? There is no text element that is a sibling of tr. tbody can only contain tr's right?
If I drill into text I can see that it has an attribute of cells : HtmlCollection[2] both nodes of which are td. So it's almost like text = tr but if thats the case then why am I getting 3 sibling nodes to represent one row?
"What are the "text" elements in text, tr, text? There is no text element that is a sibling of tr..."
Everything in the DOM is represented by a node. Including plain text.
In your case, the text nodes are coming from the whitespace you have around your elements for formatting. That text is counted just like any other text.
<table>
<tbody>
<tr>
<td>foo</td>
</tr>
</tbody>
</table>
All that empty whitespace around the opening/closing tags gets represented as text nodes. This is true for all elements in the DOM, not just tables.
Table elements have special collections for you to use, which allow you to access just the table elements.
table.tBodies[] // to get the tbody elements of a table
table.rows[] // to get the rows of a table
tbody.rows[] // to get the rows of a tbody
row.cells[] // to get the cells of a row
Or you can use the generic .children to avoid text nodes.
tbody.children[]
The text nodes are the ones you write with "" inside your HTML.
Run this in your console, then scroll to the bottom, right-click and click inspect element:
document.body.appendChild(document.createTextNode("Some text node"))
Please print the value of #text in the console if it is node console.log(nodes.item(i).nodeValue)
This may occur due a space between the html element assign value.

Categories