On review, if you look at the docs for validator.serverObject:
https://www.smartclient.com/smartcli...r.serverObject
We already show a one-liner to get the existing record. We don't really see anything further we can add here that would assist in writing shorter code; in particular note that "get the DB data and merge", meaning overlaying submitted values on top of the DB record, would not be correct, since if you used the combined record to do validation from that point forward, you would be allowing the client submitted data to override actual stored DB values, and the whole point here is to make sure you are using the real stored values.
So we don't currently see helper methods we could add that would shorten typical code here. If you think there is something to add, please propose a concrete API and show how it would be used in an example validation scenario.
Announcement
Collapse
No announcement yet.
X
-
We would not recommend Velocity for more than a one-liner.
You cannot do step-through debugging with Server Script, but it is appropriate and useful for developers who feel comfortable writing a few lines of code without step-through debugging support.
Leave a comment:
-
Hi Isomorphic,
thanks. You are right and I do know about these. Velocity validator that are more complex than one line turned out to be "write only" and un-debuggable (=breakpoint step-though) for me.
Even if it's overhead in LOC I much more like the Java validators. I did not try server script so far. This would be un-debuggable (=breakpoint step-though) as well, correct?
Best regards
Blama
Leave a comment:
-
Note that we already set up shortcut variables if you are using serverCondition with Velocity and/or Server Script (which would also make your test case much simpler - just a short snippet of Java/Groovy inside the .ds.xml file).
But the idea of also making the same functionality available to ordinary Java code is a good one, and we’’ll note it down.
Leave a comment:
-
Hi Isomorphic,
I created a new thread for a remaining issue of #6 here.
W.r.t. to the enhancement suggestion in #1/#4 and #12:- I think that the one of #1/#4 is invalid as you are already doing this for validate requests (like you say in #5). Most likely can't do this for update requests (as this would result in these fields being updated as well (to their old/current value, but anyway)).
- The enhancement suggestion from #12 would be nice, but is very easy most likely. I'll report back with a code suggestion for a base class.
Blama
Leave a comment:
-
Hi Isomorphic,
agreed. It's only annoying to have to write the "get DB data and merge" inside the every validator's server code.
Of course this is easy via inheritance but perhaps you can make this available in a validator server side base class for all users as an enhancement?
We'll report back if there is a problem from #6 left.
Best regards
Blama
Leave a comment:
-
Both have complete data from the client via DSRequest.oldValues. But be careful of writing validators wrongly by relying on client data, which may be stale or spoofed.
Leave a comment:
-
Hi Isomorphic,
w.r.t to the flow then there is a problem. In #5 you say:
Originally posted by Isomorphic View PostFor the serverCustom validator and server validators in general the entire record is sent to the server so your 1st point is covered.- Check all clientSide validators
- On error, show validation error and stop
- On success, issue Update-DSRequest (which run serverside validation logic)
Best regards
Blama
Leave a comment:
-
Docs are correct and agree with what we’ve told you. validateOnChange on a validator means validation will be triggered on change for any item where that validator is applied. It would obviously be wrong for validateOnChange on one validator on one field to effectively flip validateOnChange to true for all fields in the form.
Your flow is not correct. If there is an update and validators pass it is processed on the server immediately. There would be no reason to go back to the client first.
Leave a comment:
-
Hi Isomorphic,
Originally posted by Isomorphic View PostYou must set validateOnChange on any fields where you want validation on change. Setting a field as a dependentField does not affect the setting of validateOnChange for those fields - it still has its default value of false.
If true, validator will be validated when each item's "change" handler is fired as well as when the entire form is submitted or validated. If false, this validator will not fire on the item's "change" handler.
Note that this property can also be set at the form/grid or field level; If true at any level and not explicitly false on the validator, the validator will be fired on change - displaying errors and rejecting the change on validation failure.Originally posted by Isomorphic View PostWe're unclear on what you’re reporting in the last item. If a validator fails there will not be an update. You should double-check what requests are being sent and with what data, and what your validator returns in each case. Let us know if you, after looking closely, you believe you've found a framework bug.- Check all clientSide validators
- On error, show validation error and stop
- Check all serverSide validators via one (or more?) validate-DSRequests
- Wait for the DSRequest to return
- On error, show validation errors and stop
- On success, issue Update-DSRequest (which will again run serverside validation logic)
Blama
Leave a comment:
-
You must set validateOnChange on any fields where you want validation on change. Setting a field as a dependentField does not affect the setting of validateOnChange for those fields - it still has its default value of false.
We’re unclear on what you’re reporting in the last item. If a validator fails there will not be an update. You should double-check what requests are being sent and with what data, and what your validator returns in each case. Let us know if you, after looking closely, you believe you’ve found a framework bug.
Leave a comment:
-
Hi Isomorphic,
1.Regarding this in #5:
Note that server validators are not triggered from immediate field changes by default because they are asynchronous and could take substantial time to return. Therefore in your test case the serverCustom validator will not trigger until saving the row. This can be overridden by setting validateOnChange on the validator.
Code:<DataSource ID="supplyItem" serverType="sql" tableName="supplyItem" titleField="itemName" testFileName="/examples/shared/ds/test_data/supplyItem.data.xml" dbImportFileName="/examples/shared/ds/test_data/supplyItemLarge.data.xml" > <fields> <field name="itemID" type="sequence" hidden="true" primaryKey="true"/> <field name="itemName" type="text" title="Item" length="128" required="true"> <validators> <validator type="serverCustom" dependentFields="inStock, unitCost"[B] validateOnChange="true"[/B]> <serverObject lookupStyle="new" className="com.smartgwt.sample.server.listener.TestValidator" /> <errorMessage>$errorMessage</errorMessage> </validator> </validators> </field> <field name="SKU" type="text" title="SKU" length="10" required="true"/> <field name="description" type="text" title="Description" length="2000"/> <field name="category" type="text" title="Category" length="128" required="true" foreignKey="supplyCategory.categoryName"/> <field name="units" type="enum" title="Units" length="5"> <valueMap> <value>Roll</value> <value>Ea</value> <value>Pkt</value> <value>Set</value> <value>Tube</value> <value>Pad</value> <value>Ream</value> <value>Tin</value> <value>Bag</value> <value>Ctn</value> <value>Box</value> </valueMap> </field> <field name="unitCost" type="float" title="Unit Cost" required="true"> <validators> <validator type="floatRange" min="0" errorMessage="Please enter a valid (positive) cost"/> <validator type="floatPrecision" precision="2" errorMessage="The maximum allowed precision is 2"/> </validators> </field> <field name="inStock" type="boolean" title="In Stock"/> <field name="nextShipment" type="date" title="Next Shipment"/> </fields> </DataSource>
In the above example, no validation is triggered if I change for example SKU. The only validation made here is, when I change the ItemName. Am I missing something here, or did I misunderstand the docs?
2. If I extend the supplyItem.ds.xml with dependentFields like this:
Code:<DataSource ID="supplyItem" serverType="sql" tableName="supplyItem" titleField="itemName" testFileName="/examples/shared/ds/test_data/supplyItem.data.xml" dbImportFileName="/examples/shared/ds/test_data/supplyItemLarge.data.xml" > <fields> <field name="itemID" type="sequence" hidden="true" primaryKey="true"/> <field name="itemName" type="text" title="Item" length="128" required="true"> <validators> <validator type="serverCustom" validateOnChange="true"> <serverObject lookupStyle="new" className="com.smartgwt.sample.server.listener.TestValidator" /> <errorMessage>$errorMessage</errorMessage> [B] <dependentFields> <dependentField>inStock</dependentField> </dependentFields>[/B] </validator> </validators> </field> <field name="SKU" type="text" title="SKU" length="10" required="true"/> <field name="description" type="text" title="Description" length="2000"/> <field name="category" type="text" title="Category" length="128" required="true" foreignKey="supplyCategory.categoryName"/> <field name="units" type="enum" title="Units" length="5"> <valueMap> <value>Roll</value> <value>Ea</value> <value>Pkt</value> <value>Set</value> <value>Tube</value> <value>Pad</value> <value>Ream</value> <value>Tin</value> <value>Bag</value> <value>Ctn</value> <value>Box</value> </valueMap> </field> <field name="unitCost" type="float" title="Unit Cost" required="true"> <validators> <validator type="floatRange" min="0" errorMessage="Please enter a valid (positive) cost"/> <validator type="floatPrecision" precision="2" errorMessage="The maximum allowed precision is 2"/> </validators> </field> <field name="inStock" type="boolean" title="In Stock"/> <field name="nextShipment" type="date" title="Next Shipment"/> </fields> </DataSource>
Code:package com.smartgwt.sample.server.listener; import java.util.Map; import javax.servlet.http.HttpServletRequest; import com.isomorphic.datasource.DSResponse; import com.isomorphic.datasource.DataSource; import com.isomorphic.datasource.Validator; import com.isomorphic.log.Logger; public class TestValidator { Logger log = new Logger(DSResponse.class.getName()); public boolean condition(Object value, Validator validator, String fieldName, @SuppressWarnings("rawtypes") Map record, DataSource ds, HttpServletRequest httpServletRequest) throws Exception { log.info("TestValidator called"); String valueName = (String) record.get("itemName"); if (!valueName.equals("Account Book 168 Page Hardcover A4 4MC")) { validator.addErrorMessageVariable("errorMessage", "Error"); return false; } else return true; } }
3. With the setup from 2. I select a row by double-click. Then, I check/uncheck the dependentField "inStock" and click outside, to send the request. In the Developer Console, I first get an validation-error but then an successful update on that row. Is that behaviour wanted?
Thanks in Advance!
Kind Regards
Leave a comment:
-
Validators can be written that are dependent on other fields and should be triggered on changes to them as well. So in your case you are saying that the serverCustom validator on itemName should be rechecked on changes to inStock and unitCost in addition to direct itemName changes. For the serverCustom validator and server validators in general the entire record is sent to the server so your 1st point is covered.
Note that server validators are not triggered from immediate field changes by default because they are asynchronous and could take substantial time to return. Therefore in your test case the serverCustom validator will not trigger until saving the row. This can be overridden by setting validateOnChange on the validator.
Finally, the specification of dependentFields in the datasource isn't correct. It takes an array of strings so use:
Code:<dependentFields> <dependentField>inStock</dependentField> <dependentField>unitCost</dependentField> </dependentFields>
Leave a comment:
-
Hi Isomorphic,
this means the idea is what I described as 2nd point in #1.
Can you log the 1st point in #1 as enhancement? It would help to solve more easy cases of multi-field validation, which otherwise would require a SQLDataSource subclass and an @override of validateRecord(), like you describe here.
Please disregard the "missing docs" point I made here earlier. I just saw that it is in the parent class.
Best regards
BlamaLast edited by Blama; 17 Dec 2018, 09:44.
Leave a comment:
-
Hi Isomorphic,
please see the attached BuiltInDS-based testcase (v12.0p_2018-12-13).
As you can see, the serverCustom validator on itemName is only called for changes of itemName directly, not for changes of either unitCost or inStock.
Also having just inStock as attribute value does not change anything.
BuiltInDS.java:
Code:package com.smartgwt.sample.client; import com.google.gwt.core.client.EntryPoint; import com.smartgwt.client.Version; import com.smartgwt.client.core.KeyIdentifier; import com.smartgwt.client.data.AdvancedCriteria; import com.smartgwt.client.data.Criterion; import com.smartgwt.client.data.DataSource; import com.smartgwt.client.data.SortSpecifier; import com.smartgwt.client.types.OperatorId; import com.smartgwt.client.types.SortDirection; import com.smartgwt.client.util.Page; import com.smartgwt.client.util.PageKeyHandler; import com.smartgwt.client.util.SC; import com.smartgwt.client.widgets.IButton; import com.smartgwt.client.widgets.Window; import com.smartgwt.client.widgets.events.ClickEvent; import com.smartgwt.client.widgets.events.ClickHandler; import com.smartgwt.client.widgets.grid.ListGrid; import com.smartgwt.client.widgets.grid.ListGridField; import com.smartgwt.client.widgets.layout.VLayout; public class BuiltInDS implements EntryPoint { private VLayout mainLayout; private IButton recreateBtn; public void onModuleLoad() { KeyIdentifier debugKey = new KeyIdentifier(); debugKey.setCtrlKey(true); debugKey.setKeyName("D"); Page.registerKey(debugKey, new PageKeyHandler() { public void execute(String keyName) { SC.showConsole(); } }); mainLayout = new VLayout(20); mainLayout.setWidth100(); mainLayout.setHeight100(); recreateBtn = new IButton("Recreate"); recreateBtn.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { recreate(); } }); mainLayout.addMember(recreateBtn); recreate(); mainLayout.draw(); } private void recreate() { Window w = new Window(); w.setWidth("95%"); w.setHeight("95%"); w.setMembersMargin(0); w.setModalMaskOpacity(70); w.setTitle(" (" + Version.getVersion() + "/" + Version.getSCVersionNumber() + ")"); w.setTitle("TITLE" + w.getTitle()); w.setShowMinimizeButton(false); w.setIsModal(true); w.setShowModalMask(true); w.centerInPage(); final ListGrid supplyItemGrid = new ListGrid(DataSource.get("supplyItem")); supplyItemGrid.setHeight100(); supplyItemGrid.setWidth100(); supplyItemGrid.setCanEdit(true); supplyItemGrid.setAutoFetchData(false); ListGridField itemID = new ListGridField("itemID"); ListGridField itemName = new ListGridField("itemName"); ListGridField sku = new ListGridField("SKU"); ListGridField category = new ListGridField("category"); ListGridField units = new ListGridField("units"); ListGridField unitCost = new ListGridField("unitCost"); ListGridField inStock = new ListGridField("inStock"); ListGridField nextShipment = new ListGridField("nextShipment"); supplyItemGrid.setFields(itemID, itemName, sku, category, units, unitCost, inStock, nextShipment); supplyItemGrid.setSort(new SortSpecifier[] { new SortSpecifier(itemName.getName(), SortDirection.ASCENDING) }); supplyItemGrid.fetchData(new AdvancedCriteria(new Criterion(itemName.getName(), OperatorId.STARTS_WITH, "A"))); w.addItem(supplyItemGrid); w.show(); } }
Code:package com.smartgwt.sample.server.listener; import java.util.Map; import javax.servlet.http.HttpServletRequest; import com.isomorphic.datasource.DSResponse; import com.isomorphic.datasource.DataSource; import com.isomorphic.datasource.Validator; import com.isomorphic.log.Logger; public class TestValidator { Logger log = new Logger(DSResponse.class.getName()); public boolean condition(Object value, Validator validator, String fieldName, @SuppressWarnings("rawtypes") Map record, DataSource ds, HttpServletRequest httpServletRequest) throws Exception { log.info("TestValidator called"); return true; } }
Code:<DataSource ID="supplyItem" serverType="sql" tableName="supplyItem" titleField="itemName" testFileName="/examples/shared/ds/test_data/supplyItem.data.xml" dbImportFileName="/examples/shared/ds/test_data/supplyItemLarge.data.xml" > <fields> <field name="itemID" type="sequence" hidden="true" primaryKey="true"/> <field name="itemName" type="text" title="Item" length="128" required="true"> [B] <validators> <validator type="serverCustom" dependentFields="inStock, unitCost"> <serverObject lookupStyle="new" className="com.smartgwt.sample.server.listener.TestValidator" /> <errorMessage>$errorMessage</errorMessage> </validator> </validators>[/B] </field> <field name="SKU" type="text" title="SKU" length="10" required="true"/> <field name="description" type="text" title="Description" length="2000"/> <field name="category" type="text" title="Category" length="128" required="true" foreignKey="supplyCategory.categoryName"/> <field name="units" type="enum" title="Units" length="5"> <valueMap> <value>Roll</value> <value>Ea</value> <value>Pkt</value> <value>Set</value> <value>Tube</value> <value>Pad</value> <value>Ream</value> <value>Tin</value> <value>Bag</value> <value>Ctn</value> <value>Box</value> </valueMap> </field> <field name="unitCost" type="float" title="Unit Cost" required="true"> <validators> <validator type="floatRange" min="0" errorMessage="Please enter a valid (positive) cost"/> <validator type="floatPrecision" precision="2" errorMessage="The maximum allowed precision is 2"/> </validators> </field> <field name="inStock" type="boolean" title="In Stock"/> <field name="nextShipment" type="date" title="Next Shipment"/> </fields> </DataSource>
Blama
Leave a comment:
Leave a comment: