Changing components element colour with class binding - vue.js

I have a component which is being used in different places but at once place, the colour of the link need to change. I tried the following but it doesn't my work.
component
<section class="contact__info">
<div class="details">{{days}}</div>
<div>
<a :href="'telephone'">
<strong>{{telephone}}</strong>
</a>
</div>
</section>
<style lang="scss">
.contact__info{
a {
color: grey;
font-weight: bolder
}
}
</style>
I am calling it as following in the page which needs the colour change for the anchor in that component and which is not working is as follows
<CallWidget
:class="{anchor : isFooter}"
title="Call Us:"
days="Mon - Fri"
telephone="0123"
/>
<script>
export default {
data(){
return{
isFooter: true
}
},
};
</script>
<style lang="scss" scoped>
.anchor {
a {
color: white;
}
}
</style>

If class is inside a child component you need to add /deep/ or >>> before it to tell Vue to check inside child components.
<style lang="scss" scoped>
/deep/ .anchor {
a {
color: white;
}
}
</style>
Take a look at https://vue-loader.vuejs.org/guide/scoped-css.html#deep-selectors

Related

How can i change style of the body when my modal it will be open?

How can i change the body{overflow:hidden} when my modal it will be open?
for example it will be my modal, when its open, i would like to apply this style body{overflow:hidden}
<div v-if="dialogFoundation">
i am using vuejs3, i am using setup(){...}
The best performance would be to use javascript plain. You can add Eventlistener top the modal trigger Element. In my example i use a button. If it triggered then you can use classList and assign the body a class. In my example .dark.
Vue version
<!-- Use preprocessors via the lang attribute! e.g. <template lang="pug"> -->
<template>
<div id="app">
<h1>{{message}}</h1>
<p></p>
<button #click="doSomething">Modal</button>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Welcome to Vue!'
};
},
methods: {
doSomething() {
const b = document.querySelector('body');
b.classList.toggle('dark');
}
}
};
</script>
<!-- Use preprocessors via the lang attribute! e.g. <style lang="scss"> -->
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
a,
button {
color: #4fc08d;
}
button {
background: none;
border: solid 1px;
border-radius: 2em;
font: inherit;
padding: 0.75em 2em;
}
.dark {
background: black;
opacity: 0.4;
}
</style>
Vanilla JS
const btn = document.querySelector('button');
btn.addEventListener('click', () => {
const b = document.querySelector('body');
b.classList.toggle('dark');
})
.dark {
background: black;
opacity: 0.4;
}
<body>
<div></div>
<button>click</button>
</body>
You can use watchers in Vue.js for solving this problem.
When variables changes you can check whether it is true or not, and if true change overflow of body to hidden.
{
watch: {
dialogFoundation(dialogFoundation) {
document.body.style.overflow = dialogFoundation ? "hidden" : "auto"
}
}
}
But I think this is not good solution. You can set this styles to your app element
#app {
width: 100%;
height: 100%;
overflow: auto;
}
and you can change style of app element using Vue directives.
<template>
<div id="app" :class="{ hidden: dialogFoundation }">
Long text....
</div>
</template>
<script>
import { ref } from "vue";
export default {
setup() {
const dialogFoundation = ref(true);
return { dialogFoundation };
},
};
</script>
<style>
html,
body,
#app {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
box-sizing: border-box;
}
#app {
overflow: auto;
}
#app.hidden {
overflow: hidden;
}
</style>
Code in codesandbox - https://codesandbox.io/s/immutable-glitter-rwc2iy?file=/src/App.vue

Vue.js Scroll Snap to Component

