Bootstrap-vue - Modify Navbar from Vue component? - javascript

I have a Vue Bootstrap SPA with the following structure:
layout
navbar
breadcrumb
router view
component
some content
I would like my component to inject a submenu to the parent's navbar. For example:
export default {
name: 'Component',
data() {
return {
menu: [
'Item': {
href: '#'
}
]
}
},
mounted() {
parent.menu.addItem(this.menu)
}
}
Is this possible to do something like this?
Here the top layout:
<template>
<div>
<navbar :menu="menu"></navbar>
<router-view></router-view>
</div>
</template>
<script>
import NavBar from './navbar'
export default {
components: {
navbar: NavBar
},
data() {
return {
menu: [
{
text: "Foo",
href: "/foo",
},
{
text: "Bar",
href: "#",
},
],
};
},
};
</script>

You could emit an event to the parent component like :
data() {
return {
menu: [
{
href: '#'
}
]
}
},
mounted() {
this.$emit('emit-menu',this.menu)
}
in parent :
<template>
<navbar :menu="menu" #emit-menu="addItem"></navbar>
<router-view></router-view>
</div>
</template>
<script>
import NavBar from './navbar'
export default {
components: {
navbar: NavBar
},
methods:{
addItem(menu){
this.menu=[..this.menu,...menu]
}
},
data() {
return {
menu: [
{
text: "Foo",
href: "/foo",
},
{
text: "Bar",
href: "#",
},
],
};
},
};
</script>

Related

vue #/src/assets/images/1.jpg: hasn't been transpiled yet error

