Vue: Passing a link to components prob - vue.js

why does this doesn't work?
Child-Component:
<template>
<button class="btn" #click="router.push('{{link}}')">{{ text }}</button>
</template>
<script setup lang="ts">
import { defineProps } from 'vue';
import { useRouter } from 'vue-router';
const router = useRouter();
const props = defineProps({
text: String,
link: String,
})
</script>
Parent-Component:
`
<Button text="To MainView" link="'/mainview'"></Button>
`
Passing text works, passing link also shows the right String in Console (/mainview) but the link it shows me is http://localhost:8080/%7B%7Blink%7D%7D.
And now I'm confused because in my understanding it should work. Thanks!

In your code, this line #click="router.push('{{link}}')" don't work like that. You can write like this
#click="$router.push(`${link}`)"
Vue Mustache syntex i.e {{}} only work inside HTML tag like
<div>{{val}}</div>
to call an event you can simply run like below
<div #click="any valid js syntex">Hello</div>

You've already defined link as a static prop in your Child Component so you don't have to add single quotes around /mainview:
<Button text="To MainView" link="/mainview"></Button>
It would only be necessary if the prop is a dynamic prop (ex. :link="'/mainview'") but in this case, since you're defining /mainview directly into the prop then you can omit the single quotes (Static Props vs Dynamic props). Additionally, you can pass link directly to the #click event like so:
<button class="btn" #click="router.push(link)">{{ text }}</button>
or just use router-link instead:
<router-link :to="link">
<button class="btn">{{ text }}</button>
</router-link>

Related

How to declare local property from composition API in Vue 3?

In Vue 2 I would do this:
<script>
export default {
props: ['initialCounter'],
data() {
return { counter: this.initialCounter }
}
}
</script>
In Vue 3 I tried this:
<script setup>
import { ref } from 'vue';
defineProps({ 'initialCounter': Number })
const counter = ref(props.initialCounter)
</script>
This obviously doesn't work because props is undefined.
How can I bind one-way properties to a local variable in Vue 3?
It seems the result of defineProps is not assigned as a variable. check Vue3 official doc on defineProps. Not really sure what is the use case of ref() here but toRef API can be used as well.
import { ref } from 'vue';
const props = defineProps({ 'initialCounter': Number })
const counter = ref(props.initialCounter)
Retrieving a read-only value and assigning it to another one is a bad practice if your component is not a form but for example styled input. You have an answer to your question, but I want you to point out that the better way to change props value is to emit update:modalValue of parent v-model passed to child component.
And this is how you can use it:
<template>
<div>
<label>{{ label }}</label>
<input v-bind="$attrs" :placeholder="label" :value="modelValue" #input="$emit('update:modelValue', $event.target.value)">
<span v-for="item of errors">{{ item.value }}</span>
</div>
</template>
<script setup>
defineProps({ label: String, modelValue: String, errors: Array })
defineEmits(['update:modelValue'])
</script>
v-bind="$attrs" point where passed attributes need to be assigned. Like type="email" attribute/property of a DOM element.
And parent an email field:
<BaseInput type="email" v-model="formData.email" :label="$t('sign.email')" :errors="formErrors.email" #focusout="validate('email')"/>
In this approach, formdata.email in parent will be dynamically updated.

VueJS 3: Access root HTML element in a slot

how do I reliably access the root HTML element in a slot? I tried slots.default()[0].el but its not consistent. I realized if the slot is a simple html, it is not null, which is great, but if it has some vue directives or components, it will be null. So how can I reliably get hold of the root HTML Element in a slot?
I found one possible solution: that is to have the slot content provider to explicitly set the element you want to reference to by providing a slot-prop method to invoke. And also since Vue 3 template supports multiple root elements, its not really reliable to assume that the slot will always have one root element.
Here is the example of the solution:
<template>
<slot :set-element="(el) => element = el"></slot>
</template>
<script setup lang="ts">
import { ref, watchEffect } from "vue";
const element = ref<Element | null>(null);
watchEffect(() => {
console.log(element.value);
});
</script>
In the usage of the component, inject the slot props and use the Function Refs syntax
<MyComponent v-slot="{ setElement }">
<div :ref="(el) => setElement(el)">...</div>
<div>...</div>
</MyComponent>
You could access a slot's root HTML element directly with the slot's vnode property and in your component script, you can using this.$refs.root.
Here is an example:
<template v-slot:[slotName]="{ vnode }">
<div ref="root">
<!-- your slot content goes here -->
</div>
</template>
mounted() {
const root = this.$refs.root as HTMLElement;
console.log(root);
}
EDIT
The official documentation for v-slot can be found here: https://vuejs.org/api/built-in-directives.html#v-slot
Instead, the official documentation for $refs can be found here: https://v3.vuejs.org/guide/composition-api-template-refs.html

