Vue 3: Styling a Named Slot - vue.js

So I've looked through stackoverflow and the documentation in Vue 3 but can't quite find what I'm looking for.
I'm trying to find a way to target a named slot, penetrate the scoped element within that slot, and override one of its children's styles. I assume I need the ::slotted selector and the :deep selector for this mission. Does anyone know how to do this?
Here is an example of the situation I am trying to solve for (LayoutContainer Component):
<section>
<slot name="text"></slot>
<slot></slot>
<slot name="sidebar"></slot>
</section>
the component that will go into the "text" slot (Eyebrow Component):
<section class="eyebrow-container">
<h1>{{title}}</h1>
<h6>{{description"}}</h6>
</section>
a completed view of the code on a page component:
<LayoutContainer>
<template #text>
<Eyebrow :title='test' :description="this is a description"></Eyebrow>
</template>
<PageBody></PageBody>
<template #sidebar>
<PageSideBar></PageSideBar>
</template>
</LayoutContainer>
Solutions I have tried in SCSS with no success:
::slotted(h6) { color: red }
::slotted(text){
:deep(.eyebrow-container) {
h6 { color: red; }
}
}
::slotted(text) {
:deep(h6) { color: red; }
}
and a few others I have forgotten at this point.
Does anyone have any ideas on how to get to the h6 tag inside of the Eyebrow Component from the Page Component's SCSS?

The slot content is owned by the parent passing them in.
So you don't need to use :slotted. You can simply use the :deep selector
<style scoped>
:deep(h6) {
color: red;
}
</style>
See it live
If you are wondering how to use :slotted then in your case it would be used in LayoutContainer component trying to style what the parent component passes in.
Scoped styling and styling child components from a parent don't work as you might think if you use multi-root node components.
So if you use mutli-root node component and :deep doesn't work, See my other answer

Related

Vue.js (v3): How to have a unique data-v-* hash for each component instance

I have the following code:
blah-foo.vue:
<template>
<div>Hello {{ name }}</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
props: {
name: {
type: String,
}
},
});
</script>
<style scoped>
div {
color: white;
}
</style>
and App.vue:
<template>
<blah-foo
name="Alice"
></blah-foo>
<blah-foo
name="Bob"
></blah-foo>
</template>
The result in my browser is the following:
<div data-v-73bdd40c>Hello Alice</div>
<div data-v-73bdd40c>Hello Bob</div>
Is there any way I could tell the vue loader to generate an unique data-v-* attribute for each of them ?
What is happening in production is that since the component blah-foo is called on many different lazy-loaded pages, I end up having many times
div[data-v-73bdd40c] {
color: white;
}
all overriding each other.
That isn't a problem in itself, it just does seem very disgraceful (code wise) after having loaded a few pages when inspecting elements.
That is not possible with vue-loader. And it shouldn't be done anyway.
The whole point of the data-v-xxx attribute is to identify the components in the DOM so vue can apply to them the correct scoped css.
If it ever applied uniq data-v attributes, it would not be able to apply the correct css.
I understand your problem is that, on production, you see in the css inspector several times the css code, right?
My guess is that it's related with sourcemaps, which may mess with the css inspector. But I can't help more without additional details on your project configurations.
Even if your component is used on several pages, its module will be fetched and loaded only once. You don't have the scoped css loaded several times, it's just an inspector problem.

How to apply a body {} style to specific vue component

I'm using scoped style for most of my components to not interfere with other components.
Some of my views/components need body { overflow: hidden; }, but others don't.
I can't use
<style scoped>
body {
overflow: hidden;
}
...
</style>
How can i apply this style when specific components are loaded? (i am using vue router if that helps)
You may send a prop to your component like described in here: https://v2.vuejs.org/v2/guide/components-props.html
Let's call you prop isOverflowHidden, and create .hidden class in your css.
After that, you can add your wrapper element (first tag in component) :class="{ hidden: isOverflowHidden }"
Or you can move it to a method.
If you want you can use this this action for inline-styling.
<div :style="{ overflow: (isOverflowHidden ? 'hidden' : '')}"></div>
You can read extended information in here: https://v2.vuejs.org/v2/guide/class-and-style.html#Binding-Inline-Styles

v-show alternative for Svelte

