VeeValidate v3 prevents clicks on blur validation - vue.js

I have form with autofocus on first field.
I'm using the eager validation mode.
The issue is that when I click anywhere on the page, input loses focus and gets validate and cancels the click on the page.
Is there a way to somehow allow blur with empty value, unless I call validation manually on submit?
My code snippet:
<b-form-group id="input-group-2" label="Your name:" label-for="input-2">
<validation-provider name="name" rules="required|min:2" mode="eager" v-slot="{ errors }">
<b-form-input
id="input-2"
v-model="credentials.name"
placeholder="Your name"
:class="{'is-invalid' : errors.length}"
autofocus
/>
<b-form-invalid-feedback :state="!errors.length">{{errors[0]}}</b-form-invalid-feedback>
</validation-provider>
</b-form-group>

I would like to make something clear, vee-validate doesn't prevent "click" events, it's just that when you are clicking outside, you are blurring the field. Thus triggering validation when the error message appears, it pushes the other elements down due to its line-height, which pushes the button/link down, causing you to click outside.
I would say this is not a vee-validate issue, but rather a CSS problem, ideally, if you are displaying error messages underneath the fields you should compensate for the additional space to prevent the UI from jumping which is annoying to users as well.
A simple margin-bottom equal to the line-height of the error message should do the trick, another approach is to have an absolute error message with a relative wrapper around them, have a bottom padding equal to the line-height and it would work as well.
.InputMargin:not(.has-error) {
margin-bottom: 16px;
}
.InputPadding:not(.has-error) {
position: relative;
padding-bottom: 16px;
}
.InputPadding:not(.has-error) span {
position: absolute;
bottom: 0;
left: 0;
}
You view the sample here: https://jsfiddle.net/logaretm/4mx0hy58/17/
Now if your UI framework of choice doesn't handle this case, you could PR it for them or open an issue in their project tracker.

