Validate rows and highlight the invalid cells in slickGrid - javascript

I want to validate a row on click of Save. This question is already discussed with the following solution. I see that rows are invalid and the failures array has the invalid entries but cells are not highlighted.
Following is the my code:
function save(){
grid.validate();
}
//Validate only on Save
grid.validate = function() {
var rowFailures = {}
for (r in data) {
if(r == 'getItemMetadata'){continue;}
var failures = validateColumns({item: data[r], row: r})
if(failures.length > 0){
rowFailures[r] = failures;
}
}
if(Object.keys(rowFailures).length > 0){
grid.onValidationError.notify({"rowFailures": rowFailures}, new Slick.EventData())
}
}
//Validation Error invoked
grid.onValidationError.subscribe(function(e, args) {
console.log("Validation error");
if(args.rowFailures){
var rowsIndexValues = Object.keys(args.rowFailures);
for(i in rowsIndexValues) {
var rowFailures = args.rowFailures[rowsIndexValues[i]]
for(j in rowFailures){
var failure = rowFailures[j];
//scroll first failure into view
if(0 == i){grid.scrollRowIntoView(failure.rowIndex)}
grid.flashCell(failure.rowIndex, failure.columnIndex, 500);//Makes no difference!
}
}
}
});
//Changes the background color for the failed cells
data.getItemMetadata = function(row) {
var failures = validateColumns({row: row, item: data[row]})
if(failures.length > 0){
var obj = {};
var metadata = {"columns": obj}
//Its iterating through these failures
for(f in failures){
//Changes the background color of the cell
obj[failures[f].column.id] = {"formatter": function(row, cell, value, m, item){return '<div style="background: #FFCCCC" title="This field is //invalid">'+failures[f].item[failures[f].column.field]+'</div>'}}
}
return metadata;
}
return {}
}

