So for some reason when I add a subcomponent to the <b-modal> component it doesn't display the modal properly. It looks like the :is="currentModal" isn't being read properly.
<b-modal size="lg" v-model="currentModal" #ok="currentModal = null">
<component :is="currentModal" :email="email"></component>
</b-modal>
Basically currentModal is the current component to display. if I change it so that currentModal is a string, like such, it still doesn't work properly either:
<b-modal size="lg" v-model="currentModal" #ok="currentModal = null">
<Register v-if="currentModal=='register'":email="email"></Register>
<Login v-if="currentModal=='login'" :email="email"><Login>
</b-modal>
If I remove the b-modal tags altogether the Login and Register components work like they're supposed to, so the problem seems to be that the Register and Login components aren't reading currentModal properly from within b-modal, for some reason. Any help would be appreciated!
You need to use <b-container> as a direct child element of <b-modal>.
In this case, it'll be something like below:
<b-modal size="lg" v-model="currentModal" #ok="currentModal = null">
<b-container>
<component :is="currentModal" :email="email"></component>
</b-container>
</b-modal>
Related
I am working with Vuejs. I want to render components based on value of variable val.
My component looks like this
<template v-if="this.$val===1">
<component1 />
</template>
<template v-if="this.$val===2">
<component2 />
</template>
I have defined a global variable val using Vue.prototype and I am updating it using onclick function,where I am changing value of val to 2 but after clicking it doesnt show component2 instead of component 1.
Define val globally in main.js using following line of code
Vue.prototype.$val = 1;
Can someone please help me with this. Thanks
td,dr; Vue.prototypeis not reactive.
I'm going to enumerate issues as I observe them, hoping you'll find them useful.
You're not specifying which version of Vue you're using. Since you're using Vue.prototype, I'm going to guess you're using Vue 2.
Never use this in a <template>.
Inside templates, this is implicit (sometimes formulated: "inside templates this doesn't exist"). What would be this.stuff in controller, is stuff in the template.
You can't conditionally swap the top level <template> of a Vue component. You need to take the conditional either one level up or one level down:
one level up would be: you create separate components, one for each template; declare them and have the v-if in their parent component, rendering one, or the other
one level down would be: you move the v-if inside the top level <template> tag of the component. Example:
<template><!-- top level can't have `v-if` -->
<div v-if="val === 1">
val is 1
<input v-model="val">
</div>
<div v-else>
val is not 1
<input v-model="val">
</div>
</template>
<script>
export default {
data: () => ({ val: 1 })
}
</script>
Note <template> tags don't render an actual tag. They're just virtual containers which help you logically organise/group their contents, but what gets rendered is only their contents.1 So I could have written the above as:
<template><!-- top level can't have v-if -->
<template v-if="val === 1">
<div>
val is 1
<input v-model="val">
</div>
</template>
<template v-else>
<template>
<template>
<div>
val is not 1
<input v-model="val">
</div>
</template>
</template>
</template>
</template>
And get the exact same DOM output.
For obvious reasons, <template> tags become useful when you're working with HTML structures needing to meet particular parent/child constraints (e.g: ul + li, tr + td, tbody + tr, etc...).
They're also useful when combining v-if with v-for, since you can't place both on a single element (Vue needs to know which structural directive has priority, since applying them in different order could produce different results).
Working example with what you're trying to achieve:
Vue.prototype.$state = Vue.observable({ foo: true })
Vue.component('component_1', {
template: `
<div>
This is <code>component_1</code>.
<pre v-text="$state"/>
<button #click="$state.foo = false">Switch</button>
</div>
`})
Vue.component('component_2', {
template: `
<div>
This is <code>component_2</code>.
<pre v-text="$state"/>
<button #click="$state.foo = true">Switch back</button>
</div>
`})
new Vue({
el: '#app'
})
<script src="https://unpkg.com/vue#2.7.10/dist/vue.min.js"></script>
<div id="app">
<component_1 v-if="$state.foo"></component_1>
<component_2 v-else></component_2>
</div>
Notes:
<div id="app">...</div> acts as <template> for the app instance (which is, also, a Vue component)
Technically, I could have written that template as:
<div id="app">
<template v-if="$state.foo">
<component_1 />
</template>
<template v-else>
<component_2 />
</template>
</div>
, which is pretty close to what you were trying. But it would be slightly more verbose than what I used, without any benefit.
I'm using a Vue.observable()2 for $state because you can't re-assign a Vue global. I mean, you can, but the change will only affect Vue instances created after the change, not the ones already created (including current one). In other words, Vue.prototype is not reactive. This, most likely, answers your question.
To get past the problem, I placed a reactive object on Vue.prototype, which can be updated without being replaced: $state.
1 - there might be an exception to this rule: when you place text nodes inside a <template>, a <div> wrapper might be created to hold the text node(s). This behaviour might not be consistent across Vue versions.
2 - Vue.Observable() was added in 2.6.0. It's a stand-alone export of Vue's reactivity module (like a component's data(), but without the component). In v3.x Vue.Observable() was renamed Vue.reactive(), to avoid confusion/conflation with rxjs's Observable.
global variables are accessed in template without this keyword which means $val===1 will work.
Solution 1:
<template>
<component1 v-if='$val === 1' />
<component2 v-else/>
</template>
This will work.
But you could make use of dynamic components in your case.
Solution 2:
<template>
<component :is='currentComponent'/>
</template>
<script>
\\imports of your components will go here
export default {
name: 'app',
components: {
component1, component2
},
computed:{
currentComponent(){
return this.$val === 1?component1:component2;
}
}
}
</script>
Dynamic components are more performant and helps you maintain state of component.
I'll try to explain my problem.
I have a VueJS component, that uses slots in its template. For example:
App.vue:
<template>
<div>
<slot name="content" :content="somecontent"></slot>
</div>
</template>
<script>
export default {
data(){
return {
somecontent: "test"
}
}
}
</script>
And now I would like to initialize it like this:
<App>
<template #content="somecontent">
<div>{{somecontent}}</div>
</template>
</App>
The idea is that the user would be able to override a part of the widget content and still have some data that the widget provides. A real-life example would be a list where the elements are being loaded remotely and the user can override the list item template.
I know now that custom elements do not support scoped slots. Is there any other way to achieve this? The template syntax does not need to be like above.
Thanks for any info that could solve my problem.
I just experienced something I don't understand with a running vue app.
I have a button, which is included in muliple parent components.
I just added some eventlisteners for mouseover and mouseout.
In one case I have to call the function with .native and when the button component is used in another parent, I have to leave it away to be called.
For the sake of completeness I leave everything of my template in here:
<template>
<component
:is="computedType"
ref="button"
:class="[$options.name, { 'is-reversed': reversed, 'is-naked': naked, 'is-disabled': disabled, 'has-inverted-textcolor': invertedTextColor }]"
:style="backgroundColor ? `background-color: ${backgroundColor}` : false"
:disabled="disabled"
:aria-disabled="disabled"
:to="!external ? to : false"
:aria-controls="ariaControls"
:aria-expanded="ariaExpanded"
:target="!target && external && mailto ? '_blank' : target"
:type="computedType === 'button' ? buttonType : false"
:rel="external ? 'noopener' : false"
#click="click"
#mouseover.native="triggerHover"
#mouseout.native="releaseHover"
#mouseover="triggerHover"
#mouseout="releaseHover"
>
<slot />
</component>
</template>
You can see that I now added the #mouseover and #mouseover.native to cover both cases.
I would really be interested in what causes the difference here.
To be honest, I don't fully understand the part in the vue docs.
I also realized that I register a difference in how I can access the style properties of my DOM element.
I gave it a reference: ref="button" and I would normally access it via:
this.$refs.button.style.backgroundColor
But in the case where I need the .native to call the method, I also need $el to access the stlye propperty: this.$refs.button.$el.style.backgroundColor.
I now not only don't understand what's exactly going on but I also have a problem that my button component will fail in one of the two cases, because I can't access the style property in the same way.
The only difference in how I use the component is, that in one case the button appears in a v-for-loop. Everything else is exactly the same.
Is this causing the different behaviour?
Thank you for some background information about that.
Cheers
Edit
Here are my parent component templates:
This is where I must use #mouseout without native:
<template>
<div class="TwoColumnModule">
<div class="TwoColumnModule__text">
<BRichtext v-if="component.text" class="TwoColumnModule__richtext TwoColumnModule__BRichtext" :content="component.text" />
<BBtn
v-if="component.button_checkbox"
class="TwoColumnModule__BBtn"
:inverted-text-color="component.button_text_color"
:background-color="component.button_color"
:target="component.button_link.target"
:to="checkLinkIfInternal(component.button_link.url) ? prepareNuxtLink(component.button_link.url) : component.button_link.url"
:external="typeButton"
:href="checkLinkIfInternal(component.button_link.url) ? false : component.button_link.url"
:hover-color="component.button_color_hover"
>
{{ component.button_link.title }}
</BBtn>
</div>
<!-- eslint-disable vue/no-v-html -->
<div
v-else
class="TwoColumnModule__video"
v-html="component.vimeo_video"
/>
</div>
</template>
And this is where I need .native and must access the reference via
this.$refs.button.$el:
<template>
<div class="Storyboard">
<BHeading v-if="component.main_title" :level="3" class="Storyboard__heading Storyboard__BHeading">{{ component.main_title }}</BHeading>
<div v-for="(item, index) in component.four_column_layout" :key="index" class="Storyboard__item--four-columns">
<BRichtext v-if="item.text" class="Storyboard__richtext Storyboard__BRichtext" :content="item.text" />
<BBtn
v-if="item.button.button_checkbox"
class="TwoColumnModule__BBtn"
:inverted-text-color="item.button.button_text_color"
:background-color="item.button.button_color"
:target="item.button.button_link.target"
:to="checkLinkIfInternal(item.button.button_link.url) ? prepareNuxtLink(item.button.button_link.url) : item.button.button_link.url"
:external="checkIfTypeButton()"
:href="checkLinkIfInternal(item.button.button_link.url) ? false : item.button.button_link.url"
:hover-color="item.button.button_color_hover"
>
{{ item.button.button_link.title }}
</BBtn>
</div>
</div>
</template>
I'm completely new to Vue.js and I think I have a bit of understanding of how a router works with things like:
<router-link to="/">
But I am not really understanding what the following line does:
<router-view :key="$route.fullPath"></router-view>
I believe router-view by itself makes sure the content is displayed but what does the key part mean?
See Special Attributes - key
It can also be used to force replacement of an element/component instead of reusing it. This can be useful when you want to:
Properly trigger lifecycle hooks of a component
Trigger transitions
$route.fullPath is defined as
The full resolved URL including query and hash.
If you bind key to $route.fullPath, it will always "force a replacement" of the <router-view> element / component every time a navigation event occurs.
As mentioned above, this is most probably done in order to trigger a transition / animation.
In Vue-Router 4.0, this is implemented using a scoped slot. According to the docs the following is necessary to force a route to reload.
<router-view v-slot="{ Component, route }">
<component :is="Component" :key="route.path" />
</router-view>
This is necessary for integrating with Vue 3 transitions.
<router-view v-slot="{ Component, route }">
<transition name="fade">
<component :is="Component" :key="route.path" />
</transition>
</router-view>
Note: Putting the :key="route.path" directly on the <router-view> can cause subtle routing issues. Such as a route with a redirect triggering twice.
I have a problem with passing props in react. This is my folder structure:
src
Component
Button.js
Container
PageContainer.js
Page
Page.js
I am using Bootstrap 4 to create a Button within Button.js:
<div>
<a className="btn btn-primary sharp" href={this.props.url} role="button">{this.props.btnName}</a>
</div>
There is nothing else in the class Button. So now I put a Button into the class PageContainer:
<div>
<Header/>
<Button url={this.props.urlBack} btnName="Back"/>
<Button url={this.props.urlNext} btnName="Next"/>
</div>
As you can see I passed a title to the buttons: Back and Next. That works fine. I could now add an url and it would work fine, but that's not what I want.
I added the PageContainer to the class Page such that I can add an url at this level:
<div>
<PageContainer urlBack="/" urlNext="/nextPage"/>
</div>
For some reason this is not working. Can someone explain me how I can pass props from grandparent to grandchild? In the documentation it says that this is the way how to do it. I also get no error, because the prop is not passed from Page to PageContainer. A console.log(this.props.urlBack) results in undefined.
PS: Maybe you asking why I am using the Page.js or for what reason do I have the PageContainer. First: There are far more components, I just left them out. Second: I wanna reuse the PageContainer for several pages such that I just have to change the url.
It doesn't look like you are passing props to your <VideoContainer /> component. You are merely assigning it as a routed component <Route />
Your answer can be found here:
React react-router-dom pass props to component
i.e.
<Route path="/algorithmus/bubblesort/video"
render={(props) => <VideoContainer {...props} />}
/>
However, I don't think this will get your your this.props.url and this.props.btnName. this.props.path, yes ..but you may have to revisit some logic there.
UPDATE:
After reading your comment and checking your repo, it doesn't look like there's anything wrong with your setup. I have emulated your BubblesortVideo -> VideoContainer hierarchy at the following:
https://stackblitz.com/edit/react-eaqmua