Ended up avoiding automatic validation, switched to passive mode instead of eager. In the config file with this line:
setInteractionMode('passive');
I just call validate manually on submit:
async onSubmit() {
const isValid = await this.$refs.observer.validate();

Related

Vuetify use v-dialog components inside v-card-actions without causing padding issues

You can see the issue I'm having here:
https://codepen.io/ryanrapini/pen/LYeWZKR?editors=1010
Essentially, I have several dialogs which I have contained into their own custom "vue components" i.e. order-dialog.vue. In an ideal world, I could simply include this <order-dialog> component wherever I need it, and it would render the activator button and then handle all of the dialog state inside the component, so the parent doesn't need to worry.
Unfortunately, if I do this in a v-card-actions section I get spacing issues. Is this a bug with Vuetify or am I doing something wrong?
I thought that by moving the activator out of the <v-dialog> tag it might fix the issue, but it still doesn't unless I break my component up into a v-dialog component and a separate activator, which means I now need to manage the state of the dialog in the parent.
Thanks for any help.
I don't think you are doing something wrong, and I'm not sure that it's a Vuetify bug.
This comes from CSS rule in Vuetify library:
.v-application--is-ltr .v-card__actions>.v-btn.v-btn+.v-btn {
margin-left: 8px;
}
I think the authors assumed that this block should contain only buttons. But in your case (in 2nd and 3rd approach) an output HTML looks like this:
<div class="v-card__actions">
<button class="v-btn ...">
...
</button>
<div class="v-dialog__container"><!----></div>
<button type="button" class="v-btn ...">
...
</button>
<button type="button" class="v-btn ...">
...
</button>
</div>
So v-dialog__container breaks this rule.
You can fix you issue, by example, with an additional rule:
.v-application--is-ltr .v-card__actions>.v-btn:not(:first-child) {
margin-left: 8px !important;
}
Or you can also apply helper classes (ml-2) into specific buttons.

Override z-index of Panel layer

I am trying to override the Panel layer styles and decrease the index to 999998 in order to achieve my div to be above the grayed layer and below the panel when Panel is opened. So I tried to override the overlayProps , but as i see there is a Layer component above the overlay that has z-index:1000000 and inherits the styles. How can i override the layer class with a proper way to achieve my case ? Please check my codepen example http://codepen.io/mariosch/pen/dyGYEQG
<div>
<div style={{zIndex: 999999}}>Should be above overlay and below side panel</div>
<DefaultButton text="Open panel" onClick={openPanel} />
<Panel
headerText="Sample panel"
isOpen={isOpen}
onDismiss={dismissPanel}
// You MUST provide this prop! Otherwise screen readers will just say "button" with no label.
closeButtonAriaLabel="Close"
overlayProps={{ styles: { root: { zIndex: 999998 }}}}
>
<p>Content goes here.</p>
</Panel>
</div>
Panel takes another prop called layerProps that can be used to set the zIndex of the layer.
layerProps={{ styles: { root: { zIndex: 999998 }}}}
That said, there are still some zIndex issues with the panel overall that would need to be solved to have the text in the div show up where you'd like.
Do you want the panel the be "blocking"? If not, you might consider passing isBlocking={false} to the panel, and it would leave the entirety of the content visible.
In this example (based on yours) I added a second button to toggle the behavior of the isBlocking prop and it might also provide what you're looking for.
<Panel
isOpen={isOpen}
isBlocking={false} // <-- this makes the rest of the content on the page visible and interactive
onDismiss={togglePanel}
>
<p>Content goes here.</p>
</Panel>

displaying v-progress-circular in a conditional is making the below elements "jumpy"

The progress circle will be there until the codition sending is false. what happens is that after the dive goes away the below element will jump up to where the div dissapeared making it seem "jumpy". What would be the best way to display it in a stable manner.
<v-progress-circular
color="#64B5F6"
indeterminate
></v-progress-circular>
</div>
//rest of stuff
I suspect that you are using v-if="sending" on the parent <div>. When sending==false the v-if directive will completely remove it from the DOM. You can't even use v-show because although this will keep it in the DOM, it will have "display: none" css so it it still wont take up any space.
Instead, you need to set the visibility: hidden css to hide the spinner without collapsing the space it takes up.
You can do this in two ways:
<div :style="{visibility: sending ? 'visible' : 'hidden'}">
<v-progress-circular ...>
<div>
Or, create a re-usable custom directive:
Vue.directive('visible', function(el, binding) {
el.style.visibility = !!binding.value ? 'visible' : 'hidden';
});
Then use it like so:
<div v-visible="sending">
<v-progress-circular ...>
<div>

Make class !important

In Less is there a simple way to make all attributes in a class !important? My usecase is that I will be dynamically inserting a 'tag' (div) into existing divs that will always have inline styling.
Example:
<div class="text-widget ui-sortable" style="font-size: 5em;>
<div class="tag"><span>Click me to drag widget. My font size should never change</span></div>
<p>I am a text widget that can be dragged. When I am deselected my tag above will be deleted</p>
</div>
So .tag properties need to be !important to avoid getting the text widgets css. Is there a simple way in less to make all properties !important? Something like...
.tag !important {
.... lots of properties that I dont want to add !important to each one.
}
This is in reply to #sazr's question to my comment.
If you think you need to use !important then your CSS is very complex and often has too many top level rules that affect too many things. Sometimes this is because you are trying to create generic CSS that is applied throughout your page, sometimes it is because you are creating rules that have such a large value for specificity that you can't figure out another way to force the style you want on your element.
Learning what specificity is and how it works is the most important thing for a CSS developer. If you don't truly understand that then you are doomed to need !important to resolve issues that elude you.
Look at this chart that I took from here: http://www.standardista.com/css3/css-specificity/
Notice the image associated with !important. It is the nuclear option and should be used as a last resort. Although I use it on every rule for #media print to not have to worry about my printouts.
Using some kind of name spacing with your CSS will help reduce the death spiral that can be caused by too many non-specific selectors or too many selectors that are so specific that you can no longer override those rules.
A select like this:
#page1 .outershell .innershell .form button {
background-color: green;
}
has a specificity of 1,3,1
If you have this layout:
<div id="page1">
<div class="outershell">
<div class="innershell">
<form class="form">
<button>Click me</button>
</form>
</div>
</div>
</div>
And you want to change the button's background color to red then you have to create a new selector with a higher specificity.
This won't work
.form button {
background-color: red;
}
Since it only has a specificity of 0,1,1
#page1 .form button {
background-color: red;
}
This only has a value of 1,1,1
So you need to use two ID selectors, a fourth class selector or a second element selector. Or you can place the exact same selector after the first and then all of your buttons after that declaration will be red.
But that won't change any other buttons to red. So with this layout:
<div id="page1">
<div class="outershell">
<div class="innershell">
<form class="form">
<button>Click me</button>
</form>
</div>
<div class="secondshell">
<button>Not me</button>
</div>
</div>
</div>
the button "Not me" will not be red, or even green.
Things I do
I do not ever us ID selectors unless I must to override existing CSS.
I do not use !important except for #media print and I use it for everything in my print output. That way I know my specificity for the print output and I do not worry about some other CSS selector ruining my printouts.
I avoid deep selectors. Most of my selectors have a specificity value of 0,1,0 to 0,2,0 or 0,1,2
I use attributes for state values and attribute selectors to reduce the amount of JS I need to write, allowing CSS to do the work for me.
BEM to the rescue
OK. Some people don't like BEM. But it has save my life from the complexities of CSS. I have not had a single CSS specificity problem since I started using it, except when dealing with older CSS and even then I find it easy to repair.
BEM is not just about CSS, it is also about formatting your DOM in a sensible way to help the CSS work for you instead of you having to work for it.
Using this DOM:
<style>
.form-box--btn {
background-color: red;
}
</style>
<div>
<div>
<div class="form-box">
<form class="form-box--form">
<button class="form-box--btn">Click me</button>
</form>
</div>
<div class="other-thing">
<button class="form-box--btn">Me too</button>
</div>
</div>
</div>
I KNOW that my two buttons are red. And as long as everyone working on this page agrees to follow the rules we never run into a problem with the button changing color.
I know that is a simplistic example, but reading more about specificity, BEM and name-spacing will tell you much more than I can in this post.
Some light reading
Here are a few links that talk more about specificity, BEM and name spacing:
https://uxengineer.com/css-specificity-avoid-important-css/
http://getbem.com/
https://www.smashingmagazine.com/2007/07/css-specificity-things-you-should-know/
https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity
https://zellwk.com/blog/css-architecture-1/
Make a class for it:
.tagStyle (#bg: #00FF00) {
background: #bg;
/* Other properties */
}
.tag {
.tagStyle() !important;
}

