I'm using Bootstrap-Vue in a VueJS project, and it's being impossible using a class..
I have the next input
<div class="input__wrapper">
<b-form-input
v-model="idOrNameSelected"
class="textfield"
:class="{'input--empty': idOrNameSelected != null}"
#keydown.enter.native="onSubmit" />
<label
class="input__label">Service id or name</label>
</div>
In my script section I defined idOrNameSelected like:
data () {
return {
idOrNameSelected: ''
}
}
In my scss file I have a rule like
&:focus ~ .input__label,
&:not([value=""]) ~ .input__label {
top: 8px;
pointer-events: none;
}
And when I put any text in my input never is using this css rule, why?????
Thanks
Here's the problem. The CSS logic doesn't know about Vue observers or v-model so it doesn't update the way you think. Take a step back and try this simple example:
HTML
<input class="in" type="text" value="bar" />
<label class="lab">Test</label>
CSS
.in:not([value="foo"]) ~ .lab {
color: crimson;
}
As you can see, the label is now red. Now try changing the value="foo" you'll see the label switches colors. But, what you should note here is that it doesn't have any sort of 2-way binding on the CSS itself, but actually just takes the current value in the actual DOM.
To provide you with an actual solution, I would use a class binding in this case.
You can read about them here: https://v2.vuejs.org/v2/guide/class-and-style.html
Essentially, it allows you to dynamically add/remove a class based on some true condition. And you can actually use your v-model in there.
Here's an example of how I would do it:
<template>
<div id="app">
<input type="text" v-model="model">
<label :class="{error: model === ''}">Label</label>
</div>
</template>
<script>
export default {
name: "App",
data() {
return { model: "" };
}
};
</script>
<style scoped>
.error {
color: crimson;
}
</style>
#dev-cyprium is correct regarding Vue not placing the attribute value on the input element when using v-model (value is actually a domProp on the element)
There is a trick you can do with data attributes though:
<template>
<div>
<b-form-input id="the-input" v-model="value" :data-value="value"></b-form-input>
<label for="the-input">Hello World</label>
</div>
</template>
<style>
input.form-control ~ label {
color: red;
}
input.form-control[data-value=""] ~ label {
color: blue;
}
</style>
<script>
export default {
data() {
return {
value: ''
}
}
}
</script>
Related
I'm using elementor in wordpress, i'cant modify the html directly, so i need to swap both tags with javascript (or css if that's possible) in the form.
The code i'm using in CSS:
input:focus > .elementor-field-label {
border: 1px solid black !important;
border-radius: 5px;
transform: translate(0px, -20px);
font-size: 14px;
}
Won't work because of the position of the tags.
Then i tried this code in javascript to do the job:
$('label').each(function() {
$(this).insertAfter($(this).nextAll('input:first'));
});
But don't work.
So, how can i make this possible?
FIY:
The structure of this specific part of the form is this:
<div class="elementor-field-type-text elementor-field-group elementor-column elementor-field-group-name elementor-col-100 elementor-field-required"> /*Using just the elementor-field-group*/
<label for="form-field-name" class="elementor-field-label"> Texto </label>
<input size="1" type="text" name="form_fields[name]" id="form-field-name" class="elementor-field elementor-size-sm elementor-field-textual" required="required" aria-required="true">
</div>
With CSS you could do something like this:
.elementor-field-group {
display: flex;
flex-flow: column;
}
.elementor-field-group input {
order: -1;
}
This wouldn't change the HTML structure but visually they would swap position.
With Javascript you can do it like this:
document.querySelectorAll('.elementor-field-group label').forEach((e) => {
e.parentNode.appendChild(e)
})
This script just takes the label and appends it to the parent again, so it will be the last child of the wrapper.
Or this, to add the input element before the label. This would be the better solution, if there are more than those two elements inside of the wrapper.
document.querySelectorAll('.elementor-field-group input').forEach((e) => {
e.parentNode.insertBefore(e, e.previousElementSibling);
})
In the video Diving Deeper into Template-based Forms in the Angular Fundamentals course on Pluralsight, I am encountering a significant difference in behavior when I run the presenter's code.
The code for the Component is given at the bottom of this question. Note that it contains no properties for the model.
Also note that the an elided version of the template is provided. This is provided exactly as the presenter provided it. The NgModel attribute in the form refers to a name field from the Component, but this does not exist on the Component. When I attempt to build the code and run it, this results in compile-time errors, and the application will not run.
However, when the presenter runs the solution using exactly the same code, it runs just fine and exactly as he expects it to.
If I add backing fields to the Component, it works just fine, but then my code no longer matches his code, and there's a significant portion of this course remaining to get through.
I am puzzled by this difference in behavior. I expect it to fail to compile without backing fields in the Component.
Can someone explain what is going on here? I am using the most recent version of Angular as of this writing.
Component
import { Component } from '#angular/core';
import { Router } from '#angular/router';
import { EventService } from './shared/event.service';
import { IEvent } from './shared';
#Component({
templateUrl: './create-event.component.html',
styles: [
`
em {
float: right;
color: #e05c65;
padding-left: 10px;
}
.error input {
background-color: #e3c3c5;
}
.error ::-webkit-input-placeholder {
color: #999;
}
.error ::-moz-placeholder {
color: #999;
}
.error :-moz-placeholder {
color: #999;
}
.error :ms-input-placeholder {
color: #999;
}
`,
],
})
export class CreateEventComponent {
isDirty: boolean = true;
constructor(
private router: Router,
private eventService: EventService) {
}
saveEvent(formValues) {
this.eventService.saveEvent(formValues);
this.isDirty = false;
this.router.navigate(['/events']);
}
cancel() {
this.router.navigate(['/events']);
}
}
Template (Elided)
<h1>New Event</h1>
<hr>
<div class="col-md-6">
<form #newEventForm="ngForm" (ngSubmit)="saveEvent(newEventForm.value)"
autocomplete="off" novalidate>
<div class="form-group"
[ngClass]="{'error': newEventForm.controls.name?.invalid && newEventForm.controls.name?.touched}">
<label for="eventName">Event Name:</label>
<em
*ngIf="newEventForm.controls.name?.invalid && (newEventForm.controls.name?.touched)">Required</em>
<input (ngModel)="name" name="name" required id="name" type="text"
class="form-control" placeholder="Name of your event..." />
</div>
What's the output in the console? There should be some other issues.
I just create a stackblitz with your form implementation, it works well without any compiling error.
BTW, I think you should use [(ngModel)]="name" if you want to do the double
direction binding of the name.
https://stackblitz.com/edit/angular-6-template-driven-form-validation-gbszme?file=app%2Fapp.component.html
I'm new to Vaadin and trying to create an instance that hides the vaadin-text-field from the component vaadin-date-picker.
I started out by reading the documentation for vaadin-date-picker about the shadow DOM property stated here.
I tried with "Scoping Styles in a Theme Module" but the whole thing including the calendar icon disappeared.
Current code as below,
render() {
return html`
<dom-module id="trim-inputbox" theme-for="vaadin-date-picker">
<template>
<style>
:host(.special_field) [part="text-field"] {
visibility:hidden;
}
</style>
</template>
</dom-module>
<vaadin-date-picker class="special_field"></vaadin-date-picker>
`;
}
Thanks so much again for any kind help.
As you noticed already a calendar icon is part of a text-field itself.
In Styling section there is an example of using <vaadin-date-picker-light>:
<style>
.my-input2 input {
border: none;
font-size: 14px;
background: none;
}
</style>
<vaadin-date-picker-light>
<div class="my-input2">
<iron-icon icon="event"></iron-icon>
CHECK-IN:
<iron-input>
<input size="10">
</iron-input>
</div>
</vaadin-date-picker-light>
Maybe you could use this instead?
I'm writing a Vue component, which accepts 2 slots:
<template>
<div class="Component">
<div class="Component-Label">
<slot
name="label"
class="Component-LabelInner"
/>
</div>
<div class="Component-Input">
<slot
name="input"
class="Component-InputInner"
/>
</div>
</div>
</template>
<style scoped>
.Component { ... }
.Component-Label { ... }
.Component-LabelInner { ... }
.Component-Input { ... }
.Component-InputInner { width: 100%; ... }
</style>
For layout purposes, I absolutely need to apply some styles to the <slot> elements - the ones with Component-LabelInner and Component-InputInner classes.
(To be precise I need to apply a rule width: 100% to the Component-InputInner, as usually I'll pass there <input> element and I want everything I pass there to stretch to the container.)
The problem is that after <slot> elements get replaced by the content provided to the slots, class attribute isn't inherited (it seems no attributes are inherited on slots) and CSS selectors for .Component-LabelInner and .Component-InputInner don't work.
Can you somehow add CSS classes to the element that <slot> gets replaced with?
You can not bind class to slot tag. There are some solutions to handle this case:
With Vue mounted hook (it works but looks as bad practice):
Vue.component("slots-comp", {
template: "#slotsCompTemplate",
mounted() {
// each slot is an array, because you can pass a set of nodes wrap them with template tag
// I use only first VNode for example
this.$slots.one && this.$slots.one[0].elm.classList.add("one");
this.$slots.two && this.$slots.two[0].elm.classList.add("two");
}
});
new Vue({
el: "#app",
data: {}
})
.one {
color: red
}
.two {
color: blue
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<slots-comp>
<div slot="one">One</div>
<div slot="two">Two</div>
</slots-comp>
</div>
<script type="text/x-template" id="slotsCompTemplate">
<div>
<slot name="one"></slot>
<slot name="two"></slot>
</div>
</script>
Pass neccessary classes as props to scoped slot(it is not fully encapsulated solution):
Vue.component("slots-comp", {
template: "#slotsCompTemplate",
data() {
return {
classes: {
one: ["one"],
two: ["two"]
}
}
}
});
new Vue({
el: "#app",
data: {}
})
.one {
color: red
}
.two {
color: blue
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<slots-comp>
<div slot-scope="props" :class="props.classes.one" slot="one">One</div>
<div slot-scope="props" :class="props.classes.two" slot="two">Two</div>
</slots-comp>
</div>
<script type="text/x-template" id="slotsCompTemplate">
<div>
<slot :classes="classes" name="one"></slot>
<slot :classes="classes" name="two"></slot>
</div>
</script>
Add changes to CSS to apply styles on all internal elements:
.Component-Input > *
{
/* my rules for child elements */
}
.Component-Label> *
{
/* my rules for child elements */
}
Or add wrapper element for slots with classes Component-InputInner etc. and add similar styles for them.
Hope it will help.
Let's say you have something like:
<div class="parent">
<input class="childInput" type="text" />
<div class="sibling"></div>
</div>
I want to change the appearance of the parent/siblings when the child receives focus. Are there any CSS tricks for doing stuff like this?
Edit:
The reason for my question is as follows:
I'm creating an Angular app which needs editable text fields. It should look like a label until it is clicked, at which point it should look like a normal text input. I styled the text field based on :focus to achieve this effect, but the text is cut off by text input's boundaries. I also used ng-show, ng-hide, ng-blur, ng-keypress and ng-click to switch between the label and the text input based on blurs, key presses and clicks. This worked fine except for one thing: After the label's ng-click="setEdit(this, $event)" changes the edit boolean used by ng-show and ng-hide to true, it uses a jQuery call to .select() the text input. However, it isn't until after the completion of the ng-click that everything is $digest'd, so the text input loses focus again. Since the text input never actually receives focus, using ng-blur to revert back to showing the label is buggy: The user has to click in the text input and then click out of it again to revert back to showing the label.
Edit:
Here's an example plunk of the issue: http://plnkr.co/edit/synSIP?p=preview
You can now do this in pure CSS, so no JavaScript needed 😁
The new CSS pseudo-class :focus-within would help for cases like this and will help with accessibility when people use tabbing for navigating, common when using screen readers.
.parent:focus-within {
border: 1px solid #000;
}
The :focus-within pseudo-class matches elements that either themselves
match :focus or that have descendants which match :focus.
Can I use...
You can check which browsers support this by visiting http://caniuse.com/#search=focus-within
Demo
fieldset {
padding: 0 24px 24px !important;
}
fieldset legend {
opacity: 0;
padding: 0 8px;
width: auto;
}
fieldset:focus-within {
border: 1px solid #000;
}
fieldset:focus-within legend {
opacity: 1;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet" />
<div class="container">
<form>
<fieldset>
<legend>Parent Element</legend>
<div class="form-group">
<label for="name">Name:</label>
<input class="form-control" id="name" placeholder="Enter name">
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" class="form-control" id="email" placeholder="Enter email">
</div>
</fieldset>
</form>
</div>
There is no chance how to do that with CSS. CSS can style only siblings, children, etc. not parents.
You can use simply JS like this:
<style>
.parent {background: green}
.focused {background: red;}
</style>
<div class="parent">
<input class="childInput" type="text" />
<div class="sibling"></div>
</div>
<script>
$('.parent > *')
.focus(function() {
$('.parent').addClass('focused');
})
.blur(function() {
$('.parent').removeClass('focused');
});
</script>
http://jsfiddle.net/C4bZ6/
This code takes all direct children of .parent and if you focus one of them, class focused is added to parent. On blur, this class is removed.
You can use pure CSS to make the text input look like it's not a text input unless it is in focus
http://jsfiddle.net/michaelburtonray/C4bZ6/13/
input[type="text"] {
border-color: transparent;
transition-duration: 600ms;
cursor: pointer;
outline-style: none;
text-overflow: ellipsis;
}
input[type="text"]:focus {
border-color: initial;
cursor: auto;
transition-duration: 300ms;
}
Try the contenteditible attribute. This may require more work to turn it into usable form data however.
http://jsfiddle.net/michaelburtonray/C4bZ6/20/
<span class="parent" contenteditable>Click me</span>
You can style it even for focus-within and not(focus-within) like this (without using JavaScript => more accessible and faster):
.myform:not(:focus-within) button[type="submit"] {
display: none;
}
.myform:focus-within button[type="submit"] {
display: block;
}