I was able to achieve this using the following code
dataView.getItemMetadata = metadata(dataView.getItemMetadata);
function metadata(old_metadata_provider) {
return function (row) {
var item = this.getItem(row);
var ret = (old_metadata_provider(row) || {});
if (item) {
ret.cssClasses = (ret.cssClasses || '');
var failures = //check for some condition
if(failures.length > 0){
return {
cssClasses: 'validationError'
};
}
}
}

Related

JavaScript array has elements but length is zero

I've done some searching around the web and nothing seems to solve my problem. I have the following jQuery code:
function youtube_data_parser(data) {
//---> parse video data - start
var qsToJson = function(qs) {
var res = {};
var pars = qs.split('&');
var kv, k, v;
for (i in pars) {
kv = pars[i].split('=');
k = kv[0];
v = kv[1];
res[k] = decodeURIComponent(v);
}
return res;
}
//---> parse video data - end
var get_video_info = qsToJson(data);
if (get_video_info.status == 'fail') {
return {
status: "error",
code: "invalid_url",
msg: "check your url or video id"
};
} else {
// remapping urls into an array of objects
//--->parse > url_encoded_fmt_stream_map > start
//will get the video urls
var tmp = get_video_info["url_encoded_fmt_stream_map"];
if (tmp) {
tmp = tmp.split(',');
for (i in tmp) {
tmp[i] = qsToJson(tmp[i]);
}
get_video_info["url_encoded_fmt_stream_map"] = tmp;
}
//--->parse > url_encoded_fmt_stream_map > end
//--->parse > player_response > start
var tmp1 = get_video_info["player_response"];
if (tmp1) {
get_video_info["player_response"] = JSON.parse(tmp1);
}
//--->parse > player_response > end
//--->parse > keywords > start
var keywords = get_video_info["keywords"];
if (keywords) {
key_words = keywords.replace(/\+/g, ' ').split(',');
for (i in key_words) {
keywords[i] = qsToJson(key_words[i]);
}
get_video_info["keywords"] = {
all: keywords.replace(/\+/g, ' '),
arr: key_words
};
}
//--->parse > keywords > end
//return data
return {
status: 'success',
raw_data: qsToJson(data),
video_info: get_video_info
};
}
}
function getVideoInfo() {
var get_video_url = $('#ytdlUrl').val();
var get_video_id = getUrlVars(get_video_url)['v'];
var video_arr_final = [];
var ajax_url = "video_info.php?id=" + get_video_id;
$.get(ajax_url, function(d1) {
var data = youtube_data_parser(d1);
var video_data = data.video_info;
var player_info = data.video_info.player_response;
var video_title = player_info.videoDetails.title.replace(/\+/g, ' ');
var fmt_list = video_data.fmt_list.split(',');
var video_thumbnail_url = video_data.thumbnail_url;
var video_arr = video_data.url_encoded_fmt_stream_map;
//create video file array
$.each(video_arr, function(i1, v1) {
var valueToPush = {};
valueToPush.video_url = v1.url;
valueToPush.video_thumbnail_url = video_thumbnail_url;
valueToPush.video_title = video_title;
$.each(fmt_list, function(i2, v2) {
var fmt = v2.split('/');
var fmt_id = fmt[0];
var fmt_quality = fmt[1];
if (fmt_id == v1.itag) {
valueToPush.fmt_id = fmt_id;
valueToPush.fmt_quality = fmt_quality;
}
});
video_arr_final.push(valueToPush);
});
});
return video_arr_final;
}
function getUrlVars(url) {
var vars = {};
var parts = url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m, key, value) {
vars[key] = value;
});
return vars;
}
function fillInOptions(ytOptions) {
//console.log(ytOptions);
//alert(ytOptions[0]);
var ytFill = ytOptions;
console.log(ytFill);
//ytFill.forEach(function(i,v) {
var ytdlOptions = $('#ytdlOptions');
ytFill.forEach(function(i,v) {
console.log(i);
ytdlOptions.append(new Option(v.fmt_quality, v.fmt_id));
});
return true;
}
function showYTDLLoader() {
$('#ytdlInput').fadeOut(1000, function() {
$('#ytdlLoader').fadeIn(500);
});
var options = getVideoInfo();
//console.log(options);
if (fillInOptions(options) == true) {
//do rest
}
}
function showYTDLOptions() {
return true;
}
function startDownload() {
showYTDLLoader();
}
function hideYTDLLoader() {
$('#ytdlLoader').fadeOut(500);
}
function animateCSS(element, animationName, callback) {
const node = $(element);
node.addClass(animationName);
function handleAnimationEnd() {
node.removeClass(animationName);
node.animationend = null;
if (typeof callback === 'function') callback();
}
node.animationend = handleAnimationEnd();
}
When my button is clicked, I call showYTDLLoader() which gets an array of objects from the YouTube API that looks like this:
[
{
"video_url": "https://r7---sn-uxanug5-cox6.googlevideo.com/videoplayback?expire=1572496003&ei=Iw66Xa24H8PL3LUPiN25mAs&ip=2001%3A8003%3A749b%3Aa01%3A5cd8%3Ac610%3A6402%3Ad0fe&id=o-ADsVnoOoBQ6-SWzYZU7gHES06s7xQptJG6hn9WcakITY&itag=22&source=youtube&requiressl=yes&mm=31%2C29&mn=sn-uxanug5-cox6%2Csn-ntqe6n7r&ms=au%2Crdu&mv=m&mvi=6&pl=39&initcwndbps=1655000&mime=video%2Fmp4&ratebypass=yes&dur=917.768&lmt=1572418007364260&mt=1572474311&fvip=4&fexp=23842630&c=WEB&txp=5535432&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cmime%2Cratebypass%2Cdur%2Clmt&sig=ALgxI2wwRgIhAIp-4gyUTLoXFetbY0ha_YnR7DJqsp_MNjjIxqDdfPZJAiEA_WPd21jgX9broBcigf8rcSEVoJb2_NX7t3XZQqytsSM%3D&lsparams=mm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AHylml4wRAIgacvP3zjEq-rVEZFrX7a_hC6TR-Zab7Ii-Fbaupjs_PcCIHdZht4l4ioYL3ERz7WNiSbnOnhm5iYxEECaQXPP2hUp",
"video_title": "Arnold Schwarzenegger on Son-in-law Chris Pratt, Pranking Sylvester Stallone & Terminator’s Return",
"fmt_id": "22",
"fmt_quality": "1280x720"
},
{
"video_url": "https://r7---sn-uxanug5-cox6.googlevideo.com/videoplayback?expire=1572496003&ei=Iw66Xa24H8PL3LUPiN25mAs&ip=2001%3A8003%3A749b%3Aa01%3A5cd8%3Ac610%3A6402%3Ad0fe&id=o-ADsVnoOoBQ6-SWzYZU7gHES06s7xQptJG6hn9WcakITY&itag=18&source=youtube&requiressl=yes&mm=31%2C29&mn=sn-uxanug5-cox6%2Csn-ntqe6n7r&ms=au%2Crdu&mv=m&mvi=6&pl=39&initcwndbps=1655000&mime=video%2Fmp4&gir=yes&clen=44248820&ratebypass=yes&dur=917.768&lmt=1572416976690256&mt=1572474311&fvip=4&fexp=23842630&c=WEB&txp=5531432&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cmime%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&sig=ALgxI2wwRQIhANTZJlBHFWQWCnfK11yvLiPUV26c6NzvqIMKjDwmsByMAiBUSy0ZJMo4GdHSiRU4xBDDLxLtzwKZAqAKCiB-1aViDQ%3D%3D&lsparams=mm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AHylml4wRAIgacvP3zjEq-rVEZFrX7a_hC6TR-Zab7Ii-Fbaupjs_PcCIHdZht4l4ioYL3ERz7WNiSbnOnhm5iYxEECaQXPP2hUp",
"video_title": "Arnold Schwarzenegger on Son-in-law Chris Pratt, Pranking Sylvester Stallone & Terminator’s Return",
"fmt_id": "18",
"fmt_quality": "640x360"
}
]
But when I try and loop through each entry with fillInOptions(), my loop is never completed because the length is apparently zero. However, when I dump the array using console.log() it tells me the length is 2, and displays the above. I need to be able to add each option to my dropdown.
Thankyou!
UPDATE: Added full code, sorry!
It looks like your .forEach() is the root of the problem. The parameters of a forEach are currentValue, index like this: array.forEach(function(currentValue, index) {}); but it looks like you're using them in the opposite way
Try rewriting that iteration to this:
ytFill.forEach(function(v, i) {
console.log(i);
ytdlOptions.append(new Option(v.fmt_quality, v.fmt_id));
});
Notice the difference in the order of v and i in the parameters.