How to use a button inside v-text-field label slot

I need to insert a clickable button inside v-text-field under label slot. Meaning that there will be label : " Start typing or Select Search Panel" where "Select Search Panel" will be a link or button.
I'm able to make it visible using the v-slot:label but the button is not clickable and onSearchPanelClicked in not triggered..
<v-text-field class="input-field" clearable prepend-inner-icon="search" solo >
<template v-slot:label class="linkLabel" v-on:click="onSearchPanelClicked">
Start typing or <v-btn v-on:click="onSearchPanelClicked" class="link" flat > Select Search Panel</v-btn>
</template>
</v-text-field>
Is there any way to transfer click event to the element ? If I start typing the label must disappear like it used to work on v-text-field.
Regards
Jan
You have to overwrite your input .v-label CSS rule pointer-events.
Vuetify sets the label inside a v-text-field like this:
.v-text-field .v-label {
pointer-events: none; // unclickable element
}
So changing it to auto will enable your button inside v-text-field label to become the target of pointer events, just be aware of deep selectors when working with scoped styles at your component.
.v-input.input-field .v-label {
pointer-events: auto;
}
/* or for scoped styles */
::v-deep .v-input.input-field .v-label {
pointer-events: auto;
}
In this example the rule is applied only to fields with your custom class .input-field, if you'd like to overwrite for all the v-text-fields just remove it from the CSS code.