**I'm getting this error - vue #/src/assets/images/1.jpg: hasn't been transpiled yet error. I'm looping through tha App component static array. The src is specified correctly though. Using require vue method.
https://codesandbox.io/s/eloquent-grass-j1usw8?file=/src/components/v-carousel-item.vue
**
// APP
<template>
<v-carousel :carousel_data="sliderItems" />
</template>
<script>
import vCarousel from "./components/v-carousel.vue";
export default {
name: "App",
data() {
return {
sliderItems: [
{ id: 1, name: "img1", img: "1.jpg" },
{ id: 2, name: "img2", img: "2.jpg" },
{ id: 3, name: "img3", img: "3.jpg" },
],
};
},
components: {
vCarousel,
},
};
</script>
// Parent
<template>
<div class="container">
<div class="v-carousel">
<v-carousel-item
v-for="item in carousel_data"
:key="item.id"
:item_data="item"
/>
</div>
</div>
</template>
<script>
import vCarouselItem from "./v-carousel-item.vue";
export default {
components: {
vCarouselItem,
},
props: {
carousel_data: {
type: Array,
default: () => [],
},
},
};
</script>
// Child
<template>
<div class="v-carousel-item">
<img :src="require(`../assets/images/` + item_data.img)" alt="" />
</div>
</template>
<script>
export default {
props: {
item_data: {
type: Object,
default: () => {},
},
},
};
</script>
You want to require the images upfront.
export default {
name: "App",
data() {
return {
sliderItems: [
{ id: 1, name: "img1", img: require("#/assets/images/1.jpg") },
{ id: 2, name: "img2", img: require("#/assets/images/2.jpg") },
{ id: 3, name: "img3", img: require("#/assets/images/3.jpg") },
],
};
},
Then update the carousel item component.
<div class="v-carousel-item">
<img :src="item_data.img" alt="" />
</div>
Example: https://codesandbox.io/s/little-bush-ino5zc?file=/src/components/v-carousel-item.vue:11-91

How to hang click on only one element using v-for?

Hi I'm new to vue and I'm trying to complete one task. I have dynamic component toggles which I render with v-for. Can you suggest how can I pass an extra click to only one button (button 'border-left')? Desirable illustrative examples
<script>
import Vue from "vue";
import BorderLeftComonent from "./BorderLeftComonent.vue";
import TextBalloonComponent from "./TextBalloonComponent.vue";
import DashedComponent from "./DashedComponent.vue";
export default Vue.extend({
data() {
return {
component: "button[0].name",
color: "",
buttons: [
{
label: "A",
isActive: false,
type: "border-left",
name: "BorderLeftComonent",
},
{
label: "A",
isActive: false,
type: "text-balloon",
name: "TextBalloonComponent"
},
{
label: "A",
isActive: false,
type: "dashed",
name: "DashedComponent"
},
],
};
},
methods: {
toggleShowPopup() {
this.isOpen = !this.isOpen;
},
activeBtn(event, index) {
this.buttons[index].isActive = !this.buttons[index].isActive;
}
},
computed: {
currentComponent() {
return this.component;
},
cssVars() {
return {
'--border-left': this.color,
}
}
},
</script>
template is presented here
<template>
<div id="btn-box">
<button
v-for="(button, index) in buttons"
:key="index"
:class="button.isActive ? 'on' : 'off'"
#click="component = button.name, activeBtn($event, index)">
<div :class="`btn btn-${button.type}`">{{ button.label }}</div>
</button>
</div>
</template>
the method i need to pass to only one button
toggleShowPopup() {
this.isOpen = !this.isOpen;
}
You need conditional event binding. Try this:
<button
v-for="(button, index) in buttons"
:key="index"
:class="button.isActive ? 'on' : 'off'"
#click="component = button.name, activeBtn($event, index), button.type === 'border-left' && toggleShowPopup()">
<div :class="`btn btn-${button.type}`">{{ button.label }}</div>
</button>
This way might be a little more cleaned-up:
//import BorderLeftComonent from "./BorderLeftComonent.vue";
//import TextBalloonComponent from "./TextBalloonComponent.vue";
//import DashedComponent from "./DashedComponent.vue";
/*export default */new Vue/*.extend*/({
el: '#container',
template: `<div>
<div id="btn-box">
<button
v-for="(button, index) in buttons"
:key="index"
:class="button.isActive ? 'on' : 'off'"
:data-index="index"
#click="button.method">
<div :class="${"`"}btn btn-${"$"}{button.type}${"`"}">{{ button.label }}</div>
</button>
</div>
</div>`,
data() {
return {
component: "button[0].name",
color: "",
buttons: [
{
label: "A",
isActive: false,
type: "border-left",
name: "BorderLeftComonent",
method: this.toggleShowPopup,
},
{
label: "A",
isActive: false,
type: "text-balloon",
name: "TextBalloonComponent",
method: this.handleClick,
},
{
label: "A",
isActive: false,
type: "dashed",
name: "DashedComponent",
method: this.handleClick,
},
],
};
},
methods: {
toggleShowPopup(event) {
this.isOpen = !this.isOpen;
this.handleClick(event)
},
activeBtn(event) {
index = event.currentTarget.dataset.index
this.buttons[index].isActive = !this.buttons[index].isActive;
},
handleClick(event) {
index = event.currentTarget.dataset.index
button = this.buttons[index]
this.component = button.name;
this.activeBtn(event)
},
},
computed: {
currentComponent() {
return this.component;
},
cssVars() {
return {
'--border-left': this.color,
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="container"></div>

How to call array values dynamically from an existing array in Vuejs?

HelloWorld.vue
export const datalist = [
{ id: 1, val: "11", kk: "potter" },
{ id: 2, val: "22", kk: "james" },
{ id: 3, val: "55", kk: "limda" },
{ id: 4, val: "77", kk: "stepen" }
];
<template>
<div>
<b>Vuejs dynamic routing</b>
<div v-for="item in items" :key="item.id">
<b>{{ item.id }}.</b>
<router-link :to="{ name: 'UserWithID', params: { id: item.id } }">
{{ item.kk }}
</router-link>
</div>
<br /><br /><br />
<User />
</div>
</template>
<script>
import User from "./User.vue";
import { datalist } from "./datalist";
export default {
name: "HelloWorld",
components: {
User,
},
data() {
return {
items: datalist,
};
},
};
</script>
User.vue
import Vue from "vue";
import App from "./App.vue";
import VueRouter from "vue-router";
import HelloWorld from "./components/HelloWorld.vue";
import book from "./components/book.vue";
Vue.use(VueRouter);
const router = new VueRouter({
routes: [
{ path: "/", name: "User", component: HelloWorld },
{ path: "/", name: "BookWithID", component: book },
{ path: "/:id", name: "UserWithID", component: HelloWorld }
]
});
Vue.config.productionTip = false;
new Vue({
router,
render: (h) => h(App)
}).$mount("#app");
export const datalisttwo = [
{ id: 1, book: "steel", pen: "p1", gap: "1" },
{ id: 2, book: "iron", pen: "jp2", gap: "5" },
{ id: 3, book: "platinium", pen: "p3", gap: "2" },
{ id: 4, book: "gold", pen: "p4", gap: "9" }
];
<template>
<div>
<router-link :to="{ name: 'BookWithID' }">
{{ user.book }}
</router-link>
</div>
</template>
<script>
import { datalisttwo } from "./datalisttwo";
export default {
name: "User",
components: {},
data() {
return {
lists: datalisttwo,
};
},
computed: {
user: function () {
return this.lists.find((item) => item.id === this.$route.params.id);
},
},
};
</script>
As per the below code, in the datalisttwo.js I have array values like steel and pen Where i want to call both of them together like steel/pen as an api call in the mounted() .
When i click on the router-link, {{ user.book }} from User.vue component.
Ex:- I want to pass the pen/gap array values as query parameters. when clicked on {{ user.book }} from user.vue componet. Please go through codesandbox once, I tried adding computed property for pen and gap. But pen/gap --- but not calling dynamically
Here is my code:- https://codesandbox.io/s/new-hill-6yum4o?file=/src/components/User.vue
Your question and description is quite unclear, so I'll try to answer how I understand it. If that is not what you were expecting, try to explain it again clearly.
First, define your routes clearly. Here your have two routes pointing to '/'. Try to do it to have your user index at '/', your book at '/book/:id', and your user at 'user/:id'.
Second, I am unsure why you have your HelloWorld.vue in both User and UserWithId routes. If intended, disregard. If not, you should clean up that whole file to get the right route pointing to the right component.
Third, used on your example of potter, if you are looking at the book component, for which you haven't provided the code, you can do it such as:
...
computed: {
book() {
if (this.$route.params.id == null || this.$route.params.id == undefined) {
throw new Error('No book id provided')
}
return datalisttwo.find(_ => _.id == this.$route.params.id)
},
pen() {
this.book.pen
},
gap() {
this.book.gap
}
}
...
With this you'll be able to do whatever you whish with this.pen and this .gap.
Now, if you were to want to not import data list again, you can pass your retrieved pen & gap as query parameters: https://router.vuejs.org/api/
import Vue from "vue";
import App from "./App.vue";
import VueRouter from "vue-router";
import HelloWorld from "./components/HelloWorld.vue";
import book from "./components/book.vue";
Vue.use(VueRouter);
const router = new VueRouter({
routes: [
{ path: "/", name: "User", component: HelloWorld },
{ path: "/", name: "BookWithID", component: book },
{ path: "/:id", name: "UserWithID", component: HelloWorld }
]
});
Vue.config.productionTip = false;
new Vue({
router,
render: (h) => h(App)
}).$mount("#app");
export const datalisttwo = [
{ id: 1, book: "steel", pen: "p1", gap: "1" },
{ id: 2, book: "iron", pen: "jp2", gap: "5" },
{ id: 3, book: "platinium", pen: "p3", gap: "2" },
{ id: 4, book: "gold", pen: "p4", gap: "9" }
];
<template>
<div>
<router-link :to="{ name: 'BookWithID' }">
{{ user.book }}
</router-link>
</div>
</template>
<script>
import { datalisttwo } from "./datalisttwo";
export default {
name: "User",
components: {},
data() {
return {
lists: datalisttwo,
};
},
computed: {
user: function () {
return this.lists.find((item) => item.id === this.$route.params.id);
},
},
};
</script>
I ran this directly.
Error 1:
Imports must be at the top.
Look at 25, 0
Error 2:
Exports must be at the top
Look at 41, 8

Passing props (array) into a router-link path

I'm trying to dynamically update a <router-link> path based on what view component it is used within. Below is the <router-link> that is looping through the compItems array which is populated by each view component.
<router-link :to="{ name: compItem.name, params: { compItems: compItem.router } }" class="cz-ds-view-related-comp" v-for="compItem in compItems" :key="compItem.name">
<div class="related-comp_icon"><img class="related-comp_icon" :src="require('#/assets/home/icons/' + compItem.atomicIcon + '')" alt=""></div>
<div class="related-comp_title"><h3>{{ compItem.name }}</h3> <img src="../../assets/home/icons/arrow-right.svg"></div>
</router-link>
export default {
name: 'relatedSection',
props: {
compItems: {
type: Array
}
}
}
</script>
Below is an example of a view component defining router.
data () {
return {
compItems: [
{ name: 'Links', atomicIcon: 'atom.svg', router: 'links'},
{ name: 'Form Elements', atomicIcon: 'atom.svg', router: 'form-elements'},
{ name: 'Avatars', atomicIcon: 'atom.svg', router: 'avatars'},
{ name: 'Badges', atomicIcon: 'atom.svg', router: 'badges'}
]
}
}
And this is the console error I'm getting.
Thanks in advance!
Edit:
Here's a snapshot of the router file:
const routes = [{
path: '/',
name: 'home',
props: true,
component: Home
}, {
path: '/avatars',
name: 'avatars',
props: true,
component: Avatars
}, {
path: '/badges',
name: 'badges',
props: true,
component: Badges
}, {
path: '/buttons',
name: 'buttons',
props: true,
component: Buttons
}, {
path: '/breadcrumbs',
name: 'breadcrumbs',
props: true,
component: Breadcrumbs
}, {
path: '/form-elements',
name: 'form-elements',
props: true,
component: FormElements
}, {
path: '/icons',
name: 'icons',
props: true,
component: Icons
},
...
The router's route definitions all have lowercase name properties. When you use a <router-link> to access them by name, you need to use an exact match, but your compItems have capitalized names.
Also, you are using route params in the link but none of your routes have any defined.
Here is a refactoring to make the code clearer and correct:
<template v-for="compItem in compItems">
<router-link :to="{ name: compItem.name }" class="cz-ds-view-related-comp">
<div class="related-comp_icon">
<img class="related-comp_icon" :src="require('#/assets/home/icons/' + compItem.atomicIcon + '')" alt="">
</div>
<div class="related-comp_title">
<h3>{{ compItem.title }}</h3>
<img src="../../assets/home/icons/arrow-right.svg">
</div>
</router-link>
</template>
data () {
return {
compItems: [
{ title: 'Links', atomicIcon: 'atom.svg', name: 'links'},
{ title: 'Form Elements', atomicIcon: 'atom.svg', name: 'form-elements'},
{ title: 'Avatars', atomicIcon: 'atom.svg', name: 'avatars'},
{ title: 'Badges', atomicIcon: 'atom.svg', name: 'badges'}
]
}
}

Vue.js and Twig weird collision

Hello I have this code in my symfony 3 project :
TWIG TEMPLATE:
<div id="fileManagerContainer" class="AppContent">
{% verbatim %}
<!-- item template -->
<script type="text/x-template" id="item-template">
<li>
<div
:class="{bold: isFolder}"
#click="toggle"
#dblclick="changeType">
{{model.name}}
<span v-if="isFolder">{{open ? '-' : '+'}}</span>
</div>
<ul v-show="open" v-if="isFolder">
<item
class="item"
v-for="model in model.children"
:model="model">
</item>
<li class="add" #click="addChild">+</li>
</ul>
</li>
</script>
{% endverbatim %}
<p>(You can double click on an item to turn it into a folder.)</p>
<!-- the demo root element -->
<ul id="demo">
<item
class="item"
:model="treeData">
</item>
</ul>
</div>
VUE FILE :
// demo data
var data = {
name: 'My Tree',
children: [
{ name: 'hello' },
{ name: 'wat' },
{
name: 'child folder',
children: [
{
name: 'child folder',
children: [
{ name: 'hello' },
{ name: 'wat' }
]
},
{ name: 'hello' },
{ name: 'wat' },
{
name: 'child folder',
children: [
{ name: 'hello' },
{ name: 'wat' }
]
}
]
}
]
}
// define the item component
Vue.component('item', {
template: '#item-template',
props: {
model: Object
},
data: function () {
return {
open: false
}
},
computed: {
isFolder: function () {
return this.model.children &&
this.model.children.length
}
},
methods: {
toggle: function () {
if (this.isFolder) {
this.open = !this.open
}
},
changeType: function () {
if (!this.isFolder) {
Vue.set(this.model, 'children', [])
this.addChild()
this.open = true
}
},
addChild: function () {
this.model.children.push({
name: 'new stuff'
})
}
}
})
// boot up the demo
var demo = new Vue({
delimiters: ['{{', '}}'],
el: '#demo',
data: {
treeData: data
}
})
ant it works on jsfiddle, but doesnt do a thing in real project. All scripts are loaded perfectly, Vue.js works but just this piece of code does not. Any ideas ?

Categories