I'm trying to scroll snap to components in Vue.js 3. I can get scroll snap to work correctly with vanilla HTML and CSS. Here's what it looks like: Scroll Snap Demo
I'm trying to copy that simple layout but using App.js as the container and components as the divs. Here's what it looks like in App.vue:
<template class="container">
<CompI class="snapAlign"/>
<CompII class="snapAlign"/>
<CompIII class="snapAlign"/>
<CompIV class="snapAlign"/>
</template>
<script>
import CompI from './components/CompI.vue'
import CompII from './components/CompII.vue'
import CompIII from './components/CompIII.vue'
import CompIV from './components/CompIV.vue'
export default {
name: 'App',
components: {
CompI,
CompII,
CompIII,
CompIV
}
}
</script>
<style>
*{
margin: 0;
padding: 0;
text-align: center;
}
.container{
overflow-y: scroll;
scroll-snap-type: y mandatory;
}
.snapAlign{
scroll-snap-align: start;
}
</style>
Here's what I've got for components, they're all basically the same as each other:
<template>
<div class="one">
<h1>One</h1>
</div>
</template>
<script>
export default {
name: 'CompI'
}
</script>
<style scoped>
.one{
height: 100vh;
width: 100vw;
background-color: green;
}
</style>
I also tried using vue-scroll-snap but it doesn't seem to work on components the way the guides show it working on divs. This was the guide I tried: Vue Scroll Snap Guide
Here's what I've got for App.vue using that method:
<template>
<vue-scroll-snap >
<CompI/>
<CompII/>
<CompIII/>
<CompIV/>
</vue-scroll-snap>
</template>
<script>
import CompI from './components/CompI.vue'
import CompII from './components/CompII.vue'
import CompIII from './components/CompIII.vue'
import CompIV from './components/CompIV.vue'
import VueScrollSnap from "vue-scroll-snap"
export default {
name: 'App',
components: {
CompI,
CompII,
CompIII,
CompIV,
VueScrollSnap
}
}
</script>
<style>
*{
margin: 0;
padding: 0;
text-align: center;
}
</style>
The components are the same as the other method.
The pages appear correctly but there's no scroll snap effect with either of these attempts. I'd like to try and use the components as full screen elements to snap to, the app will scale better like that.
+++ Solution +++
Ivo Gelov was correct, remove the class from <template>, place the components inside a div and give it the class "container". For some reason that alone wasn't enough, more style was needed on that class then it works. Here's the final CSS for that class:
.container{
width: 100%;
height: 100vh;
margin: 0 auto;
overflow-x: hidden;
overflow-y: scroll;
scroll-snap-type: y mandatory;
}

How do I use cell-class-name of el-table correctly?

I want to use cell-class-name to change the styling of individual cells based on the row and column of the specific cell.
As a minimal example I however just try to apply the same selected-cell-styling to every cell. Unfortunately, there's no effect through setting cell-class-name .
<template>
<div>
<el-table :data="myTable" :cell-class-name="cellStyle">
<el-table-column prop="name" label="name" width="115"/>
<el-table-column prop="occupation" label="occupation" align="right" width="115"/>
</el-table>
</div>
</template>
<script>
import { Table, TableColumn } from "element-ui";
export default {
components: {
"el-table": Table,
"el-table-column": TableColumn
},
name: "HelloWorld",
data() {
return {
myTable: [
{
name: "jhon",
occupation: "Lawyer"
},
{
name: "Tom",
occupation: "Judge"
}
]
};
},
methods: {
cellStyle() {
return "selected-cell"
}
},
props: {
msg: String
}
};
</script>
<style scoped>
.selected-cell {
background: red;
color: red;
}
</style>
https://codesandbox.io/s/element-ui-table-header-issue-1jdvu?fontsize=14&hidenavigation=1&theme=dark
Removing the scoped attribute from the style tag should fix it.
You can separate them in the following way:
<style>
.selected-cell {
background: red;
color: red;
}
</style>
<style scoped>
/* other styles here*/
</style>
I was experiencing a similar scenario where my class definitions were not recognised / applied on my designs.
As previously mentioned above, on my case Ive tried it with stylus and it worked very well too.
<style lang="stylus">
total-bg-color = rgba(242,242,242,0.3)
.total-row
background-color total-bg-color !important
td .cell
font-weight bold
</style>
<style lang="stylus" scoped>
color-white = rgba(255,255,255,1)
.layerTable
margin-top 0.5rem
background-color color-white !important
overflow-x auto
padding-left 0.5rem
padding-right 0.5rem
.el-table
margin-top 1rem
</style>
As you can notice, there is a block without the scoped tag (the first one), in that area it worked like a charm, but in the other block with the scoped tag, it did not.

