Do not submit empty form fields in ExtJS - forms

I have an extjs form with fields. The user isn't required to enter data into each field so I do not want to submit the fields with no data. I want it to post only fields that actually have data. Is this possible?

I recommend using form's beforeaction event. While handling this event you can check all fields. If all values are empty just return false;. The following example works in ExtJS4 and has to work in ExtJS3:
myform.on('beforeaction', function(form, action) {
if (action.type == 'submit') {
var doSubmit = false, vals = form.getValues();
for (var i in vals)
if (vals[i] !== '') {
doSubmit = true;
break;
}
return doSubmit;
}
});
Actualy, the right way to not submit empty fields is to use action's submitEmptyText config. But it's not working in current version (ExtJS4.0.2a).

Another options is to override component's getSubmitValue() method and return null if this field is empty, this way it won't be included into submit fields.
{
xtype: 'combo',
getSubmitValue: function(){
var value = this.getValue();
if(Ext.isEmpty(value)) {
return null;
}
return value;
}
}

Instead of using form's submit, directly call Ext.Ajax.request(...) with the url, method type (GET/POST) and params (and any other options as explained in the call documentation).
To generate params, iterate over the form fields and check for null value before adding to params.

This bug is present in ExtJS 4.0.7 too.
As Molecule Man pointed:
Actualy, the right way to not submit empty fields is to use action's submitEmptyText config. But it's not working in current version (ExtJS4.0.2a).
A possible solution to fix this bug is by overriding 2 functions, getValues in "Ext.form.Basic" (where the bug is) and createForm (to create our basic form) in "Ext.form.Panel" by extension in the following way:
Ext.define("My.form.Basic", {
alias: "form.mybasic",
extend: "Ext.form.Basic",
getValues: function(asString, dirtyOnly, includeEmptyText, useDataValues) {
var values = {};
this.getFields().each(function(field) {
if (!dirtyOnly || field.isDirty()) {
var data = field[useDataValues ? "getModelData" : "getSubmitData"](includeEmptyText);
if (Ext.isObject(data)) {
var isArray = Ext.isArray;
Ext.iterate(data, function(name, val) {
if (includeEmptyText && val === "") {
val = field.emptyText || "";
}
if (includeEmptyText || ((!isArray(val) && val !== "") || (isArray(val) && val.length !== 0))) {
if (name in values) {
var bucket = values[name];
if (!isArray(bucket)) {
bucket = values[name] = [bucket];
}
if (isArray(val)) {
values[name] = bucket.concat(val);
}
else {
bucket.push(val);
}
}
else {
values[name] = val;
}
}
});
}
}
});
if (asString) {
values = Ext.Object.toQueryString(values);
}
return values;
}
});
Ext.define("My.form.Panel", {
alias: "form.mypanel",
extend: "Ext.form.Panel",
createForm: function() {
return Ext.create("My.form.Basic", this, Ext.applyIf({listeners: {}}, this.initialConfig));
}
});
The code is copied from the ext source code. The only change is inside the iteration of each field: introduced the following wrapping "if":
if (includeEmptyText || ((!isArray(val) && val !== "") || (isArray(val) && val.length !== 0)))
I am a bit late but, better later than never...

Related

w2ui filter option "contains not" possible?

