Reputation: 4616
In a Svelte app, I have a form with simple validation.
<form id="surveyForm" class="mt-4">
<div class="form-group">
<input type="text" class="form-control" placeholder="First name">
</div>
<div class="form-group">
<input type="text" class="form-control" placeholder="Last name">
</div>
<button class="btn btn-full" on:click={Continue}>Continue</button>
</form>
import {fly, fade} from 'svelte/transition';
let hasError = false;
let errMessage = "";
let isSuccessVisible = false;
function validateInput() {
var surveyForm = document.getElementById('surveyForm'),
inputFields = surveyForm.querySelectorAll('input[type=text]');
hasError = false;
inputFields.forEach(function(field) {
field.classList.remove("is-invalid");
var inputFieldVal = field.value;
if (inputFieldVal.length == 0) {
field.classList.add("is-invalid");
hasError = true;
}
});
errMessage = "All the fileds are mandatory";
}
function Continue(e) {
e.preventDefault();
validateInput();
if (hasError == false) {
isSuccessVisible = true;
setTimeout(function() {
isSuccessVisible = false;
}, 3000);
}
}
In order to give invalid form fields a red border, I use border: 1px solid #c00 !important
:
.error-alert {
border: 1px solid #c00 !important;
color: #c00;
}
The class is-invalid
is correctly added (and removed), yet, the default global.css
styles overwrite my component-level styles, as you can see in this REPL.
Why does this happen? What is a reliable fix?
NOTE: The case described is a simplified one, in reality I might need other validations, like making sure the value is numeric. The solution I have to implement is not a very specific one.
Upvotes: 1
Views: 1463
Reputation: 899
You can use the technique Rich described for other validations by using HTML5 client-side form validation. More information can be found on the MDN
<form id="surveyForm" class="mt-4" class:submitted>
<div class="form-group">
<input type="text" class="form-control" placeholder="First name" required>
</div>
<div class="form-group">
<input type="text" class="form-control" placeholder="Last name" required>
</div>
<div class="form-group">
<input type="number" class="form-control" placeholder="Enter a number" required>
</div>
<div class="form-group">
<input type="email" class="form-control" placeholder="Email" required>
</div>
<div class="form-group">
<input type="date" class="form-control" placeholder="Date" required>
</div>
<button class="btn btn-full" on:click|preventDefault={Continue}>Continue</button>
</form>
If you don't want to use this - or it doesn't cover all your use cases for some reason - you can manipulate the classes using Svelte instead of accessing the DOM directly:
<div class="form-group">
<input bind:value={second} type="text" class="form-control" class:is-invalid="{ submitted && second.length == 0 }" placeholder="Last name">
</div>
The reason Svelte removes your .is-invalid
style is because Svelte doesn't use it. I wouldn't recommend it in this case but you can force Svelte to keep the style using :global()
:
:global(.is-invalid) {
border: 1px solid #c00;
}
Upvotes: 2
Reputation: 29605
In the REPL, you see the following warning:
Unused CSS selector (87:1)
That's because the .is-invalid
selector isn't used anywhere in the markup — if a classname is unused, Svelte tells you about it and removes it. For Svelte to be able to 'see' it, it has to be in the markup — you can't add it programmatically with classList
. (This is a feature, not a bug: programmatically manipulating the DOM will inevitably lead to your view getting out of sync with your state.)
In this case though, you don't need to add a class to the input element, or do the checking inside a function. You can just use the input's required
attribute and :invalid
CSS selector:
<input type="text" class="form-control" placeholder="First name" required>
input:invalid {
border: 1px solid #c00;
}
The :invalid
state will be applied before the user submits any data though, so you may want to add a class that indicates whether the user has tried to submit already:
<form id="surveyForm" class="mt-4" class:submitted>
<div class="form-group">
<input type="text" class="form-control" placeholder="First name" required>
</div>
<!-- ... -->
</form>
.submitted input:invalid {
border: 1px solid #c00;
}
The easiest way to add that class is in a click
event handler on the button.
Using the stuff you get for free in HTML comes with other benefits. For example, the form won't submit as long as there are validation errors (and validation isn't limited to 'required' — you can mandate specific patterns, or use e.g. type="email"
to require a valid email address, etc), which means that inside your submit
event handler, you already know that the inputs are valid.
Upvotes: 5