How to properly use slot inside of vue js web component and apply styles

I have come across an issue where the implementation of slots in a webcomponent is not functioning as expected. My understanding of Web Components, Custom Elements and Slots is that elements rendered in a slot should inherit their style from the document and not the Shadow DOM however the element in the slot is actually being added to the Shadow DOM and therefore ignoring the global styles. I have created the following example to illustrate the issue that I am having.
shared-ui
This is a Vue application that is compiled to web components using the cli (--target wc --name shared-ui ./src/components/*.vue)
CollapseComponent.vue
<template>
<div :class="[$style.collapsableComponent]">
<div :class="[$style.collapsableHeader]" #click="onHeaderClick" :title="title">
<span>{{ title }}</span>
</div>
<div :class="[$style.collapsableBody]" v-if="expanded">
<slot name="body-content"></slot>
</div>
</div>
</template>
<script lang="ts">
import { Vue, Component, Prop } from 'vue-property-decorator'
#Component({})
export default class CollapsableComponent extends Vue {
#Prop({ default: "" })
title!: string;
#Prop({default: false})
startExpanded!: boolean;
private expanded: boolean = false;
constructor() {
super();
this.expanded = this.startExpanded;
}
get isVisible(): boolean {
return this.expanded;
}
onHeaderClick(): void {
this.toggle();
}
public toggle(expand?: boolean): void {
if(expand === undefined) {
this.expanded = !this.expanded;
}
else {
this.expanded = expand;
}
this.$emit(this.expanded? 'expand' : 'collapse');
}
public expand() {
this.expanded = true;
}
public collapse() {
this.expanded = false;
}
}
</script>
<style module>
:host {
display: block;
}
.collapsableComponent {
background-color: white;
}
.collapsableHeader {
border: 1px solid grey;
background: grey;
height: 35px;
color: black;
border-radius: 15px 15px 0 0;
text-align: left;
font-weight: bold;
line-height: 35px;
font-size: 0.9rem;
padding-left: 1em;
}
.collapsableBody {
border: 1px solid black;
border-top: 0;
border-radius: 0 0 10px 10px;
padding: 1em;
}
</style>
shared-ui-consumer
This is a vue application that imports the shared-ui web component using a standard script include file.
App.vue
<template>
<div id="app">
<shared-ui title="Test">
<span class="testClass" slot="body-content">
Here is some text
</span>
</shared-ui>
</div>
</template>
<script lang="ts">
import 'vue'
import { Component, Vue } from 'vue-property-decorator';
#Component({ })
export default class App extends Vue {
}
</script>
<style lang="scss">
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.testClass{
color: red;
}
</style>
main.ts
import Vue from "vue";
import App from "./App.vue";
Vue.config.productionTip = false;
// I needed to do this so the web component could reference Vue
(window as any).Vue = Vue;
new Vue({
render: h => h(App),
}).$mount('#app');
In this example I would expect the content inside the container to have red text however because Vue is cloning the element into the Shadow DOM the .testClass style is being ignored and the text is rendered with a black fill.
How can I apply .testClass to the element inside of my web component?
Ok, so I managed to find a workaround for this that uses native slots and renders the child components correctly in the correct place in the DOM.
In the mounted event wire up the next tick to replace the innerHtml of your slot container with a new slot. You can get fancy and do some cool replacements for named slots and whatnot but this should suffice for illustrating the workaround.
shared-ui
This is a Vue application that is compiled to web components using the cli (--target wc --name shared-ui ./src/components/*.vue)
CollapseComponent.vue
<template>
<div :class="[$style.collapsableComponent]">
<div :class="[$style.collapsableHeader]" #click="onHeaderClick" :title="title">
<span>{{ title }}</span>
</div>
<div ref="slotContainer" :class="[$style.collapsableBody]" v-if="expanded">
<slot></slot>
</div>
</div>
</template>
<script lang="ts">
import { Vue, Component, Prop } from 'vue-property-decorator'
#Component({})
export default class CollapsableComponent extends Vue {
#Prop({ default: "" })
title!: string;
#Prop({default: false})
startExpanded!: boolean;
private expanded: boolean = false;
constructor() {
super();
this.expanded = this.startExpanded;
}
get isVisible(): boolean {
return this.expanded;
}
onHeaderClick(): void {
this.toggle();
}
//This is where the magic is wired up
mounted(): void {
this.$nextTick().then(this.fixSlot.bind(this));
}
// This is where the magic happens
fixSlot(): void {
// remove all the innerHTML that vue has place where the slot should be
this.$refs.slotContainer.innerHTML = '';
// replace it with a new slot, if you are using named slot you can just add attributes to the slot
this.$refs.slotContainer.append(document.createElement('slot'));
}
public toggle(expand?: boolean): void {
if(expand === undefined) {
this.expanded = !this.expanded;
}
else {
this.expanded = expand;
}
this.$emit(this.expanded? 'expand' : 'collapse');
}
public expand() {
this.expanded = true;
}
public collapse() {
this.expanded = false;
}
}
</script>
<style module>
:host {
display: block;
}
.collapsableComponent {
background-color: white;
}
.collapsableHeader {
border: 1px solid grey;
background: grey;
height: 35px;
color: black;
border-radius: 15px 15px 0 0;
text-align: left;
font-weight: bold;
line-height: 35px;
font-size: 0.9rem;
padding-left: 1em;
}
.collapsableBody {
border: 1px solid black;
border-top: 0;
border-radius: 0 0 10px 10px;
padding: 1em;
}
</style>
shared-ui-consumer
This is a vue application that imports the shared-ui web component using a standard script include file.
App.vue
<template>
<div id="app">
<shared-ui title="Test">
<span class="testClass" slot="body-content">
Here is some text
</span>
</shared-ui>
</div>
</template>
<script lang="ts">
import 'vue'
import { Component, Vue } from 'vue-property-decorator';
#Component({ })
export default class App extends Vue {
}
</script>
<style lang="scss">
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.testClass{
color: red;
}
</style>
main.ts
import Vue from "vue";
import App from "./App.vue";
Vue.config.productionTip = false;
// I needed to do this so the web component could reference Vue
(window as any).Vue = Vue;
new Vue({
render: h => h(App),
}).$mount('#app');

Not able to call Vue component in vue cli application

I am trying to create the component for tile which can be used in multiple views. I have called the component in view but it is not working
Code of component (Tile.vue)
<template>
<div class="tile">
<label class="title">Tile Title</label>
</div>
</template>
<script>
export default {
name: 'CustomTile'
}
</script>
<style scoped lang="less">
.tile { width:248px;
height: 126px;
background-color:#e4e4e4;
.title {
padding-left: 15px;
}
}
</style>
code of view (Report.vue) where I am trying to call above component
<template>
<div>
<div class="topnav">
<button type="button">Expand/Collapse</button>
</div>
<div class="content">
<CustomTile></CustomTile>
</div>
</div>
</template>
<script>
import CustomTile from '#/components/Tile.vue'
export default {
name: 'Report'
}
</script>
<style scoped lang="less">
.topnav {
overflow: hidden;
background-color: #d6d6d6;
}
.topnav a {
float: left;
color: #f2f2f2;
text-align: center;
padding: 14px 16px;
text-decoration: none;
font-size: 17px;
}
.topnav a:hover {
background-color: #ddd;
color: black;
}.topnav a.active {
background-color: #4CAF50;
color: white;
}
.content {
height:500px;
width:500px;
}
</style>
CustomTile is not getting rendered. I am not able to figure out what/where is the problem.
You need to properly import the component in the parent where you want to use it and register it:
Report.vue:
<script>
import CustomTile from '#/components/Tile.vue'
export default {
name: 'Report',
components: {
CustomTile
}
}
</script>
And then, since CustomTile is a CamelCase component name, you need to use the following notation:
<custom-tile></custom-tile>