The case is that I'm showing Loading component on fetch request. I use store to set $loading to true and inside conditions is the Loading component. The problem is that the Loading component seems to be taking some time to show. It feels/looks like the reason is re-rendering of Loading component. So, I was looking for v-show like thing in Svelte, which I cannot find in Docs. (Don't get angry if its there, just tell me.)
Can anyone help with this case?
Either wrap it in an {#if someCondition} block, or slap a hidden={!someCondition} attribute on an element.
If you want a block of HTML that does not re-render when the condition is changed, here is a simple solution:
<script>
// Show.svelte
export let show = true;
</script>
<div class:hide={!show}>
<slot />
</div>
<style>
.hide {
display: none !important;
}
</style>
And then use the Show component to create that block:
<script>
import Show from "Show.svelte";
let show = true;
</script>
<button on:click={() => { show = !show}}>
Click to Show/Hide Content
</button>
<Show {show}>
<div>Content</div>
</Show>
I have posted the Show component as an npm package https://www.npmjs.com/package/svelte-show

Vue.js How to define (override) css style in a Component?

The default style for the p tag on my page has some bottom margin. My component uses p tags, and accordingly, the p tags in my component text show the corresponding bottom margin. How can I override/define new css style for the p tags in my component. I define my component like this:
Vue.component ('activity-component', {
props: {
customer_id:{},
is_admin:{},
isAdmin:{},
isKitsActionplan:{},
....
template:
`<div
class="row msDashboard-box"
style="cursor:default;padding-top:12px;
padding-bottom:12px;"
>
...
<p> ... </p>
});
Maybe u can try this approach,
Pass a variable with the class name to the component
<my-component v-bind:class="variable with class name"></my-component>
Then apply a rule to all p elements inside it, something like this i guess:
.test p{
your styles
}
U can see more here: vue api class and style bindings
I dont know for sure if this was what you wanted, but i gave it a shot :)
You have several options - choose your own adventure:
Use a global utility style
Somewhere globally, define a utility class like:
.u-margin-reset {
margin: 0;
}
Then in your template:
<p class="u-margin-reset">hello</p>
Use scoped CSS
If you are using single file components, you can use scoped css:
<template>
<p class="special-p">hello</p>
</template>
<style scoped>
.special-p {
margin: 0;
}
</style>
Use inline styles
Vue.component('activity-component', {
template: `<p style="margin:0;"></p>`,
});
or
Vue.component('activity-component', {
computed: {
myStyle() {
return {
margin: 0,
};
},
},
template: `<p :style="myStyle"></p>`,
});
As an aside, I'd recommend using a CSS reset that globally resets the margins of all elements to 0. Then each component should set the margins as needed for its child elements/components. This may not be reasonable if you already have a large codebase, however.

Programmatically destroy a component from other component

In my vue app some particular components inrouter-view are cached by using <keep-alive></keep-alive> that vue provides.
When a user logs in the posts.vue component is cached as I said above
Everything works fine.
Now I want to destroy this posts.vue component when the user logs out.So that posts.vue component re-renders when the user logs in again
The log out button is present in completely other component menu.vue and the on click logic for log out is present in this nenu.vue component
So how could I implement this using vm.$destroy() method from menu.vue component on posts.vue component
so my main problem is how to control lifecycle of one component from the other
the docs warn using this as follows :,
In normal use cases you shouldn’t have to call this method yourself.
Prefer controlling the lifecycle of child components in a data-driven fashion using v-if and v-for.
but in my case I cant use v-if or v-show
or is there any better way i could use
When you log in or log out from child component, emit 'login' or 'logout' event respectively.
<template>
<div id="app">
<router-view name='login' #login="login=true"></router-view>
<router-view name='header' #logout="login=false"></router-view>
<keep-alive v-if="login">
<router-view name='write'></router-view>
</keep-alive>
<router-view name='management'></router-view>
<router-view name='account'></router-view>
<router-view name='statistics'></router-view>
<router-view name='view'></router-view>
<router-view name='password'></router-view>
</div>
</template>
<script>
export default {
name: 'app',
data () {
return {
login: true
}
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 0;
height: 100%;
}
</style>
I had similar problem and one simple solution I found was to force reload the web application via location.reload(), which clears the keep-alive.
You might also find this discussion interesting: https://github.com/vuejs/vue/issues/6259
There is a pretty easy way to do this. Thanks to #loomchild for the link.
You can define on routes a meta object. Define it in such a way for routes using keep-alive that you want to invalidate.
{
path: '/invalidate_me',
...
meta: {uuid: generateUUID()} // You'll need a method for this or something similar
},
Check out Create GUID / UUID in JavaScript? for help with UUID if need be.
Then, in your keep alive definition:
<router-view :key="$route.path + ($route.params.id ? $route.params.id : '').toString() + ($route.meta && $route.meta.uuid ? $route.meta.uuid.toString() : '')"></router-view>
Something like that. If you don't include ids or don't want to cache by those, fine. Those are not related to the question, but something that I am using. So you really only need the meta part. We're basically telling Vue to consider a component to be defined by the combination of the id on the route (if it exists) and the uuid on the route (if it exists)
Then, when you want to invalidate inside a component, change the uuid. You may need to find the route in the lists on $router, or if you're already inside the correct component, you can do:
this.$route.meta.uuid = generateUUID();
This gets a new uuid and clears the cache.
Hope this helps someone else!