How to make a restriction on the addition of tags in the input?

there is a fully working code. its task is to add tags (text) separated by commas in the input field remembering the choice (the added tag changes color).
everything would be fine, but with this functionality, users can add an unlimited number of tags. how to prevent users from adding more than 4-5 tags? and if you exceed the limit tag will be replaced by another tag?
html code:
<ul id="aTags">
HTML;
for ($i=0;$d[$i];$i++) {
$html .= '<li class="atags-b" onClick="aTags.toggle(this);">'.$d[$i].'</li>';
}
$html .= <<<HTML
</ul>
javascript code:
aTags = {
inputObj : document.getElementById("tags"),
tagsColl : document.getElementById("aTags").getElementsByTagName("li"),
array_value : function (arr) {
var c = 0;
var tmpArr = new Array ();
for (key in arr) {
tmpArr[c] = arr[key];
++c;
}
return tmpArr;
},
toggle : function (e) {
var iArr = this.inputObj.value.split(',');
var in_arr = false;
for (var i in iArr) {
iArr[i] = iArr[i].replace(/^[\s\xa0]+|[\s\xa0]+$/g, "");
}
for (var i=0;i<iArr.length;i++) {
if (iArr[i] == e.innerHTML) {
in_arr = true;
delete iArr[i];
break;
}
else if (iArr[i] == '')
delete iArr[i];
}
if (in_arr === false) {
iArr.push(e.innerHTML);
}
iArr = this.array_value(iArr);
this.highlight(iArr);
this.inputObj.value = iArr.join(', ');
},
highlight : function (tags) {
for (var i=0;i < this.tagsColl.length;i++) {
this.tagsColl[i].className = 'atags-b';
for (var ii=0;ii<tags.length;ii++) {
//alert (this.tagsColl[i].innerHTML + ' <> ' + tags[ii]);
if (this.tagsColl[i].innerHTML == tags[ii]) {
this.tagsColl[i].className = 'atags-c';
break;
}
}
}
},
iHighlight : function () {
var iArr = this.inputObj.value.split(',');
var in_arr = false;
for (var i in iArr) {
iArr[i] = iArr[i].replace(/^[\s\xa0]+|[\s\xa0]+$/g, "");
}
this.highlight (iArr);
}
}
click count:
let num = 0, button = document.querySelector('[class=atags-b]');
button.onclick = function () {
num++, num > 4 ? this.disabled = true : '';
};
Thanks!!
-Aison
Just before iArr = this.array_value(iArr), I would limit the array to 4 elements:
iArr = iArr.slice(0, 4);