VueJS - Generating html string of a component in computed property

I'm looking for a suggestion regarding a cleaner approach to generate a component data as html string and to pass it raw through the props of the component.
component-a.js
import componentB from './component-b'
computed: {
tooltipHTML() {
render "<componentB :name='user1'/>
}
}
I would prefer something similar to the above idea.
Generating HTML in a computed property and passing it as props to another component to be rendered in that component will not work.
What you are looking for is Slots
Since the complete code is not provided I guess you wanted to render <componentB :name='user1'/> inside another component( a tooltip component)
You would be doing it as follows using slots:
<tooltip-comp>
<componentB :name='user1'/>
</tooltip-comp>
In your tooltip component
//tooltip component
<template>
<div class="my-tooltip">
<p>my tooltip</p>
<slot></slot>
</div>
</template>

vuejs Setting Dynamic path to template's src

I have an vue application which I have divided in components kinda manner seen below.
What I want is to bind src property in <template> like below so that I could have a dynamic path every time a user asks for different template to get loaded.
The .ts file will have same code in use for every different template. which prompt me to ask this question.
Please suggest a solution to it. Or am I going into right direction or not to achieve this ?
One way to achieve dynamic templates being rendered is using dynamic component rendering:
App.vue:
<template>
<div>
<button #click="selectedComponent = 'app-quote'">Quote</button>
<button #click="selectedComponent = 'app-author'">Author</button>
<button #click="selectedComponent = 'app-new'">New</button>
<hr>
<component :is="selectedComponent"></component>
</div>
</template>
<srcript>
import Quote from './components/Quote.vue'
import Author from './components/Author.vue'
import New from './components/New.vue'
data: function() {
return {
selectedComponent: 'app-quote'
}
},
components: {
'app-quote': Quote,
'app-author': Author,
'app-new': New
}
</script>

Vue Multiselect does not update {{ value }} via v-model

I am using this example for Vue Multiselect "^2.0.0-beta.14" in Laravel 5.3. https://github.com/monterail/vue-multiselect/tree/2.0#install--basic-usage
The plugin renders correctly but I cannot get the selection via v-model. I am expecting #{{ selected }} to update with the current selection.
app.js
Vue.component('dropdown', require('./components/Multiselect.vue'));
VUE JS
<template>
<div>
<multiselect
v-model="value"
:options="options">
</multiselect>
</div>
</template>
<script>
import Multiselect from 'vue-multiselect'
export default {
components: { Multiselect },
data () {
return {
value: null,
options: ['list', 'of', 'options']
}
}
}
</script>
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
HTML
<div id="app">
<h3>Dropdown</h3>
<div>
<label class="typo__label">Single select</label>
<dropdown></dropdown>
<pre class="language-json"><code>#{{ value }}</code></pre>
</div>
</div>
NB
The official example uses selected instead of value but this does not work either. According to the docs selection is replaced by value as of V2.
If you are using TypeScript Interfaces with Vue.js 2.0, avoid using a optional properties to store the value from child components. i.e. if your property is
value:? IMyCustomInterface instead use value: MyCustomObject|null and set the object to null in the constructor.
If the property is optional, it will compile fine, but child components won't update it properly.
The reason value is not showing up in root is because the data is isolated to the dropdown component. To get your data from a component to show up in the Root you need to use props.
See this question for a detailed explanation
How to get data from a component in VueJS