How to have attribute inheritance for Vue component slots? - javascript

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.

Related

How to change style of ant design in vue js

I can't change style in ant-tooltip on nuxtjs. I access to class ant-tooltip-inner but it don't changing.
<div class="box-form-input">
<a-tooltip placement="topRight" trigger="focus">
<template slot="title">
<span>Please fill in your Fullname</span>
</template>
<a-input
v-model="full_name"
/>
</a-tooltip>
</div>
<style>
.ant-tooltip-inner {
background-color: red;
}
</style>
Overriding Antd tooltip in vue.js works as expected.
<template>
<div id="app">
<!-- remove h3 tag causes wrong arrow direction -->
<h3>try to remove this element.
<br>After removed, the tooltip arrow changed direction.
</h3>
<a-tooltip placement="left" title="wrong arrow direction">
<span>why don't use popper.js</span>
</a-tooltip>
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
export default {
name: "App",
components: {
HelloWorld
}
};
</script>
<style>
.ant-tooltip-inner {
background-color: red;
}
</style>
See working CodeSandBox.

input value in vuejs using css :not[value=""]

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>

Add style to pure VUE component

I have a Element.js file, which is a VUE component exported like this:
export default {
template: `
<div>
<h1>Single-file JavaScript Component</h1>
<p>{{ message }}</p>
</div>
`,
data() {
return {
message: 'Oh hai from the component'
}
},
style: `
h1, p {
color: red !important; /* NOT WORKING */
}
`
}
And not in the usual <template></template> <script></script> <style></style> [dot]Vue structure.
Using the first structure. Is it possible to add CSS style to it?
I tried with the style prop as shown above but it is not working.
As the The Single File Components documentation is stating you can't do it via CSS
No CSS support means that while HTML and JavaScript are modularized into components, CSS is conspicuously left out
But you can still use Binding-Inline-Styles to style your component.
Vue.component('button-counter', {
template: `
<div>
<h1 :style="style">Single-file JavaScript Component</h1>
<p :style="style">{{ message }}</p>
</div>
`,
data() {
return {
message: 'Oh hai from the component',
style: {
color: 'red'
}
}
}
})
new Vue({ el: '#components-demo' })
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="components-demo">
<button-counter></button-counter>
</div>

css button styles wont work using <style> tags with vue.js

Im currently trying to learn vue.js and am trying to add styles to a component. The component itself works, and the functionality (alert message) works aswell but I cant get the styles to implement.
(Now I understand that technically I'm not using vue.js to style in my first example but this is to show what I had tried)
Attempt 1:
<template>
<div class="container">
<input id="test-btn" type="button" v-on:click= clicked()>
</div>
</template>
<script>
export default{
name: 'test-btn',
methods: {
clicked: function () {
alert("Here's your message")
}
}
}
</script>
<style scoped>
#test-btn{
color: #CC00CC;
width: 150;
height: 50;
}
</style>
Though I had changed the color width and height the button remains the generic grey and doesn't change width or height (it just stays a square). but it does work when I click it (at least something works).
Since I couldn't get that to work I tried to use the v-bind method.
Attempt 2:
<template>
<div class="container">
<input id="test-btn" type="button" v-on:click= clicked() v-bind:style="btnStyle">
</div>
</template>
<script>
export default{
name: 'test-btn',
methods: {
clicked: function () {
alert("Here's your message")
}
},
data: {
btnStyle: {
color: 'red',
width: 100,
height: 50
}
}
}
</script>
<style scoped>
/* #test-btn{
color: #CC00CC;
width: 150;
height: 50;
}*/
</style>
This attempt at v-bind also had failed as well. A friend told me that buttons are difficult to make the styling work and it may not be an error with my code, it may be default styling that is over riding it (I cant accept that). So what I did was tried to add !important to my css color line in the script tags but that didn't work either.
Your <button> is not styled because you have a CSS issue. Add px to width and height. See CSS in the demo below.
The color CSS property is the font color. To change the <button> color use background: yellow;.
new Vue({
el: '#app',
methods: {
clicked: function() {
alert("Here's your message")
}
}
})
#test-btn {
color: #CC00CC;
background-color: yellow;
width: 150px; /* was 150, now 150px */
height: 50px;
}
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div class="container">
<input id="test-btn" type="button" v-on:click="clicked()" value="Click Me">
</div>
</div>
Works with data and v-bind:style as well (just do width: '150px'; and height: '50px';). To change the background color add background: 'yellow' as well.
new Vue({
el: '#app',
data: {
btnStyle: {
color: '#CC00CC',
background: 'yellow',
width: '100px',
height: '50px'
}
},
methods: {
clicked: function() {
alert("Here's your message")
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div class="container">
<input id="test-btn" type="button" v-on:click="clicked()" v-bind:style="btnStyle" value="CLICK ME">
</div>
</div>

How to pass data to Vue JS components?

Disclaimer: I have tried to read the docs before opening this question.
I have this component:
<template>
<accordion id="facilities-menu" :one-at-atime="true">
<template v-for="facilities in facilitiesGroups">
<panel class="accordion-toggle" :header="`Facilities #${$index+1}`" :is-open="$index === 0">
<ul>
<li v-for="facility in facilities">
{{facility}}
</li>
</ul>
</panel>
</template>
</accordion>
</template>
<style lang="scss" scoped>
#import "../../../styles/theme-colors.scss";
.panel {
background: #5E6466;
border: none;
}
</style>
<script>
import { accordion, panel } from 'vue-strap'
module.exports = {
components: {
accordion, panel
},
data () {
return {
'facilitiesGroups': [['Continente Alfragide', 'Jumbo Almada', 'Portugália'], ['Pingo Doce', 'Lidl', 'Allegro'], ['Casa']]
}
}
}
</script>
And then I instantiate this component like this:
<template>
<div class="user">
<user></user>
</div>
<facilities></facilities>
</template>
<style lang="scss" scoped>
#import "../../../styles/theme-colors";
.user {
width: 100%;
height: auto;
margin-top: 8%;
margin-bottom: 5%;
}
</style>
<script>
import User from './userProfile'
import Facilities from './groupedFacilities'
module.exports = {
components: {
'user': User,
'facilities': Facilities
}
}
</script>
However, you can see that in the first component I am defining the data to be { 'facilitiesGroups': [['Continente Alfragide', 'Jumbo Almada', 'Portugália'], ['Pingo Doce', 'Lidl', 'Allegro'], ['Casa']] }. But i want this to be passed as an argument, not to be statically defined in the component as it is now.
I have read the docs about how could this be done here. But their example...
Vue.component('child', {
// declare the props
props: ['msg'],
// the prop can be used inside templates, and will also
// be set as `this.msg`
template: '<span>{{ msg }}</span>'
})
...Resembles nothing to what I have in my code... I don't even use Vue.component() anywhere!
How come I am using a different "style" of coding with Vue JS (I started from their boilerplate)?
How can I map Vue JS official documentation to this "style"?
How can pass that array as an argument/property?
Thanks!
Your component needs to declare the data you want passed in as 'props'
<script>
import { accordion, panel } from 'vue-strap'
module.exports = {
components: {
accordion, panel
},
props: ['facilitiesGroups']
}
</script>
... then in your parent component template you pass your data as an attribute. Below is an example where "facilitiesGroups" is a data element of your parent component:
<template>
<div class="user">
<user></user>
</div>
<facilities :facilities-groups="facilitiesGroups"></facilities>
</template>

Categories

Resources