toggle tabs with vue props not working - Quasar - javascript

I have quasar tabs that are being rendered in v-for loop.
Problem is that when I provide array as prop from parent, then my tabs stop working to toggle whereas it works with local array.
<div v-for="test in tests">
<q-tabs
v-model="test.status"
align="justify"
>
<q-tab
v-for="(tab, tabIndex) in tabs"
:key="tabIndex"
:name="tab.value"
>
{{ tab.name }}
</q-tab>
</q-tabs>
</div>
<script>
export default {
props: ['tests']
data (){
return {
tabs: [
{
name: 'Required',
value: 'required'
},
{
name: 'Not Required',
value: 'not-required'
}
]
}
}
Tests Data
[
{
id: "test 1"
name: 'TEST 1'
status: 'required'
},
{
id: "test 2"
name: 'TEST 2'
status: 'not-required'
}
]
Expected result
Toggle of tabs must work when component is provided with props data to iterate.

Related

Why will vue draggable items not move between lists?

I am trying to create a card builder using Vue draggable. I have a row full of cardElements and these can be dragged to the card builder. The card builder is a rectangle made up of individual "buckets" of lists. Here is what it looks like:
Card builder Layout
Here is the script to generate the elements list:
import draggable from 'vuedraggable'
export default {
name: "clone",
display: "Clone",
order: 2,
components: {
draggable
},
data() {
return {
cardElements: [
{ name: "Logo", id: 1 },
{ name: "Stamp", id: 2 }
],
arrA: []
};
},
methods: {
log: function(evt) {
window.console.log(evt);
}
}
};
You can see that the cardElements are correctly rendered from script and this is how they are rendered in the HTML:
<draggable class="dragArea list-group" :list="cardElements" :group="{ name: 'people', pull: 'clone', put: false }" #change="log">
<div class="list-group-item" v-for="element in cardElements" :key="element.name">
{{ element.name }}
</div>
</draggable>
These are draggable and I can change their order like so:
Changed Order of items
Each "bucket" on the card is declared as an array like so (this is only for the top left space on the card builder, each square is a different number):
data() {
return {
cardElements: [
{ name: "Logo", id: 1 },
{ name: "Stamp", id: 2 }
],
arrA: []
};
}
<draggable class="dragArea list-group" :list="arrA" group="cardItem" #change="log">
<div class="lvl1-1 bucket empty" v-for="element in arrA" :key="element.name">
{{ element.name }}
</div>
</draggable>
But when I drag an element from the cardElements list to the arrA list, it doesn't move and I'm not sure why. I can get it working in the example code supplied here, but not when I change it to my own code.
The issue was that I had the group defined as :group="{ name: 'people', and then was calling it as group="cardItem" so I changed :group="{ name: 'people', to this :group="{ name: 'cardItem', and it worked

Adding 'input' used to edit array object key in selected elements - Vue.js

i need to add an input field used to edit the title in the currently selected element component (selection was done by clicking). The problem is that there should be one input and work for each selected element. I couldn't find a similar task and solving on the Internet. Maybe someone will tell you how to do it?
ItemsList.vue component:
<template>
<input type="text" placeholder="Edit selected items"/>
<div class="items-col">
<ul class="items-list">
<Item v-for="item in items" :key="item" :title="item.title"/>
</ul>
</div>
</template>
<script>
import Item from '#/components/Item.vue'
export default {
data() {
return {
items: [
{ title: 'item 1' },
{ title: 'item 2' },
{ title: 'item 3' },
{ title: 'item 4' },
{ title: 'item 5' },
{ title: 'item 6' }
]
}
},
components: {
Item
}
}
</script>
Item.vue component:
<template>
<li class="item" #click="isActive = !isActive" :class="{ active: isActive }">{{ title }}</li>
</template>
<script>
export default {
name: 'ItemsList',
data() {
return {
isActive: false
}
},
props: {
title: String
}
}
</script>
<style>
.item.active {
color: red;
}
</style>
You might want to reconsider which component should be responsible of knowing which item is active at any point of time: hint: it should be the parent/consuming component. That is because you:
Have only a single input field, which means only one item can be edited at any point of time
You want to let the parent/consuming component to be the single source of truth of which item is actually active
Therefore, the first thing you should do is to ensure that isActive is a prop on the Item component, while the parent ItemList component keeps track of which is active at any point.
Then, it is simply a matter of:
Implementing a toggling logic for the isActive flag. The flag is updated when a native click event is fired from the Item component. For the toggling logic, we can simply toggle between a zero-based index of the click item, or -1, which we used to indicate that nothing is active.
Using v-bind:value and a computed property to reflect the value of the currently active item. We can simply retrieve it using this.items[this.activeIndex] on the parent component
Listening to the onInput event and then updating the correct item
See proof-of-concept below:
Vue.component('item-list', {
template: '#item-list-template',
data() {
return {
items: [{
title: 'item 1'
},
{
title: 'item 2'
},
{
title: 'item 3'
},
{
title: 'item 4'
},
{
title: 'item 5'
},
{
title: 'item 6'
}
],
activeIndex: -1,
}
},
methods: {
onItemClick(index) {
this.activeIndex = this.activeIndex === index ? -1 : index;
},
setActiveItemValue(event) {
const foundItem = this.items[this.activeIndex];
if (!foundItem) return;
return this.items[this.activeIndex].title = event.currentTarget.value;
}
},
computed: {
activeItemValue() {
return this.items[this.activeIndex]?.title ?? '';
}
}
});
Vue.component('item', {
template: '#item-template',
props: {
isActive: Boolean,
title: String
}
});
new Vue({
el: '#app'
});
li.active {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<item-list></item-list>
</div>
<script type="text/x-template" id="item-list-template">
<div>
<input type="text" placeholder="Edit selected items" :value="activeItemValue" #input="setActiveItemValue" />
<div class="items-col">
<ul class="items-list">
<item v-for="(item, i) in items" :key="i" :title="item.title" :isActive="activeIndex === i" #click.native="onItemClick(i)" />
</ul>
</div>
</div>
</script>
<script type="text/x-template" id="item-template">
<li class="item" :class="{ active: isActive }">{{ title }}</li>
</script>
If you want a solution with your current components (not the cleanest) , you can actually emit an event to the parent component when you activate an element that event should containe the index of the object in the items array
Then you can use the index to get and set the title variable , here is an example :
Item.vue
<template>
<li class="item" #click="activateItem" :class="{ active: isActive }">{{ title }}</li>
</template>
<script>
export default {
name: 'ItemsList',
data() {
return {
isActive: false
}
},
methods:{
activateItem() {
this.isActive = !this.isActive
this.$emit('activatedItem', this.isActive ? this.index : null)
}
},
props: {
title: String,
index: Number
}
}
</script>
<style>
.item.active {
color: red;
}
</style>
ItemList.vue
<template>
<div>
<input type="text" placeholder="Edit selected items" #input="inputChange" :value="inputValue"/>
<div class="items-col">
<ul class="items-list">
<Item v-for="(item, index) in items" :key="index" :title="item.title" :index="index" #activatedItem="itemSelected"/>
</ul>
</div>
</div>
</template>
<script>
import Item from '#/components/Item.vue'
export default {
data() {
return {
items: [
{ title: 'item 1' },
{ title: 'item 2' },
{ title: 'item 3' },
{ title: 'item 4' },
{ title: 'item 5' },
{ title: 'item 6' }
],
selectedIndex: null,
inputValue: ''
}
},
methods:{
itemSelected(index){
this.selectedIndex = index;
if(this.selectedIndex != null) {
this.inputValue = this.items[this.selectedIndex].title;
}
},
inputChange(event){
this.inputValue = event.target.value;
if(this.selectedIndex != null){
this.items[this.selectedIndex].title = this.inputValue
}
}
},
components: {
Item
}
}
</script>
You should also be aware that with the component Item you have given you can select more than one item !

How to pass an object to a web component generated width Vue?

My component has to render an object to fill the content. When I am working in the Vue project, I have not problem to pass this object with
<my-compnent :card="card"></my-component>
But when I tried to use the built component, it doesn't read "MyCard" as an object, ir reads as a string... please, some help
I have tried in the same way that vue, using :card but it doesn´t work, if I only use card="card" it get access to the component but without the object
My code:
<script>
card =
{
name: 'card',
graphic: 'https://static.anychart.com/images/gallery/v8/line-charts-line-chart.png',
subtitle: 'about this card',
info: 'this is a test content card',
selector1: [
{ name: 'opt 1' },
{ name: 'opt 2' },
{ name: 'opt 3' }
],
selector2: [
{ name: 'opt A' },
{ name: 'opt B' },
{ name: 'opt C' }
]
}
</script>
<script src="https://unpkg.com/vue"></script>
<body>
<my-component card="card"></card>
</body>
And the error:
[Vue warn]: Invalid prop: type check failed for prop "card". Expected Object, got String with value "card".
Also I have tried
<my-component :card="card"></card>
but this only works in a vue project, not in exported web-components. It gives this error:
[Vue warn]: Error in render: "TypeError: can't access property "name", _vm.card is undefined"
card="card" will pass the string 'card' as value to the card attribute. If you want to pass a JS object tp an exported web component outside Vue, then you'll have to do it in JS itself.
<script>
const card =
{
name: 'card',
graphic: 'https://static.anychart.com/images/gallery/v8/line-charts-line-chart.png',
subtitle: 'about this card',
info: 'this is a test content card',
selector1: [
{ name: 'opt 1' },
{ name: 'opt 2' },
{ name: 'opt 3' }
],
selector2: [
{ name: 'opt A' },
{ name: 'opt B' },
{ name: 'opt C' }
]
}
let comp = document.querySelector('my-component')
comp.card = card
</script>
<body>
<my-component card="card"></card>
</body>
You can use v-bind:card or simply ':card' only if you're working inside vue project, in which case you don't need to use exported component. But in this case the object card needs to be passed to the data property of your Vue instance, otherwise Vue can't find it. This is why you get the error.
<script>
const app = new Vue({
el: '#app',
data: {
card,
},
}
</script>

Bootstrap Modal popup component not working on click of angular formly element

I have a reuquirement in which i am using a multiselect of angular formly which also has logo for each row. On click of this logo i want to load modal popup component, i am following a Stackblitz Demo for calling modal-component in app.component on click of element.
The link of demo which i followed : Bootstrap modal Stackblitz Demo
My workaround demo which i am implenenting is as follows : Workaround Demo Stackblitz
The error which i am getting on click of logo with the function openModal() is of undefined object.
How to i rectify this while working with angular formly?
Following is the snippet of the code:
multiselect formly component
#Component({
selector: 'formly-field-multiselect',
template: `<br><p-multiSelect [options]="to.options"
[formControl]="formControl"
[formlyAttributes]="field"
[showClear]="!to.required" >
<ng-template let-item pTemplate="item">
<div>{{item.label}}</div>
<div>
<i class="pi pi-check" (click)="to.openModal()"></i>
</div>
</ng-template>
</p-multiSelect>
`,
})
app.component.ts in which modal component(calenderComponent) is called
fields: FormlyFieldConfig[] = [
{
key: "select",
type: "multiselect",
templateOptions: {
multiple: false,
placeholder: "Select Option",
options: [
{ label: "Select City", value: null },
{ label: "New York", value: { id: 1, name: "New York", code: "NY" } },
{ label: "Rome", value: { id: 2, name: "Rome", code: "RM" } },
{ label: "London", value: { id: 3, name: "London", code: "LDN" } },
{
label: "Istanbul",
value: { id: 4, name: "Istanbul", code: "IST" }
},
{ label: "Paris", value: { id: 5, name: "Paris", code: "PRS" } }
],
openModal() {
this.modalRef = this.modalService.show(CalenderComponent, {
initialState: {
title: 'Modal title'
}
});
},
}
}
If you debug this, you can see that this in the context of your function (openModal), is the field you defined in the fields object, which doesnt have the modalService service you injected to the component, to overcome this, you can use a lambda expression:
openModal: () => {
this.modalRef = this.modalService.show(CalenderComponent, {
initialState: {
title: "Modal title"
}
});
}
this keeps the closure as you wanted, you can read more about it here
After you solve that problem, you will face another one:
To overcome this, you need to add the CalenderComponent as an entrycomponent in your app module file (app.module.ts):
entryComponents: [CalenderComponent]
Here is a forked working stackblitz

How to loop in Vue.js?

I'm trying to create a menu with vue.js, but my menu isn't showing up. Here is what I have tried:
<div id="app" class="container">
<b-menu v-for="menu in menu_items" v-bind:data="menu.menu_list" v-bind:key="menu.menu_list">
<label>{{ menu.menu_list }} </label>
<b-menu-list v-for="menu_item in menu_items.menu_list" v-bind:data="menu_item" v-bind:key="menu_item.message">
<b-menu-item > {{ menu.message }}</b-menu-item>
</b-menu-list>
</b-menu>
</div>
And here is my Vue.js code:
var App = new Vue({
el: '#app',
data: {
menu_items: [{
menu_list : 'General' [
{ message: 'First Menu' },
{ message: 'Second Menu' }
],
menu_list : 'Setting' [
{ message: 'First Setting Menu' },
{ message: 'Second Setting Menu' }
]
}]
}
})
Any solution(s)?
What i want to achieve
- General
- First Menu
- Second Menu
- Setting
- First setting Menu
- Second setting Menu
I'd suggest an alternate data structure.
menu_items: [
{
label: "General",
items: [
{
message: 'First Menu'
},
{
message: 'Second Menu'
}
]
},
{
label: "Settings",
items: [
{
message: 'First Setting Menu'
},
{
message: 'Second Setting Menu'
}
]
}
]
In the above structure, each menu is an object that has a label and a list of menu items.
Then you could build your template this way:
<b-menu v-for="menu in menu_items" v-bind:key="menu.label">
<label>{{ menu.label }}</label>
<b-menu-list v-for="item in menu.items" v-bind:key="item">
<b-menu-item> {{ item.message }}</b-menu-item>
</b-menu-list>
</b-menu>
I took out the v-bind:data properties. I'm not sure what they are for.
I'm not sure how you want to format your data, but you should probably change your data structure.
This is an example that might help you out though:
var App = new Vue({
el: '#app',
data: {
menu_items: [{
general: [
{ message: 'First Menu' },
{ message: 'Second Menu' }
],
setting: [
{ message: 'First Setting Menu' },
{ message: 'Second Setting Menu' }
]
}]
}
})
https://jsfiddle.net/5tLxdxds/
Here's my solution if you want to pass label directly from data
var App = new Vue({
el: '#app',
data: {
label1='General',
label2='Settings',
menu_items: [
{
items:[
{
message:'first menu'
},
{
message:'second menu'
}
]
label = this.label1;
},
{
items:[
{
message:'first Settings menu'
},
{
message:'second Settings menu'
}
]
label = this.label2;
},
]
}
});
Now To render it in html code :
<b-menu v-for="menu in menu_items" :key="menu.label">
<label>{{ menu.label }}</label>
<b-menu-list v-for="item in menu.items" :key="item">
<b-menu-item> {{ item.message }}</b-menu-item>
</b-menu-list>
</b-menu>
This is alternate solution if you want to pass your particular label name as data else anywhere
Hope you got perfect help!

Categories