Dojo Dijit: Why does attr("required", true) fail to set the style "dijitRequired"? or Is there another class which indicates a Dijit is required? - dojo

As far as I can judge, the CSS-Rule "dijitRequired" is used to mark a required input field. Yet, this style is not set when I apply the "required"-Attribute to a dijit, for example, a date dijit:
The Dijit is built as follows:
<input dojoType="dijit.form.DateTextBox" class="l" id="datumsTestID" name="datumsTest" tabindex="5" value="2009-01-01" />
The Attribute is set with the following Javscript code
dijit.byId('datumsTestID').attr('required', true)
Am I doing something wrong or is the style "dijitRequired" not intended to be used as I assume?
For my purposes, I patched ValidationTextBox.js to set/unset the class, but is there a cleaner (meaning: more correct) way to set the class or can I style required fields using other attributes?
ValidationTextBox.js, Dojo 1.3, Line 116
_setRequiredAttr:function(_12){
this.required=_12;
if (_12) dojo.addClass(this.domNode, "dijitRequired");
else dojo.removeClass(this.domNode, "dijitRequired");
dijit.setWaiState(this.focusNode,"required",_12);
this._refreshState();
}

Hmm, I don't see that code in ValidationTextBox.js or anywhere else. My _setRequiredAttr() in 1.3 is:
_setRequiredAttr: function(/*Boolean*/ value){
this.required = value;
dijit.setWaiState(this.focusNode,"required", value);
this._refreshState();
}
Actually I don't see any references to dijitRequired at all, maybe that's something you added to your local copy?

Setting dijitRequired is not enough. dijit.form.DateTextBox has its own internal state. Even if required attribute is set, this widget display error only when it has been blurred. You can disable this mechanism using such subclass:
dojo.provide("my.DateTextBox");
dojo.require("dijit.form.DateTextBox");
dojo.declare("my.DateTextBox", dijit.form.DateTextBox, {
_setRequiredAttr: function(required){
this._hasBeenBlurred = true;
this.inherited(arguments);
}
});

Related

What is the best method for two way binding and disabling editable text fields in Blazor?