I am using w2ui (1.5) and I would be interested in whether it is possible to use a filter that only delivers negative results
That means only records/keys which not fullfilling a certain criteria.
Like a condition "contains not" or "is not" in addition to
http://w2ui.com/web/docs/1.5/w2grid.textSearch.
Thanks!
Gordon
okay, a possible solution is
w2ui['grid'].search([{ field: var1, value: var2, operator: 'not in'}], 'OR');
I coded my own solution to this problem. This adds a way to use "not" for string and "!=" for number searches.
This function does the search and it is also used to store the grid advanced search popup in history state.
I'm sure this can be even more optimized, so please use this more like a guideline. Hope this helps somebody.
function searchExtend(event, grid) {
// if event is null, we do just the local search
var searchObj;
if (event == null) {
searchObj = grid;
} else {
searchObj = event;
}
// make a copy of old data
const oldSearchData = structuredClone(searchObj.searchData);
const oldSearchLogic = structuredClone(searchObj.searchLogic);
var searchData = searchObj.searchData;
var invertedSdata = [];
var toSplice = [];
// check operator if it's "not" or "!="
for (var i = 0; i < searchData.length; i++) {
var sdata = searchData[i];
// invert the condition
if (sdata.operator == "not") {
toSplice.push(i);
invertedSdata.push({
field: sdata.field,
type: sdata.type,
operator: "contains",
value: sdata.value
});
}
if (sdata.operator == "!=") {
toSplice.push(i);
invertedSdata.push({
field: sdata.field,
type: sdata.type,
operator: "=",
value: sdata.value
});
}
}
// remove all "not" and "!=" from searchData
for (var i in toSplice) {
searchData.splice(i, 1);
}
var foundIds = [];
// use inverted criteria to search
if (invertedSdata.length > 0) {
grid.searchData = invertedSdata;
grid.searchLogic = "OR";
grid.localSearch();
grid.searchLogic = oldSearchLogic;
// store found ids
foundIds = structuredClone(grid.last.searchIds);
}
if (foundIds.length > 0) {
// perform a search with original criteria - spliced "not" and "!="
grid.searchData = searchData;
grid.localSearch();
var allRecIds = structuredClone(grid.last.searchIds);
// if there's not any results, push push all recIds
if (grid.last.searchIds.length == 0) {
for (let i = 0; i < grid.records.length; i++) {
allRecIds.push(i);
}
}
// remove all ids found with inverted criteria from results. This way we do the "not" search
for (const id of foundIds) {
allRecIds.splice(allRecIds.indexOf(id), 1);
}
if (event != null) {
// let the search finish, then refresh grid
event.onComplete = function() {
refreshGrid(grid, allRecIds, oldSearchData);
setSearchState(grid);
}
} else {
// refresh the grid
refreshGrid(grid, allRecIds, oldSearchData);
setSearchState(grid);
}
return;
}
if (event != null) {
event.onComplete = function() {
setSearchState(grid); // store state
}
} else {
// refresh whole grid
refreshGrid(grid, allRecIds, oldSearchData);
setSearchState(grid);
}
}
function refreshGrid(grid, allRecIds, oldSearchData) {
grid.last.searchIds = allRecIds;
grid.total = grid.last.searchIds.length;
grid.searchData = oldSearchData;
grid.refresh();
}
function setSearchState(grid) {
history.replaceState(JSON.stringify(grid.searchData), "Search");
}
To use this, you have to call it from the grid's onSearch:
onSearch: function(event) {
searchExtend(event, w2ui["grid"]);
}
Also, if you want to use the history.state feature, it needs to be called from onLoad function:
onLoad: function(event) {
event.onComplete = function() {
console.log("History state: " + history.state);
if (history.state != null) {
w2ui["grid"].searchData = JSON.parse(history.state);
searchExtend(null, w2ui["grid"]);
}
}
To add operators, please use this reference.
This is my solution to the problem:
operators: {
'text': ['is', 'begins', 'contains', 'ends', 'not'], // could have "in" and "not in"
'number': ['=', 'between', '>', '<', '>=', '<=', '!='],
'date': ['is', 'between', {
oper: 'less',
text: 'before'
}, {
oper: 'more',
text: 'after'
}],
'list': ['is'],
'hex': ['is', 'between'],
'color': ['is', 'begins', 'contains', 'ends'],
'enum': ['in', 'not in']
}

How to customise the ghost-text (drag item name) with AgGrid

As per title, is there a way to customise the "ghost text" with the (unmanaged) row-dragging implementation in AgGrid via the API?
I found workaround using valueGetter property to change this text.
Example configuration of column:
private dragDropColumn= {
rowDrag: true,
...
valueGetter: (params) => {
return params.data.myVariableFromRow;
}
}
Hope this helps
You can now use colDef.rowDragText to set a callback function to set the dragged text.
https://www.ag-grid.com/javascript-grid-row-dragging/#custom-row-drag-text
onRowDragEnter event,
you could search for the ghost element
and then append your custom class to it.
document.querySelectorAll('.ag-dnd-ghost')[0]
.classlist.add('my-custom-ghost-element');
Dont forget to follow the hierarchy of classes, otherwise you would end up using !important in the custom class :-)
// For updating text
search for the element with className ag-dnd-ghost-label
document.querySelectorAll('.ag-dnd-ghost-label')[0]
.innerHTML = 'your_custom_text';
Ghost element is added only during drag, once drag is ended, its removed from dom by Ag-Grid.
Hope this helps
I've researched this and there is nothing built in to ag-grid. I accomplished this by attaching to the onRowDragMove and onRowDragMove events.
I set a class variable 'isRowDragging' to only do this once while dragging.
I use the Angular Renderer2 class (this.ui) to find and update the Element of the ghost label.
All of this is available with vanilla javascript or other supported ag-grid frameworks.
this.gridOptions.onRowDragMove = (params: RowDragMoveEvent) => {
const overNode = params.overNode;
const movingNode = params.node;
if (!this.isRowDragging) {
this.isRowDragging = true;
if (!movingNode.isSelected()) {
if (params.event && params.event.ctrlKey) {
movingNode.setSelected(true);
}
if (params.event && !params.event.ctrlKey) {
movingNode.setSelected(true, true);
}
}
let labelText: string = '';
const selectedNodes = this.gridOptions.api.getSelectedNodes();
if (selectedNodes.length === 1) {
labelText = selectedNodes[0].data.Name;
}
else {
const guids: string[] = [];
let folderCount: number = 0;
let docCount: number = 0;
selectedNodes.forEach((node: RowNode) => {
guids.push((node.data.FolderGuid || node.data.DocumentGuid).toLowerCase());
if (node.data.FolderGuid !== undefined) {
folderCount++;
}
else {
docCount++;
}
});
if (folderCount === 1) {
labelText = '1 folder';
}
else if (folderCount > 1) {
labelText = folderCount + ' folders';
}
if (docCount === 1) {
labelText += (labelText !== '' ? ', ' : '') + '1 document';
}
else if (docCount > 1) {
labelText += (labelText !== '' ? ', ' : '') + docCount + ' documents';
}
}
console.log(this.ui.find(document.body, '.ag-dnd-ghost').outerHTML);
this.ui.find(document.body, '.ag-dnd-ghost-label').innerHTML = labelText;
}
}
this.gridOptions.onRowDragEnd = (event: RowDragEndEvent) => {
this.isRowDragging = false;
}
I am using ag-grid with react and using cellRendererFramework property of ColDef to use custom react component, my value getter returns object, so when I start dragging I am getting [object object], I added toString method to returning object and did the trick, below is my sample code
const col1 = {
rowDrag: true,
...
valueGetter: (params) => {
const {id, name} = params.data;
return {id, name, toString: () => name}; // so when dragging it will display name property
}
}

How to check if text is found in column in Protractor

I'm trying to assert that a name is displayed in a column of a table. I've written an inResults function that will iterate through a column's text to see if a name exists. Here's what I'm trying:
Page object:
this.names = element.all(by.repeater('row in rows').column('{{row}}'));
this.inResults = function(nameString) {
var foundit = '';
this.names.each(function(name) {
name.getText().then(function(it) {
console.log(it); // each name IS printed...
if(it == nameString) {
console.log('it\'s TRUE!!!!'); // this gets printed...
foundit = true;
}
});
});
return foundit; // returns '' but should be true?
};
Spec expect:
expect(friendPage.inResults('Jo')).toBeTruthy();
Both console statements print as expected... but my expect fails as foundit's value is still ''. I've tried this a number of ways and none are working. What am I missing?
I've devised what I think is a better/cleaner way to solve this. It's less complex and doesn't require locator/css code in the method.
friend.page.js
// locator
this.friendName = function(text) { return element.all(by.cssContainingText('td.ng-binding', text)) };
// method
this.inResults = function(name) {
return this.friendName(name).then(function(found) {
return found.length > 0;
});
};
friend.spec.js
expect(friendPage.inResults('Jo')).toBeTruthy();
I've added this to my protractor_example project on GitHub...
I would recommend you to use filter: http://angular.github.io/protractor/#/api?view=ElementArrayFinder.prototype.filter
this.inResults = function(nameString) {
return this.names.filter(function(name) {
return name.getText().then(function(text) {
return text === nameString;
});
}).then(function(filteredElements) {
// Only the elements that passed the filter will be here. This is an array.
return filteredElements.length > 0;
});
});
// This will be a promise that resolves to a boolean.
expect(friendPage.inResults('Jo')).toBe(true);
Use map to do this.This will return a deferred that will resolve with the values in an array, so if you have this:
this.mappedVals =element.all(by.repeater('row in rows').column('{{row}}')).map(function (elm) {
return elm.getText();
});
It will resolve like this:
this.inResults = function(nameString) {
var foundit = '';
mappedVals.then(function (textArr) {
// textArr will be an actual JS array of the text from each node in your repeater
for(var i=0; i<textArr.length; i++){
if(it == textArr[i]) {
console.log('it\'s TRUE!!!!'); // this gets printed...
foundit = true;
}
}
return foundit;
});
}
And Use that in Spec file like,
friendPage.inResults('Jo').then(function(findIt){
expect(findIt).toBeTruthy();
});

Quickest way to validate form input fields with JS

What is the quickest way to validate al my input fields with javaScript?
I want to check if the fields are filled in or not.
Thx
following is my javascript code :
<script>
function Check(frm)
{
var input, EmptyFound=false;
var elem = document.getElementById('frmMain').elements;
for(var i=0;i<elem.length;i++)
{
input = elem[i];
if(input.type == "text")
{
if(input.value == "")
{
EmptyFound = true;
break;
}
}
return notEmpty;
}
</script>
I would use jQuery Validate.
A single line of jQuery to select the form and apply the validation
plugin. And a bit of metadata on each element to specify the
validation rules.
The docs specifically for enforcing required are at
http://docs.jquery.com/Plugins/Validation/Methods/required
It requires adding jQuery (if you don't use it already) but provides a fast and powerful validation framework.
you can use this to distinguish between input different types
this is just a sample from my project
//var frm_elements = document.mainForm.elements;
//var frm_elements =document.getElementById('mainForm').elements;
for (var i = 0; i < document.mainForm.elements.length; i++)
{document.mainForm.elements[i];
if(document.mainForm.elements[i]!=null)
{
var field_type = document.mainForm.elements[i].type;
switch (field_type)
{
case "text":
if(document.mainForm.elements[i].name.indexOf("from")!='-1' &&
document.mainForm.elements[i].name.indexOf("frotom")!='-1' &&
!document.mainForm.elements[i].name.indexOf("serNo")!='-1' &&
!document.mainForm.elements[i].name.indexOf("repKey")!='-1' &&
!document.mainForm.elements[i].name.indexOf("orderKey")!='-1' &&
!document.mainForm.elements[i].name.indexOf("formatKey")!='-1' )
{
document.mainForm.elements[i].value = "";
}
break;
case "password":
case "textarea":
case "hidden":
/* frm_elements[i].value = "";
break;*/
case "radio":
case "checkbox":
/*if (frm_elements[i].checked)
{
frm_elements[i].checked = false;
}
break;*/
case "select-one":
case "select-multi":
if(document.mainForm.elements[i].name.indexOf("ddlMainSubModule")=='-1')
document.mainForm.elements[i].selectedIndex = 0;
break;
default:
break;
}
}
}

Create a tag for generating #link in Play2.0

while actively learning Play2.0 I am stuck with creating a tag. In the sample application, called computer-database, the following helper is created in the list template:
#****************************************
* Helper generating navigation links *
****************************************#
#link(newPage:Int, newSortBy:String) = #{
var sortBy = currentSortBy
var order = currentOrder
if(newSortBy != null) {
sortBy = newSortBy
if(currentSortBy == newSortBy) {
if(currentOrder == "asc") {
order = "desc"
} else {
order = "asc"
}
} else {
order = "asc"
}
}
// Generate the link
controllers.orders.routes.Work.list(newPage, sortBy, order, currentFilter)
}
Since I want to use this helper in a view templates I thought that the best solution would be to create a tag for it. So I did the following (in my tags package):
#(newPage : Int, newSortBy:String) {
var sortBy = currentSortBy
var order = currentOrder
if(newSortBy != null) {
sortBy = newSortBy
if(currentSortBy == newSortBy) {
if(currentOrder == "asc") {
order = "desc"
} else {
order = "asc"
}
} else {
order = "asc"
}
}
// Generate the link
controllers.orders.routes.Computer.list(newPage, sortBy, order, currentFilter)
}
But, obviously this is not working and I do not know where or why it is not working.
Thanks for the input.
UPDATE WITH ANSWER:
So in Scala template we have to define, just as in Java, the arguments that are passed to this view (Note: that the variables that you will use in the javascript must be passed too!). The template will be compiled as a method as stated in the documentation.
The working tag looks like:
#(newPage : Int, newSortBy : String, currentSortBy: String, currentOrder: String, currentFilter : String ) #{
var sortBy = currentSortBy
var order = currentOrder
if(newSortBy != null) {
sortBy = newSortBy
if(currentSortBy == newSortBy) {
if(currentOrder == "asc") {
order = "desc"
} else {
order = "asc"
}
} else {
order = "asc"
}
}
// Generate the link
controllers.orders.routes.Work.list(newPage, sortBy, order, currentFilter)
}
The trick is that the first version uses a template syntax allowing to write Scala code instead of HTML: #{ val scalaVal = 42}.
In your tag, the template engine interpretes your code as HTML.
If you want to copy-paste this code, don’t forget the leading # before the opening brace.