I am working with an application which generates different kind of reports.
In couple of reports, we are using Longitude/Latitude as an input box. We need to put some validation logic for Latitude/Longitude e.g The latitude must be a number between -90 and 90 and the longitude between -180 and 180.
So what is the best-fit approach for that? Shall I create a Directive for same or any other idea?
In case of Directive if some can share some code then it's helpful.
Depends on how you wish to handle this... technically, HTML5 already can do that
<input type="number" name="longtitude" min="-180" max="180">
<input type="number" name="latitude" min="-90" max="90">
But you can of course use a directive, if you have more fields that you want to restrict in terms of upper and lower limits. It has several benefits while doing this. We can exert more control about how our components are used. The pure HTML5 solution has the drawback, that you can still input numbers that are greater than those.
EDIT: I also created a directive for you, that looks somewhat like this in the HTML Template...
<input #inputReference appNumberRange [inputRefObject]="{inputRef: inputReference, minValue: -180, maxValue: 180}" type="number"
placeholder=" - Longitude- ">
So, what happened here? I created a directive with the name NumberRange, which has the input parameter "inputRefObject" that takes a template variable #inputReference in this case, the min and max value.
But now let's go to the directive...
import { Directive, HostListener, Input } from '#angular/core';
#Directive({
selector: '[appNumberRange]'
})
export class NumberRangeDirective {
constructor() { }
#Input() inputRefObject: {inputRef: any, minValue: number, maxValue: number};
#HostListener('keyup', ['$event'])
restrictNumberRange() {
const {inputRef, minValue, maxValue} = this.inputRefObject;
if(inputRef.value > maxValue) {
inputRef.value = maxValue;
} else if(inputRef.value < minValue) {
inputRef.value = minValue
} else {
// Do nothing, or perhaps do something else...
return;
}
}
}
It's kept rather simplistic and I wouldn't vouch for best practices here, but it should do what you wish it to do.
We use the hostlistener on the object we're using the keyup event on. Technically, I wouldn't even need to pass the event in the HostListener arguments, but I left it there in case you needed it.
But yeah, if the number is larger than the maxnumber, the value in the input is changed to the highest allowed input and vice versa for the lowest number. From there you can basically expand the way you like. You could perhaps check if the inputs are actually only of the number type! You could check the value the moment something is pasted into the input and so forth
Related
Recently, in Blazor 7, a feature has been added to make it easier to bind and call the method based on changes in the bound expression.
In .NET 7, you can now easily run async logic after a binding event has completed using the new #bind:after modifier:
<input #bind="searchText" #bind:after="PerformSearch" />
#code {
string searchText = "";
async Task PerformSearch()
{
// Do something async with searchText
}
}
In this example, the PerformSearch async method runs automatically after any changes to the search text are detected.
Another method has been added too. The #bind:get and #bind:set modifiers are always used together. The #bind:get modifier specifies the value to bind to, and the #bind:set modifier specifies a callback that's called when the value changes.
The questions are:
What is the difference between #bind:after="PerformSearch" and #bind:set="PerformSearch"? Both of these seem to call the PerformSearch after the searchText is changed.
Where is the use of each?
What is the difference between #bind:after="PerformSearch" and #bind:set="PerformSearch"?
You should only use #bind:after="PerformSearch" with #bind="searchText",in which case the bind will set the value of searchText, so you shouldn't also try and set it in PerformSearch.
If you use #bind:set="PerformSearch" then you must set the value of searchText in PerformSearch, and use #bind:get="searchText".
Where is the use of each?
The MS Docs article I think gives a good guide. It all depends on your knowledge level on components.
It's important to understand two points:
This is Razor syntax, not C#.
It's just syntactic sugar: high level functionality, shorthand Razor directives to encapsulate existing functionality.
Also note:
There's been a lot of MS Blazor Team activity on this subject since 7.0 was released. See https://github.com/dotnet/aspnetcore/issues/44957 for details on problems with the way the Razor compiler treats the #bind directives.
There are some updates to the MS Docs on the subject - https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-7.0?view=aspnetcore-7.0#blazor-custom-elements.
Here's my demo page for this answer.
#page "/"
<PageTitle>Index</PageTitle>
<input class="form-control mb-3" type="text" #bind:get="this.Value" #bind:set="ValueSetter" #bind:event="oninput" />
<input class="form-control mb-3" type="text" #bind:get="this.Value" #bind:after="ValueSetter" />
<input class="form-control mb-3" type="text" #bind="this.Value" #bind:after="DoSearch" #bind:event="oninput"/>
<div class="alert alert-info m-2 p-2">
#Value
</div>
<div class="alert alert-primary m-2 p-2">
#message
</div>
#code {
private string? Value;
private string message = "Not Set";
private async Task DoSearch()
{
await Task.Delay(1000);
message= $"Set at {DateTime.Now.ToLongTimeString()}";
}
private void ValueSetter(string __value)
=> this.Value = __value;
private Task SearchSetter(string __value)
{
this.searchText = __value;
return DoSearch();
}
}
Let's look at the actual C# code the Razor compiler builds.
This is the code snippet when just using bind:set=this.ValueSetter:
__builder.AddAttribute(8, "oninput", EventCallback.Factory.CreateBinder(
this,
CompilerServices.RuntimeHelpers.CreateInferredBindSetter(
callback: this.ValueSetter,
value: this.Value
),
this.Value));
This simply calls the setter delegate assigned to set.
This is the code snippet when using :bind=this.Value and #bind:after=DoSearch:
__builder.AddAttribute(14, "oninput", EventCallback.Factory.CreateBinder(
this, CompilerServices.RuntimeHelpers.CreateInferredBindSetter(
callback: __value => {
this.Value = __value;
return RuntimeHelpers.InvokeAsynchronousDelegate(callback: DoSearch);
},
value: this.Value),
this.Value));
It's a little more complicated. The compiler builds the equivalent to this:
Task AnonymousMethod(string __value)
{
this.Value = __value;
return DoSearch()
}
A Note on Development Environment Errors
Depending on your development environment, you will get errors with certain combinations. Some of which at the moment appear to be misleading or totally wrong. They will be fixed shortly.
Quote from Dan Roth: Hi folks. The VS fix for this just missed the window for 17.4.4 but should be addressed in the next VS patch update in February. We apologize for the wait and thank you for your patience!
In Visual Studio.
This is a correct error:
<InputText class="form-control" #bind-Value:get="this.searchText" #bind-Value:set="this.SetSearchText" #bind-Value:after="DoSearch" />
Severity Code Description Project File Line Suppression State
Error (active) RZ10019 Attribute 'bind-Value:after' can not be used with 'bind-Value:set'. Invoke the code in 'bind-Value:after' inside 'bind-Value:set' instead.
While this is bull!
<input class="form-control mb-3" type="text" #bind:get="this.Value" #bind:set="ValueSetter" #bind:event="oninput" />
And while it gives this error compiles and runs!
Severity Code Description Project File Line Suppression State
Error (active) CS1503 Argument 3: cannot convert from 'Microsoft.AspNetCore.Components.EventCallback<string>' to 'System.Action<string?>'
And this line:
<input class="form-control mb-3" type="text" #bind:get="this.Value" #bind:after="ValueSetter" />
Compiles but is obviously also total bull.
__builder.AddMarkupContent(9, "\r\n\r\n<input class=\"form-control mb-3\" type=\"text\" #bind:get=\"this.Value\" #bind:after=\"ValueSetter\">\r\n\r\n");
Why is it #bind:get+#bind:set and not just #bind+#bind:set?
Because if you see <input #bind="#val" #bind:set="#MyMethod" /> often, it creates confusion:
It looks as if the #bind:set is what makes it a two-way binding, and that you could make it one-way by removing that. Whereas in fact that would be wrong (you'd still have a two-way binding, just one that behaves differently now).
It looks as if it would be equivalent to write <input value="#val" #bind:set="#MyMethod />, and it almost is, but not quite because the formatting logic would differ. Much better not to create the ambiguity and have one correct solution.
We can avoid the above problems by having a compiler rule that #bind:get and #bind:set must always be used as a pair - you can't just have one of them and not the other (nor can you have them with #bind). So none of the weird cases will arise.
Couldn't you use #bind:set to achieve (in effect) #bind:after, and hence we don't need #bind:after?
Theoretically yes. You could #bind:set to a method that writes to your field and then runs your async logic. However, this is far less obvious for newcomers, and is less convenient in common cases. And it invites mistakes: if you do something async before setting the field, the UI will temporarily revert and generally behave badly. So it's valuable to have #bind:after for convenience and to guide correct usage. We can regard #bind:get/#bind:set as a more advanced case mainly for people implementing bindable components, as it gives them a really clean and safe solution, and such developers are advanced enough to understand that they just shouldn't do async work before calling ValueChanged.
Can you use all three at once, e.g., <input #bind:get="#value" #bind:set="#MyMethod" #bind:after="#DoStuff" />?
Sure, why not? I think that the generated logic should await MyMethod before calling DoStuff, since "after" feels like it means "after all the work involved in calling set". It's an edge case but I can't think of any problems this will cause nor any major increase in implementation cost.
Do other #bind modifiers like #bind:event and #bind:format work with this?
Yes, and that's partly why it's a big improvement over manual value/onchange pairs.
you can refer this link get more idea https://github.com/dotnet/aspnetcore/issues/39837
I want to hook up 2 input controls to each other, so when one changes, it updates the other. As an example, its 2 percentage fields, if I set one to 80, the other will get set to 20 to balance it to total 100.
Both inputs are in a form group, so I feel like I should use these and methods on these to subscribe to value changes and not touch native elements or events.
Can anyone suggest the base way to go about it?
You can subscribe to valueChanges for that particular formControlName.
For example in your controller you can do
this.myForm.get('firstControl').valueChanges.subscribe(val => {
this.myForm.patchValue({
secondControl:100-val
});
});
You should repeat the same to listen when the second control changes.
Remember to validate with max values too i.e set maximum to 100.
A simple way would be to use the ngModelChange event emitted by one input field to update the other.
In the code below it updates input #2 when input #1 changes, and vice versa.
Input 1: <input type="text" [(ngModel)]="first" (ngModelChange)="second = 100 - first" />
Input 2: <input type="text" [(ngModel)]=second (ngModelChange)="first = 100 - second"/>
Note: this is pretty basic, all inline, but in your example you'd want to add some error handling for non-numeric characters, range values > 100 etc. So you might want to define the handler for ngModelChange in your components definition (presumably in typescript) rather than in the template like I've done here.
I'm using angular reactive forms and was able to create a component that binds to the form FormGroup and the names of the controls, like this:
<app-percentage-balance
[formGroup]="form"
firstControlName="firstControl"
secondControlName="firstControl">
in my component I have:
#Input() formGroup: FormGroup;
#Input() firstControlName: string;
#Input() secondControlName: string;
public ngOnInit(): void {
this.firstControl = this.formGroup.controls[this.firstControlName] as FormControl;
this.secondControl = this.formGroup.controls[this.secondControlName] as FormControl;
this.firstControl.statusChanges.subscribe((status) => {
if (status == "VALID") {
// do stuff
}
});
this.secondControl.statusChanges.subscribe((status) => {
if (status == "VALID") {
// do stuff
}
});
}
I was tring to use material component in angular 2 dart as a number input:
<material-input type="number"></material-input>
but it behaves like a normal input. In docs it sais it supports type "number". Am i doing anything wrong? Or isn't number type implemented yet?
Thank you for any suggestions.
I can share my personal experiment trying to have a number (integer) input. It does not work perfectly on all browsers but I wanted to have the proper keyboard shown on Android and iOS. What I did was forcing the type on the inner input element programmatically. It seems that on Firefox it does not prevent entering text but does display a message ("Please enter a number"). It does not handle decimal neither (i.e. it does expect an integer)
initInputNumber(MaterialInputComponent inputComponent) {
inputComponent.type = "number";
InputElement inputElement = inputComponent.inputEl.nativeElement;
inputElement.type = "number";
// http://stackoverflow.com/questions/6178556/phone-numeric-keyboard-for-text-input
// As of mid-2015, I believe this is the best solution:
// <input type="number" pattern="[0-9]*" inputmode="numeric">
inputElement.attributes["inputmode"] = "numeric";
inputElement.pattern = "[0-9]*"; // this and only this works 0-9
}
I don't know if that's the best solution but I find it hard to have a complete cross-browser solution
I think you need to set an errorMsg
<material-input type="number" errorMsg="That's not a number"></material-input>
This line https://github.com/dart-lang/angular2_components/blob/a0eff879a6cb347b8beb95ed758c02c6dd9dfaa0/lib/src/components/material_input/material_input.dart#L232 seems to indicate that type="tel" and type="number" are set to text for the internal input element, while this line https://github.com/dart-lang/angular2_components/blob/a0eff879a6cb347b8beb95ed758c02c6dd9dfaa0/lib/src/components/material_input/material_input.dart#L61 says that errorMsg is used when and invalid number is entered when type="number".
I'm trying to get regex pattern in input type number to show only numbers and dots.
I tried something like this.
<input type="number" pattern="[0-9.]*">
<input type="tel">
Both are showing only numbers (0-9), but not displaying . (dot). I need to use dot in input field.
Is it possible thru html5? Or Shall I go with javascript?
Note: This is working fine in Android, but . (dot) not displaying in iphones
I need to display mobile keypad like this..
Any help regarding this?
If you only specify "type=number" it will display keypad on iPhone like:
And if you specify pattern like <input type="number" pattern="\d*"/> or <input type="number" pattern="[0-9]*" />, then keypad on iPhone will be like :
Still it cannot display dot(.), currently there is no pattern to handle such case.
So you may opt for <input type="tel" /> which will provide keypad like:
Please refer to below links for more details on inputs for iOS:
http://bradfrost.com/blog/mobile/better-numerical-inputs-for-mobile-forms/
http://blog.pamelafox.org/2012/05/triggering-numeric-keyboards-with-html5.html
https://about.zoosk.com/nb/engineering-blog/mobile-web-design-use-html5-to-trigger-the-appropriate-keyboard-for-form-inputs/
http://mobiforge.com/design-development/html5-mobile-web-forms-and-input-types
http://www.petefreitag.com/item/768.cfm
http://html5tutorial.info/html5-contact.php
Hope this will help you. :)
Updates for customization (reference: https://stackoverflow.com/a/20021657/1771795)
You can do some customization using javascript.
Lets take example of currency input with decimals pattern in which e.which to read CharCode entered and then push it into an array (before) which represents digits before decimal mark and another array (after) to move values from (before) array past the decimal mark.
complete fiddle link
HTML:
<input type="tel" id="number" />
JS
Variables and functions:
// declare variables
var i = 0,
before = [],
after = [],
value = [],
number = '';
// reset all values
function resetVal() {
i = 0;
before = [];
after = [];
value = [];
number = '';
$("#number").val("");
$(".amount").html("");
}
// add thousand separater
function addComma(num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
Main code:
// listen to keyup event
$("#number").on("keyup", function (e, v) {
// accept numbers only (0-9)
if ((e.which >= 48) && (e.which <= 57)) {
// convert CharCode into a number
number = String.fromCharCode(e.which);
// hide value in input
$(this).val("");
// main array which holds all numbers
value.push(number);
// array of numbers before decimal mark
before.push(value[i]);
// move numbers past decimal mark
if (i > 1) {
after.push(value[i - 2]);
before.splice(0, 1);
}
// final value
var val_final = after.join("") + "." + before.join("");
// show value separated by comma(s)
$(this).val(addComma(val_final));
// update counter
i++;
// for demo
$(".amount").html(" " + $(this).val());
} else {
// reset values
resetVal();
}
});
Reset:
// clear arrays once clear btn is pressed
$(".ui-input-text .ui-input-clear").on("click", function () {
resetVal();
});
Result:
Not every input type and attribute is supported in all browsers. In general, most modern browsers from IE10+ include basics such as email and number.
The browser will revert to a standard text input when a specific type and ignore attributes when those values are not supported.
So you should use a good regular expression pattern.
for example
<input type="tel" name="tel" pattern="^(?:\(\d{3}\)|\d{3})[- . ]?\d{3}[- . ]?\d{4}$" />
1234567899
123 456 7899
123-456-7899
123.456.7899
supported
Browser support for 'tel' type
Android (yes)
iOS (yes)
IE (yes)
Mobile (yes)
Opera (yes)
Mobile (yes)
Opera (yes)
Classic (yes)
Opera Mini (no)
Firefox (yes)
Mobile (yes)
Chrome for Android (yes)
(Sources: caniuse.com, DeviceAtlas, mobilehtml5.org)
Browser support for pattern attribute
But the pattern attribute is supported in Internet Explorer 10, Firefox, Opera, and Chrome.
And is not supported in Internet Explorer 9 and earlier versions, or in Safari.
For iOS use the input attribute type="number", inputmode="decimal".
This will show the number pad with the “dots” on iOS 12.3+.
I had a similar scenario whereby I needed to support both comma and point as both the decimal mark and digit grouping [see here]
E.g.
1.00 / 1,00
1,000,000.00 / 1.000.000,00
At the same time the scenario required that the number keypad was displayed on mobile devices.
The initial implementation combined the 'number' type with the pattern attribute.
<input type="number" pattern="^(0*[,.]*[0-9][0-9]*([,.][0-9]+)*|[0-9]?[,.][0-9]*[1-9][0-9]*)$" required />
However the number validation failed inputs that the pattern would allow. This meant the field and thus form were marked as invalid.
The solution was to change the type to 'tel'.
<input type="tel" pattern="^(0*[,.]*[0-9][0-9]*([,.][0-9]+)*|[0-9]?[,.][0-9]*[1-9][0-9]*)$" required />
Mobile users would now see a number keypad by default, and the pattern validation would be used to validate the input.
Unfortunately it's not possible to achieve the exact functionality that you're looking for is not possible. However there is a kind of "hack" available which will give similar functionality:
http://www.brownphp.com/2011/05/iphone-currency-input-web-apps/ (Link broken)
It's a bit of JS which fills in the decimal automatically when the user starts typing, like this: 0.01 -> 0.12 -> 1.23 -> 12.34 . So this can be used for similar effect.
There are several tricks for displaying different keyboards on mobile devices for HTML 5 inputs (i.e. <input> tags).
For example, some are documented on Apple's website, Configuring the Keyboard for Web Views.
These are great for usability, but when it comes to an input for for international postal codes (mostly numeric, but letters allowed), we're left with some poor options. Most people recommend using the pattern="\d*" trick to show the numeric keyboard, but that doesn't allow for letter input.
The type="number" input type shows the regular keyboard but shifted to the numeric layout:
This works well for iOS devices, but it makes Chrome think the input must be a number and even changes the input's behavior (up/down increment and decrement the value).
Is there any way to get iOS to default to the numeric layout, but still allow for alphanumeric input?
Basically, I want the iOS behavior for type="number" but I want the field to behave like a regular text field on desktop browsers. Is this possible?
UPDATE:
Sniffing the user-agent for iOS and using the type="number" input type is not an option. type="number" is not meant for string values (like postal codes), and it has other side effects (like stripping leading zeros, comma delimiters, etc) that make it less than ideal for postal codes.
Will this work?
HTML:
<input type="tel" pattern="[0-9]*" novalidate>
This should give you the nice numeric keyboard on Android/iOS phone browsers, disable browser form validation on desktop browsers, not show any arrow spinners, allows leading zeros, and allows commas and letters on desktop browsers, as well as on iPad.
Android / iOS phones:
Desktop:
iPad:
Browsers currently have no proper way of representing numeric codes like postcodes and credit card numbers. The best solution is to use type='tel' which will give you a number keypad and ability to add any character on desktop.
Type text and pattern='\d*' will give you a number keypad but only on iOS.
There is an HTML5.1 proposal for an attribute called inputmode which would allow you to specify keypad regardless of type. However not is not currently supported by any browser.
I would also recommend having a look at the Webshim polyfill library which has a polyfill method for these types of inputs.
A quick google search found this Stackoverflow question.
HTML
<input type="text">
Javascript
$('input[type="text"]').on('touchstart', function() {
$(this).attr('type', 'number');
});
$('input[type="text"]').on('keydown blur', function() {
$(this).attr('type', 'text');
});
The input type is switched before the form can be validated, showing the correct keyboard without messing up the value. If you only want it to run on iOS, you will probably have to use the user agent.
Stackoverflow on detecting iOS
An update to this question in iOS 11. You can get the number keypad by simply adding the pattern attribute (pattern="[0-9]*") to any input with a number type.
The following works as expected.
<input type="number" pattern="[0-9]*">
This also works.
<input type="number" pattern="\d*">
#davidelrizzo posted part of the answer, but the comments from #Miguel Guardo and #turibe give a fuller picture but are easy to miss.
This will make the numerical side of the ios keyboard display by default and maintains the ability to switch to the alphabetical side. When the html input type changes, the device changes the keyboard to match the appropriate type.
(function ($) {
var control = $('#test2');
var field = $('#test1');
control.bind('click', function () {
if (control.is(':checked')) {
field.attr('type', 'text');
} else {
field.attr('type', 'number');
}
})
}(jQuery));
<input type="number" id="test1" value="123" />
<input id="test2" type="checkbox" />Change
alternate demo: http://jsfiddle.net/davidcondrey/dbg1L0c0/3/embedded/result/
If you want the large numerical format keyboard (the telephone style) you can adjust the code accordingly and it still works:
(function ($) {
var control = $('#test2');
var field = $('#test1');
control.bind('click', function () {
if (control.is(':checked')) {
field.attr('type', 'text');
} else {
field.attr('type', 'tel');
}
})
}(jQuery));
<input type="tel" id="test1" value="a" />
<input id="test2" type="checkbox" />Change
You can use the native HTML API attribute called inputmode="numeric", inputmode="decimal". (This is the preferred way to implement it)
You can read further about input mode at the MDN here
<input type="number" inputmode="numeric" />
Try this:
<input type="text" name="postalcode" class="js-postal">
<script src="https://code.jquery.com/jquery-2.1.1.js" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
if('ontouchstart' in window) { // ensure we are in touch device.
$('input.js-postal').on('focus', function() {
var $this = $(this);
// memorize current value because...
var val = $this.val();
// this may cause reset value.
$this.attr('type', 'number');
setTimeout(function() {
// Asynchronously restore 'type' and value
$this.attr('type', 'text');
$this.val(val);
}, 0);
});
}
});
</script>
I know this is very hackish way, but this apparently works.
I haven't tested on Android devices though.
Note this may causes a little noticeable glitches when $this.attr('type', 'number') because this reset the value when input has non numerical characters.
Basic ideas are stolen from this answer :)
<input type="tel" inputmode="decimal">
Above has been working for me for several years. By working, I mean that on mobile devices, a numeric virtual keyboard is displayed when the input receives focus. On iOS devices, the keyboard allows users to enter numbers, decimals, and characters. My keyboard event handler inserts thousand separators, and there is no issue with passing validation, so I assume that users could also type in commas. Negative values can be entered, so hyphens are allowed, though I don't see a hyphen on the iOS keyboard.
If you would like to try this, you may try any calculator at my site.
Why not check the header of the HTTP request (user agent), and serve up the numeric layout to the iOS devices, while serving up the alphanumeric layout to the rest?