For POCOs with lots of fields I find it really convenient to have a simple form/component binding structure like this
< input type="text" bind="#Person.FirstName" />
< input type="text" bind="#Person.LastName" />
< input type="text" bind="#Person.Address" />
Where I then have save or create new buttons/events to deal with object at a time situations. In other words the entire object is bound to an editable component making life super easy to bring objects from the DB for editing and back into the db.
What I find annoying are situations where I have to both display an object's details but make sure parts of it are locked/un-editable based on certain conditions. Let's say I can't change LastName for some business logic reason. This:
< input type="text" bind="#Person.LastName" disabled="#somecondition" />
is unsafe as users can potentially inspect the page, modifying the disabled flag and still cause the two way binding to be enabled and changes overwritten in the existing save event. The workarounds I find are annoying e.g., you can use if code blocks to switch between textboxes and plain-text binding, or you just do all the business logic in the event which introduced more logic for error reporting.
Here is a compromise that I think "works":
if(some_protective_condition) { < input type="text" bind="#Person.Address" /> }
else { < span>#Person.Addressv< /span>}
If I understand correctly most of these workarounds essentially play with the DOM or some aspect of visibility ensuring the editable control does not get rendered. What is the coding pattern would you guys use for these situations of interface level locking?
EDIT/TLDR: Looking for the best/safe least code pattern to maintain two-way binding, display a value and make it editable/non-editable in certain situations.
Suggestions welcome as I am trying to build good solid long term habits.
Tried several techniques. Looking for the best option if I have missed something.
I think what your looking for is InputBase. Creating a component that inherits from InputBase is going to give you access to additional features like validation styles (#CssClass) on top of two-way binding. You can use the #attributes on the input inside your component to add readonly and disabled attributes according to your POCO.
For InputBase have a peek at my answer here. Disabling the input by adding #attributes="GetAttributes()" to the html input.
[Parameter] public bool IsReadOnly { get; set; } = false;
[Parameter] public bool IsDisabled { get; set; } = false;
Dictionary<string,object> GetAttributes()
{
// Required should be handled by the DataAnnotation.
var dict = new Dictionary<string, object>();
if (IsReadOnly)
{
dict.Add("readonly", true);
}
if (IsDisabled)
{
dict.Add("disabled", true);
}
return dict;
}
I think the 'Blazor Way' to integrate business rules into your views is to use AuthorizeView...
<AuthorizeView Policy="CanEditAddress">
<Authorized>
< input type="text" bind="#Person.Address" />
</Authorized>
<NotAuthorized>
< span>#Person.Addressv< /span>
</NotAuthorized>
</AuthorizeView>
The docs show how to populate authorization policies using cascading parameters...
https://learn.microsoft.com/en-us/aspnet/core/blazor/security/?view=aspnetcore-7.0#expose-the-authentication-state-as-a-cascading-parameter
If your rules are not really 'business rules' but are 'presentation rules' (like your forms have an edit mode vs a display mode) then the above pattern is still a good way to go. That is, I would create a class named 'PresentationView' that is the equivalent of AuthorizationView but for presentation rules.

Identifying a Customized Built-In Element without an is= attribute

In my Chessly.github.io project I use Customized Built-In IMG Elements to define SVG chesspieces:
Question: How can I distinguish a regular IMG from a customized IMG?
document.body.append(
document.createElement("IMG", {
is: "white-queen"
});
);
This creates a chesspiece, but does not set the is= attribute
I now explicitly set the is= attribute myself, but since this attribute does nothing and can be set to any value (I use is as an observed attribute in my Custom Element code) it is not a solid way to distinguish IMG elements from Customized IMG elements when walking the DOM.
If I promote a pawn (replace the img src)
<img is=white-pawn/>
with element.setAttribute("is","white-queen")
How can I determine the piece originally was the white pawn?
It still is a white-pawn in the Custom Elements registry.
Am I overlooking something?
Simplified code (with basic SVG shape) in JSFiddle: https://jsfiddle.net/dannye/k0va2j76/
Update: Code (based on correct answer below)
let isExtendedElement = x =>
Object.getPrototypeOf(x).__proto__.constructor.name !== 'HTMLElement';
note! This does NOT catch Autonomous Custom Elements!
maybe better:
let isBaseElement = x =>
Object.getPrototypeOf(Object.getPrototypeOf(x)).__proto__.constructor.name=='Element';
I think adding explicitly the is attribute is the best solution for now.
Else you'll have to deal with the class reference. In your case:
yourElement.constructor === customElements.get( 'circle-image' )
yourElement.constructor === CircleImage //if it's the named class
This supposes that you know the name of the custom elements you want to check.
Else you'll have to go through the protoype chain:
CircleImage --> HTMLImageElement --> HTMLElement --> Element --> Node
If HTMLElement is only the father, it's a built-in element.
If HTMLElement is the grandfather (or grand-grand...), it's probably an extended build-in element.
update
If you use a named class you can also retrieve its name:
elem.constructor.name

how to hide dojo validation error tooltip?

I'm using dojo to validate input fields and if there is an error (for eg: required field) it appears in the dojo tooltip. But, I would like to show error in the custom div instead of tooltip.
So, I'm wondering if there is a way to hide/disable the validate error to appear in the tooltip? If so, I can capture the error message shown in the hidden tooltip and show the result in custom div, which will be consistent with error styling across the application.
Please advise. Thanks.
I would recommend to use the standard Dojo validation mechanism, contrary to what vivek_nk suggests. This mechanism works great in most cases, and covers most situations (required, regular expressions, numbers, dates etc.).
To solve your issue: you can overrule the "dispayMessage" function of a ValidationTextBox (for example).
displayMessage: function(/*String*/ message){
// summary:
// Overridable method to display validation errors/hints.
// By default uses a tooltip.
// tags:
// extension
if(message && this.focused){
Tooltip.show(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
}else{
Tooltip.hide(this.domNode);
}
}
Just create your own ValidationTextBox widget, extend dijit/form/ValidationTextBox, and implement your own "displayMessage" function.
Simple solution for this scenario is not to add the "required" condition at all to those fields. Instead add a separate event handler or function to check for this validation.
For eg: add a function for onBlur event. Check if the field is a mandatory. If so, show message in the custom div as expected.
<input data-dojo-type="dijit/form/TextBox"
id="sampleText" type="text" mandatory="true" onBlur="checkMandatory(this)"/>
function checkMandatory(field) {
if(field.mandatory=='true' && field.value=="") {
alert('value required'); // replace this code with my showing msg in div
} else {
field.domNode.blur();
}
}
This above code snippet does not use Dojo for validation, rather manual. Dojo actually helps to ease this by just adding the attribute "required". If that is not required, then just ignore Dojos help for this case and go native.
So, for all fields, just add the attributes - "mandatory" & "onBlur", and add the above given function for onBlur action for all these fields.

How to define global datepattern for DateTextbox in dojo 1.6

We are using dojo DateTextbox in our applications. Earlier it was displaying local format mm/dd/yyyy
Now end user needs same in dd-MMM-yyyy format. Below code will take care for single textbox.
<input id="startDate" name="startDate" size="10" value="${fromdate }"
dojoType="dijit.form.DateTextBox" required="true"
constraints="{min:'08/22/2008',datePattern : 'dd-MMM-yyyy'}" />
But we have many DateTextBoxs in our project. Adding constraints attribute in all the textfield would be tedious job.
Is there any we can define it globally so it would take care of all the textfields ?
thanks
The proper way to do that is to extend from the DateTextBox and define your constraints.
For example:
declare("CustomDateTextBox", [DateTextBox], {
postCreate: function() {
this.inherited(arguments);
this.set('constraints', {
min: '08/22/2008',
max: new Date(),
datePattern: 'dd-MMM-yyyy'
});
}
});
This ofcourse means that you need to use CustomDateTextBox in stead of DateTextBox. If you really want to use the dijit/form/DateTextBox you can define the name dijit/form/DateTextBox but I don't recommend it because if you would ever need the default DateTextBox too, you can't.
The this.inherited(arguments) is also very important since it will run a super-call, this means that the default postCreate will also be called (without it your widget won't work).
I also made a full example at JSFiddle, if you want to use dijit/form/DateTextBox you can use this example.
EDIT: I just noticed you're using Dojo 1.6. The code won't work but the idea is the same, just extend your widget and it will work.
EDIT 2: This code might work with Dojo 1.6.
A couple of alternatives are
extend the dijit class and add a set of default constraints.
create an object for the constraints
An example is:
var myConstraints = {
min: new Date( 1950, 2, 10),
max: new Date(),
datePattern : 'dd-MMM-yyyy'
};
Then when you declare a box have:
constraints: myConstraints

Difference between id and data-dojo-id

What is the difference between an id and a data-dojo-id in a dojo tag such as this:
<button id="save" data-dojo-type="dijit/form/Button" type="button" data-dojo-attach-event="onClick:save">Save</button>
I try to reference this button to change it's label with:
var myButton = dijit.byId("save");
so that I can change the button label
myButton.set("label", "Add New");
If I use id="save" it works. If I only use data-dojo-id="save" it doesn't work.
I'm fairly new to Dojo so an explanation or tutorial you can point me to would be much appreciated!
data-dojo-id assigns widget into global namespace, i.e. into window object:
<button data-dojo-id="save" data-dojo-type="dijit/form/Button">Save</button>​
so you can access the button directly:
save.set("label", "Add New");
See the difference in action at jsFiddle: http://jsfiddle.net/phusick/7yV56/
EDIT: To answer your questions. I do not use data-dojo-id at all. It pollutes global namespace which is the direct opposite of what the AMD does. Anyway, you can still use something like widgets.save and widgets.rename to minimize the pollution:
<button data-dojo-id="widgets.save" data-dojo-type="dijit/form/Button">Save</button>​
<button data-dojo-id="widgets.rename" data-dojo-type="dijit/form/Button">Rename</button>​
IMO, data-dojo-id is there for progressive enhancement, not for fully-fledged applications.
data-dojo-id just assigns an instance to a variable, so with multiple dijits with the same data-dojo-id the variable will point to the last one assigned (i.e. it'll not be an array).
You can avoid extensive use of registry.byId writing your method to obtain widgets according to your needs. The best way to start is dijit/registy.findWidgets(rootNode, skipNode). Please also note, that dojo/parser.parse(rootNode, options) returns an array of instantiated objects, or more precisely:
Returns a blended object that is an array of the instantiated objects,
but also can include a promise that is resolved with the instantiated
objects. This is done for backwards compatibility. If the parser
auto-requires modules, it will always behave in a promise fashion and
parser.parse().then(function(instances){...}) should be used.
An example of a method I use to assign ContentPane's dijits into its widgets property, which is an object:
_attachTemplateWidgets: function(widgets) {
widgets = widgets || this.getChildren();
for(var each = 0; each < widgets.length; each++) {
var widget = widgets[each];
var attachPoint = widget.params.dojoAttachPoint;
if(attachPoint) {
this.widget[attachPoint] = widget;
}
var children = widget.getChildren();
if(children.length > 0) {
this._attachTemplateWidgets(children);
}
}
}
I put the entire class here: https://gist.github.com/3754324. I use this app.ui._Pane instead of dijit/layout/ContentPane.