javascript function return "undefine" from jsp page

i have a jsp page and call a JS function which is in some abc.js file from this JSP page.
i have included this js file to jsp page.
JSP JavaScript Code:-
function doFinish(tableId, col, field)
{
var oldselectedCells = "";
var selItemHandle = "";
var selRightItemHandle = "";
var left = -1;
var right = -1;
// Get the table (tBody) section
var tBody = document.getElementById(tableId);
// get field in which selected columns are stored
var selectedCellsFld = document.getElementById(tableId + datatableSelectedCells);
selectedCellsFld.value = oldselectedCells;
for (var r = 0; r < tBody.rows.length; r++)
{
var row = tBody.rows[r];
if (row.cells[col].childNodes[0].checked == true)
{
selectedCellsFld.value = oldselectedCells +
row.cells[col].childNodes[0].id;
selItemHandle = row.cells[col].childNodes[0].value
oldselectedCells = selectedCellsFld.value + datatableOnLoadDivider;
left = selItemHandle.indexOf("=");
right = selItemHandle.length;
selRightItemHandle = selItemHandle.substring(left+1,right);
var index=getColumnIndex(tBody,"Name");
if(index!=null)
{
if(field == 1)
{
window.opener.document.TemplateForm.eds_asbactionscfg_item_handle_child_physpart.value = selRightItemHandle;
window.opener.document.TemplateForm.ChildPhysicalPart.value = row.cells[index].childNodes[0].innerHTML;
}
else if (field == 2)
{
window.opener.document.TemplateForm.eds_asbactionscfg_dev_doc_item_handle_name.value = selRightItemHandle;
window.opener.document.TemplateForm.DeviationObject.value = row.cells[index].childNodes[0].innerHTML;
}
else if (field == 3)
{
window.opener.document.TemplateForm.eds_asbactionscfg_dev_doc_item_handle_name.value = selRightItemHandle;
window.opener.document.TemplateForm.DeviationObject.value = row.cells[index].childNodes[0].innerHTML;
}
}
}
}
window.close();
}
JS Code:-
function getColumnIndex(tBody,columnName)
{
var cells = tBody.parentNode.getElementsByTagName('th');
for (var i=0;i<cells.length; i++)
{
if(cells[i].hasChildNodes())
{
if(cells[i].childNodes[0].innerHTML.replace(/(\r\n|\n|\r)/gm ,"").trim() == columnName)
{
return i;
}
}
}
}
i had debug this code with firebug & calling getColumnIndex(tBody,columnName) function works fine but when it return to caller the var index=getColumnIndex(tBody,"Name"); the index value is "undefine".
suggest some solution.
getColumnIndex(tBody,columnName) function works fine.
as if it matches this if condition
if(cells[i].childNodes[0].innerHTML.replace(/(\r\n|\n|\r)/gm ,"").trim() == columnName)
{
return i;
}
so that it returns something.
but when you replace this
var index=getColumnIndex(tBody,"Name"); so that coulmnName would be "Name" in String.
And it doesn't match with any columnName so that your condition going to be wrong and function doesn't return anything.
var index=getColumnIndex(tBody,"Name"); the index value is "undefine".
suggestion is put some else condition on that and return some error message like this :
if(cells[i].childNodes[0].innerHTML.replace(/(\r\n|\n|\r)/gm ,"").trim() == columnName)
{
return i;
} else{
// put some error message
// return null
}
i had debug this code with firebug & calling getColumnIndex(tBody,columnName) function works fine
From this, I'm assuming that there isn't anything wrong with the implementation of your getColumnIndex function, so your issue with getting an undefined value must have to do with when this function is returning a value.
but when it return to caller the var index=getColumnIndex(tBody,"Name"); the index value is "undefine".
This leads me to assume that your tBody variable is not being set correctly, given that the "function works fine".
I'm assuming there is a case in your code where the conditions of your getColumnIndex function is not met.
function getColumnIndex(tBody,columnName)
{
var cells = tBody.parentNode.getElementsByTagName('th');
for (var i=0;i<cells.length; i++)
{
if(cells[i].hasChildNodes())
{
if(cells[i].childNodes[0].innerHTML.replace(/(\r\n|\n|\r)/gm ,"").trim() == columnName)
{
return i;
}
}
}
// If your code reaches this point, then the prior conditions have not been met
// You can choose to do something else here for return false/undefined etc.
return undefined;
}

JqGrid local data custom validation failing

I have an implementation of jqgrid with a method that checks for duplicates cell entries. This method is part of a custom validation method defined in editrules. It appears to work ok, but once I delete rows from the grid, the $grid.jqGrid('getGridParam', '_index'); seems to be getting messed up. I'm doing local deletes I scavenged as follows:
delSettings = {
onclickSubmit: function (options) {
var gridId = $.jgrid.jqID($grid[0].id),
gridP = $grid[0].p,
newPage = gridP.page,
rowids = gridP.multiselect ? gridP.selarrrow : [gridP.selrow],
reloadLastPage = false;
options.processing = true; // - reset the value of processing option which could be modified
$.each(rowids, function () { // - delete selected rows
$grid.delRowData(this);
});
This is the pertinent method, the idea is to check for rows with a duplicate entry, skipping the row being edited (selectedRowId).
function IsIDImagePathUnique(imagePath) {
var status = true,
$grid = $('#list'),
selectedRowId = $grid.jqGrid('getGridParam', 'selrow'),
gridData = $grid.jqGrid('getGridParam', 'data'),
idsToDataIndexes = $grid.jqGrid('getGridParam', '_index');
$.each(idsToDataIndexes, function (id, index) {
if (_editType === 'edit' && id === selectedRowId) // - skip edited row
return true;
if( gridData[index].IDImage === imagePath ) {
status = false;
return false; // - break
}
});
return status;
}
Help or comments appreciated.
UPDATED:
I still need to figure out what my issue was, but I came up with a workaround to avoid using the $grid.jqGrid('getGridParam', '_index'); :
function IsIDImagePathUnique(imagePath) {
var status = true,
$grid = $('#list'),
gridData = $grid.jqGrid('getGridParam', 'data'),
allowed = _editType ? 1 : 0,
counter = 0;
for (var i = 0; i < gridData.length; i++) {
if (gridData[i].IDImage === imagePath && ++counter > allowed) {
status = false;
break;
}
}
return status;
}

TypeError: this.Rows is undefined

When i click the search button it always alert me "TypeError: this.Rows is undefined
for (var i = 0, row; row = this.Rows[i], rowText = this.RowsText[i]; i++) { "
Here is my code:
<script language="javascript" type="text/javascript">
//define the table search as an object, which can implement both functions and properties
window.tableSearch = {};
//initialize the search, setup the current object
tableSearch.init = function() {
//define the properties I want on the tableSearch object
this.Rows = document.getElementById('data').getElementsByTagName('TR');
this.RowsLength = tableSearch.Rows.length;
this.RowsText = [];
//loop through the table and add the data to
for (var i = 0; i < tableSearch.RowsLength; i++) {
this.RowsText[i] = (tableSearch.Rows[i].innerText) ? tableSearch.Rows[i].innerText.toUpperCase() : tableSearch.Rows[i].textContent.toUpperCase();
}
}
//onlys shows the relevant rows as determined by the search string
tableSearch.runSearch = function() {
//get the search term
this.Term = document.getElementById('textBoxSearch').value.toUpperCase();
//loop through the rows and hide rows that do not match the search query
for (var i = 0, row; row = this.Rows[i], rowText = this.RowsText[i]; i++) {
row.style.display = ((rowText.indexOf(this.Term) != -1) || this.Term === '') ? '' : 'none';
}
}
//runs the search
tableSearch.search = function(e) {
//checks if the user pressed the enter key, and if they did then run the search
var keycode;
if (window.event) { keycode = window.event.keyCode; }
else if (e) { keycode = e.which; }
else { return false; }
if (keycode == 13) {
tableSearch.runSearch();
}
else { return false; }
}
</script>
The two most probable causes to that problem are:
You never called your init function. Make sure to call it after your HTML has loaded and before using Rows the first time.
There is a Firefox bug where getElementsByTagName('TR'); returns undefined. make sure that that is not your problem.

Categories

Resources