I am using type=url in my form to only allow a url to be passed through.
However, using the standard validation message is as follows
Is there a way to change this? Here is the design what I am trying to achive (outline search bar, add message beneath, and make text in search box orange)
Here is function after my form
function searchIt() {
let form = document.querySelector('form')
console.log(form)
form.addEventListener('submit', async(e) => {
// onclick or the event that start the call
interval = setInterval(() => {
progress = progress >= 100 ? 100 : progress + 1
document.getElementById('myprogress').style.width = `${progress}%`
// end interval and wait at 100%
if(progress == 100) clearInterval(interval);
}, maxTime/100)
document.getElementById('loadingcontainer').style.display = ""
e.preventDefault()
let urlIN = form.url.value
let url = encodeURIComponent(urlIN)
console.log(url)
try {
const data = await fetch('/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: url
})
}).then(res => {
document.open()
res.text().then(function(text) {
document.write(text)
// Hide the progressbar, stop the timer and reset progress
clearInterval(interval);
progress = 0;
document.getElementById('myprogress').style.width = "0%"
document.getElementById('loadingcontainer').style.display = "none";
});
})
} catch (err) {
console.error(err)
}
})
}
Rather than write all the necessary validation for URLs, you can use the HTML5 input type "url" in place of type "text", which has all that validation built in:
<input type="url" name="location">
That will require a properly formed URL, but ftp:// is also proper.
You can further constrain it by using the
pattern attribute for your requirements "beginning in http://, https://, includes www.".
Here, using novalidate on the <form_> prevents the browser from showing it's own messages, then you can test a field's validity with field.checkValidity()
const urlField = document.getElementById('onlyweb');
const messagebox = document.getElementById('errormessage');
document.getElementById('testform')
.addEventListener('submit', function(e) {
e.preventDefault();
const valid = urlField.checkValidity();
console.log('valid =', valid);
if (valid) {
messagebox.innerHTML = ''; // clear any message that might be there
messagebox.classList.add('hidden');
}
else {
messagebox.innerHTML = '<span class="errmsg">You need to include http:// or https:// and include the "www." prefix.</span>';
messagebox.classList.remove('hidden');
}
});
input:invalid {
border-color: red;
}
div {
margin-bottom: 1em;
}
#errormessage {
font-family: sans-serif;
color: red;
border: 1px solid gray;
padding: 1em;
}
#errormessage.hidden {
display: none;
}
<form id="testform" action="#" novalidate>
<div>
Invalid fields will have a red border
</div>
<div>
<label for="onlyweb">Only Web URLs</label>
<input type="url"
name="onlyweb" id="onlyweb"
pattern="^https?://www\..*$"
>
</div>
<div>
<input type="submit" value="Test It">
</div>
<div id="errormessage" class="hidden">
</div>
</form>
I recommend just manipulating classes and writing CSS using the :valid and :invalid pseudo-selectors, rather than changing a bunch of field.style.something=newvalue which have to be coordinated to make sure you do & undo things consistently.
It also mixes style into your code, instead of keeping it in CSS where it belongs.
See MDN's Client-side form validation
for The Constraint Validation API
especially where it gives "A more detailed example"
Yes, it is perfectly possible. However, you must create the validation yourself by attaching a onsubmit function to the form like so:
var input = document.getElementsByTagName('input')[0];
var expression = /(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/gi;
var regex = new RegExp(expression);
var errorMsg = document.getElementById("errorMessage");
function val(){
var value = input.value;
if(regex.test(value)){
input.style.borderColor="green";
input.style.color="green";
return true;
}else{
input.style.borderColor="red";
input.style.color="red";
errorMsg.style.display="block";
return false;
}
}
#errorMessage{
color:red;
display:none;
}
.input{
width: 80%;
margin: auto;
}
input{
width: 80%;
margin: auto;
padding: 2em;
border-radius: 25px;
outline: none;
border: 1px solid black;
}
<form action="stackoverflow.com" onsubmit="return val()">
<div class="input">
<input>
<p id="errorMessage">Yikes! That's not a valid URL.</p>
</div>
</form>
You can use the input type url and then check the validity using .checkValidity().From Mozilla on the checkValidity method: checks whether the element has any constraints and whether it satisfies them. If the element fails its constraints, the browser fires a cancelable invalid event at the element, and then returns false.
It's also important to note that when one says URL validation, what exactly do they refer to when referencing a url? One could write in a url address bar, www.stackoverflow.com and they would end up at stackoverflows web site, however, the URL validation will require that the valid protocol http, https is called in the url address in order to validate using an input url type. More info on url can be found here... What is a URL? -Mozilla
const input = document.getElementById("url");
const errorMessage = document.getElementById("errorMessage");
const validate = document.getElementById("validate");
validate.addEventListener('click', (e) => {
e.preventDefault();
if (input.checkValidity() !== true) {
input.className = 'false';
input.classList.remove('true');
errorMessage.style.display = 'block';
} else {
input.className = 'true';
input.classList.remove('false');
errorMessage.style.display = 'none';
}
})
#errorMessage {
display: none;
color: orange;
}
.true {
border: 1px solid green;
color: green;
}
.false {
border: 1px solid orange;
color: orange;
}
input#url {
border-radius: .9rem;
padding: .7rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form novalidate>
<input id="url" type="url" required />
<input type="submit" name="validate" value="Check" id="validate">
</form>
<p id="errorMessage">Yikes! That's not a valid URL.</p>
Related
I am sorting an array of so called 'activities' in my customElement using LitElement:
#property({ type: Array }) private activityListLocal: Array<Activity> = [];
in the parent customElement called "find-activity".
Each activity is being rendered here.
${repeat(
this.activityListLocal,
activity =>
html` <div class="activity-container">
<div class="activity">
<activity-info .activity=${activity}></activity-info>
</div>
<div class="activity" class="rating">
<activity-rating
.activity=${activity}
#appactivityremoveclick=${() => this.deleteActivity(activity)}
></activity-rating>
</div>
</div>`
)}
This is how it looks visually:
2 activities marked for some impression
On clicking the button "Highest Rating", I am sorting the list of activities:
sortActivityListLocal() {
this.activityListLocal = [...this.activityList];
this.activityListLocal = this.activityListLocal.sort((a, b) => (a.avgRating < b.avgRating ? 1 : -1));
}
if (category === 'all') {
this.activityListLocal = this.activityList;
} else if (category === 'Highest Rating') {
this.sortActivityListLocal();
if (this.activityListLocal.length === 0) {
this.nothingHere.style.display = 'block';
}
}
//....
}
Note: this.activityList is a local copy of the server response.
In the image, you see the two sliders, which should be updated to move with the activity if the position on the page changes. The issue: The "my rating" slider does not properly "move" with the activity, if it has been changed/dragged after the page has been loaded for the first time.
Before:
Activities are loaded in properly, cinema has a higher rating than Tennis
After:
Activities are sorted properly, all sliders are correctly "moved" if "myRating" has not been changed/dragged
But if the slider was dragged after inital load in, and then selecting the "highest rating" category and therefore sorting the array, it stays in place:
Before:
After loading
Dragging the slider (not even requesting an update with a click on the refresh icon, issue happening in both cases)
Modification leading to the issue
After:
Issue visible
The interesting thing, the slider has the correct! value in the html inspector, but the display is not showing it. Why is this happening?
Code of the component holding the sliders:
import { LitElement, html } from 'lit';
import { customElement, property, query } from 'lit/decorators.js';
import { httpClient } from '../../../http-client';
import { PageMixin } from '../../page.mixin';
import { Activity, Rating } from '../find-activity';
import componentStyle from './activity-rating.css';
#customElement('activity-rating')
// eslint-disable-next-line #typescript-eslint/no-unused-vars
class ActivityRatingComponent extends PageMixin(LitElement) {
static styles = componentStyle;
#property({ reflect: true }) activity = {} as Activity;
#property() rating = {} as Rating;
#query('#deleteButton') private deleteButton!: HTMLImageElement;
private currentSliderValue = -1;
async updated() {
console.log(
`Personal rating for ${this.activity.title} is ${this.activity.personalRating}, avgRating ${this.activity.avgRating}, currentSliderValue ${this.currentSliderValue}`
);
this.currentSliderValue = this.activity.personalRating ? this.activity.personalRating : 0;
console.log(`Current slider value after: ${this.currentSliderValue}`);
if (this.activity.deletepermission === false) this.deleteButton.style.display = 'none';
else this.deleteButton.style.display = 'inline';
}
render() {
return html`
${this.renderNotification()}
<div class="outer-rating">
<p>${this.activity.motivationtitle}</p>
<div class="slidecontainer">
<label for="overallRating">Overall Rating</label>
<input
type="range"
min="0"
max="100"
value=${this.activity.avgRating ? this.activity.avgRating : 0}
class="slider"
id="overallRating"
disabled
/>
</div>
<div class="slidecontainer">
<label for="myRating">My Rating</label>
<input
type="range"
min="0"
max="100"
value=${this.activity.personalRating ? this.activity.personalRating : '0'}
class="slider"
id="myRating"
#change="${(e: Event) => this.readcurrentSliderValue(e)}"
/>
<img id="personalSlider" src="/refresh.png" alt="update" #click=${this.savecurrentSliderValueToDb} />
<img
class="remove-task"
src="/deleteicon.png"
alt="update"
id="deleteButton"
#click="${this.confirmDelete}"
/>
</div>
</div>
`;
}
confirmDelete(e: Event) {
const target = e.target as HTMLInputElement;
if (target) {
const result = confirm('Want to delete?');
if (result) {
this.emit('appactivityremoveclick');
}
}
}
readcurrentSliderValue(e: Event) {
const target = e.target as HTMLInputElement;
if (e) {
this.currentSliderValue = Number(target?.value);
console.log('Read new slider value ' + Number(target?.value));
}
}
async savecurrentSliderValueToDb() {
const partialRating: Partial<Rating> = {
activityid: this.activity.id,
rating: Number(this.currentSliderValue) //userID is not included here as it is being provided by the auth Middleware on patch request.
};
await httpClient.patch(`rating/${this.activity.id}${location.search}`, partialRating);
const responseRatingAll = await httpClient.get(`rating/findAverageRating/${this.activity.id}` + location.search);
try {
this.activity.avgRating = (await responseRatingAll.json()).results;
this.activity.personalRating = partialRating.rating ? partialRating.rating : 0;
} catch (error) {
this.showNotification((error as Error).message, 'error');
}
this.requestUpdate();
}
emit(eventType: string, eventData = {}) {
const event = new CustomEvent(eventType, {
detail: eventData,
bubbles: true,
composed: true
});
this.dispatchEvent(event);
}
}
Visual confirmation that slider has the correct value, but doesn't show it.
Thank you :)
Edit: In addition to the answer below - specifically for the case where "you want to force a value to be set on an element". Lit has an optimization where "if a value hasn't changed, don't do anything". Rendering the same value to an expression will not cause the expression to update. To make sure Lit updates the expression if the underlying DOM value has changed use the live directive.
The native browser input elements default behavior is:
When the value attribute is changed, update the input elements value property.
After a manual user interaction (such as typing into the input element if it is a text input), the value attribute no longer updates the input property.
After the value property has been updated the attribute no longer causes the property to update.
Therefore by setting the value property the value updates.
Because of that browser behavior, in Lit you can use a property expression to set the value property.
I.e.: <input .value=${this.activity.avgRating ? this.activity.avgRating : 0}.
Below is an example of the browser input behavior. Click the two buttons. One will update the value attribute, the other the value property.
Then interact with the input. Type in it. Now the attribute button will stop working.
const inputEl = document.querySelector('input')
const getRandomValue = () => String(Math.random()).slice(0, 5)
document.querySelector("#btn-attr")
.addEventListener("click", () => {
inputEl.setAttribute('value', getRandomValue())
});
document.querySelector("#btn-prop")
.addEventListener("click", () => {
inputEl.value = getRandomValue()
});
<input value="12345">
<button id="btn-attr">Change input attribute</button>
<button id="btn-prop">Change input property</button>
I have an input textbox field where a user can enter comma separated values like...
value 1,value 2,value 3,value 4
I have a requirement to set the maximum length of each value entered in that field to be 128 characters. I know we have a maxlength property which will set at overall field level. But is there any way which I can restrict the length of each value that is being entered in the field?
<input type="text" class="form-control" formControlName="licenseOwnerName" maxlength="128">
Above HTML will set for the total value in the field, but How can I set for each value separated by comma.
I have a validator which checks whether the user entered at least 2 values
import { AbstractControl } from "#angular/forms";
export function InputValuesValidator(control: AbstractControl): { [key: string]: boolean } | null {
if (control.value) {
let splitString = (control.value).split(',');
if(splitString.length == 1 || splitString[1] == "") {
return { 'valuelisterror': true };
}
}
return null;
}
Can I modify this validator to check for length as well? Please suggest. Thanks.
make your text validation function be like this,
textInputValidation(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const valuesLength = control.value.split(',').length - 1;
if (control.value.split(',')[valuesLength].length>128) {
return {
textLengthError: 'You have entered more than 128 chars '
};
} else {
return null;
}
};
}
You will also have to remove maxlength property from your HTML, also my suggestion is that use different functions for validation, putting all the validation logic is not a good practice.
It's important when you try to make some component, first try to create a simple model of it, for what you want it's much better if you try to do that:
event.keyCode === 188 mean comma, also you can add Enter or whatever you want; and the input always has a maxlength and user can't type more than it
Html:
<label for="tag" class="a">
<ul>
<li *ngFor="let tag of tags">{{tag}}</li>
<li>
<input id="tag" type="text" maxlength="125" [(ngModel)]="tag" (keyup)="callMe($event)">
</li>
</ul>
</label>
ts:
tags = [];
tag = null;
callMe(event) {
if (this.tag.length <= 128) {
if (event.keyCode === 188) {
this.tags.push(this.tag);
this.tag = null;
}
}
}
Css:
.a {
border: dashed 1px #ccc;
display: block;
}
.a ul li {
display: inline-block;
margin: 0 5px;
list-style: none;
}
input {
border: none;
outline: none;
}
This creates an array of words seperated by a comma and checks them for a length (4 here). Just a rough idea, customize it to your needs.
<input id="license" type="text" class="form-control" formControlName="licenseOwnerName" maxlength="128">
let lic = document.getElementById("license");
lic.addEventListener("input", function(e) {
lic_arr = lic.value.split(",");
for(let word in lic_arr) {
if(lic_arr[word].length > 4) {
alert("too long");
}
}
})
i am trying to validate email address using JQuery but it always return true even for invalid email address.
this is the function.
function check_email($email) {
var regex = /^([a-zA-Z0-9_\.\-\+])+\#(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
if (!regex.test(email)) {
return false;
}
else {
return true;
}
};
Here is the function which send parameter email to this function
function valid_email() {
var $email = $("#email").val();
if (check_email($email)) {
$("#email").css("border", "1px solid green");
$("#email").css("border", "1px solid green");
$("#email_error_message").hide();
}
else {
$("#email").css("border", "1px solid red");
$("#email_error_message").show();
$("#email_error_message").html("please provide a vallid email address");
}
}
Your check_email function has a param named '$email' but uses 'email' internally.
Here's a corrected version (with some lightly simplified logic):
function check_email(email) {
var regex = /^([a-zA-Z0-9_\.\-\+])+\#(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
return (regex.test(email))
};
function valid_email() {
var $email = $("#email").val();
if (check_email($email)) {
$("#email").css("border", "1px solid green");
$("#email_error_message").hide();
} else {
$("#email").css("border", "1px solid red");
$("#email_error_message")
.html("please provide a valid email address")
.show();
}
}
// fire it on change (which occurs on blur, not every keystroke)
$('#email').on('change', valid_email)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="email">
<div id="email_error_message"></div>
You do not need to use regex to validate emails. You can use a plain HTML5 email input and call .checkValidity() on it which will return true if valid and false if invalid.
function valid_email() {
var valid = $("#email")[0].checkValidity();
if (valid) {
$("#email").css("border", "1px solid green");
$("#email").css("border", "1px solid green");
$("#email_error_message").hide();
} else {
$("#email").css("border", "1px solid red");
$("#email_error_message").show();
$("#email_error_message").html("please provide a vallid email address");
}
}
$("button").on("click", valid_email);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="email" id="email" />
<button>Check validity</button>
This has an advantage of
You outsource the email validation to the browser.
You avoid writing regular expression that disallow legitimate emails.
I have created an angular project using a custom multi-select-autocomplete that has
#Output() selectionChange: EventEmitter<any[]> = new EventEmitter<any[]>();
As well as the following method
onSelectionChange(val : any) {
const filteredValues = this.getFilteredOptionsValues();
let count = 0;
if (this.multiple) {
this.selectedValue?.filter(item => {
if (filteredValues.includes(item)) {
count++;
}
});
this.selectAllChecked = count === this.filteredOptions.length;
}
this.selectedValue = val.value;
this.selectionChange.emit(this.selectedValue);
}
I use it in my other components as such
<div style="width: 100%; margin-right: 26px; margin-bottom: 15px;">
<multi-select-autocomplete class="input-medium"
[placeholder]="'Search and Select Brands'"
[options]="companies"
[display]="'name'"
[value]="'id'"
[labelCount]="10"
[label]="'Brands'"
(selectionChange)="selectBrand($event)">
</multi-select-autocomplete>
</div>
</div>
But I come across an issue where the (selectionChange) method is continuously firing even before I make a selection.
Any ideas where I may have gone wrong?
I am using the HTML required attribute to perform in-page validation.
<input type="text" required>
Please see this plunker: https://plnkr.co/edit/Swy4hFEewCYlnL0bJKq1?p=streamer
But I don't want to show the default error text, "Please fill out this field.", instead I want another customized message say "You cannot leave the xyz field empty. Blah, blah, blah"
How can I do this?
Try this code.
HTML
<form action="">
<input id="email" type="email" required="required" />
<input type="submit" id="btnSubmit" />
</form>
JavaScript
(function (exports) {
function valOrFunction(val, ctx, args) {
if (typeof val == "function") {
return val.apply(ctx, args);
} else {
return val;
}
}
function InvalidInputHelper(input, options) {
input.setCustomValidity(valOrFunction(options.defaultText, window, [input]));
function changeOrInput() {
if (input.value == "") {
input.setCustomValidity(valOrFunction(options.emptyText, window, [input]));
} else {
input.setCustomValidity("");
}
}
function invalid() {
if (input.value == "") {
input.setCustomValidity(valOrFunction(options.emptyText, window, [input]));
} else {
console.log("INVALID!"); input.setCustomValidity(valOrFunction(options.invalidText, window, [input]));
}
}
input.addEventListener("change", changeOrInput);
input.addEventListener("input", changeOrInput);
input.addEventListener("invalid", invalid);
}
exports.InvalidInputHelper = InvalidInputHelper;
})(window);
InvalidInputHelper(document.getElementById("email"), {
defaultText: "Please enter an email address!",
emptyText: "Please enter an email address!",
invalidText: function (input) {
return 'The email address "' + input.value + '" is invalid!';
}
});
A working JSFiddle can be found at
http://jsfiddle.net/meghanagpal/B4hYG/622/
Sure you can, with a dash of JavaScript. You can use HTMLInputElement.setCustomValidity(String). When it's set to any non-empty string, the owning <input> won't validate. You can call this within a change event or similar; if you decide, via your own code, that the <input> is invalid, just set the message. If you decide that it's all good, then set it to "" (the empty String).