Getting the dropdown to show based on an ID in vue - vue.js

I have 2 pages, one has a list of products and the other one has a bunch of data for that product. There is also a drop-down in the second page.
The problem I'm having is that I'm struggling to get the drop-down to have the selected product selected.
So for example, if I select product 1 from the first page then on the second page the drop-down will show product 1 and if I select product 2 then the drop-down will show product 2.
Here is my code for productIndex.vue
<template>
<div>
<div class="content-header">
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">Product Index</h3>
</div>
<div class="card-body p-0">
<table class="table table-sm table-hover">
<thead>
<tr>
<th>ID</th>
<th>Product Name</th>
<th style="width: 180px;"></th>
</tr>
</thead>
<tbody>
<tr v-for="product in products">
<td>{{ product.id }}</td>
<td>{{product.name}}</td>
<td class="text-right">
<a #click="goToProductData(product.id)" class="btn btn-xs btn-primary">
Data
</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['products'],
data() {
return {
}
},
methods: {
goToProductData(product_id){
localStorage.setItem('product', product_id);
window.location = `/product-data`;
}
},
}
</script>
and here is my productData.vue
<template>
<div>
<div class="content-header">
<div class="container">
<div class="card">
<div class="card-body">
<div class="row mb-2">
<div class="col-sm-6">
<label for="product_id_select">Product</label>
<select id="product_id_select" class="form-control select2" style="width: 100%;">
<optgroup v-for="product in selectProducts" :label="customer.text">
<option v-for="p in product.children" :value="p.id">{{ p.text }}</option>
</optgroup>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['products'],
data() {
return {
}
},
computed: {
selectProducts() {
let select2Options = [];
Object.keys(this.products).forEach(product => {
let productOptions = [];
this.products[product].forEach(p => {
productOptions.push({ id: p.id, text: p.name });
});
select2Options.push({
text: product,
children: productOptions
});
});
return select2Options;
}
}
}
</script>

You forgot to add the v-model to the select in the second page. You can find some example here.
Your code should be something like:
<select id="product_id_select" v-model="selectedProductId" class="form-control select2" style="width: 100%;">
where selectedProductId is a value in your data (or a computed property) that contains the value inserted into the local storage in the first page

Related

How to solve the avoid mutating a prop

I'm trying to compare prices from last year and this year and the problem I'm getting is this:
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value
I understand what it's saying but I'm not sure how to solve it.
Here is my code:
<template>
<div class="content-header">
<div class="container">
<div class="row">
<div class="col-sm-6">
<div class="card">
<div class="card-body">
<div class="row mb-2">
<div class="col-sm-12">
<label for="year_select">Select Year</label>
<select id="year_select" #change="getYearComparison()" class="form-control" style="width: 100%;" v-model="yearSelect">
<option v-for="year in years" :value="year.id">
{{ year.year }}
</option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-lg-12">
<div class="row">
<div class="col-lg-12">
<div class="row">
<div class="col-lg-12">
<table class="table">
<thead>
<tr class="text-center">
<th colspan="4" style="border-right: #dee2e6 solid 1px">
Previous Year
</th>
<th colspan="3" style="border-right: #dee2e6 solid 1px">
This year
</th>
<th colspan="2">
Variance
</th>
</tr>
</thead>
<tr v-for="c in compare">
<td>
{{ c.prev_price }}
</td>
<td>
{{ c.curr_price }}
</td>
<td style="border-right: #dee2e6 solid 1px">
{{ c.v_price }}
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['compare'],
data(){
return {
years: [],
tariffSelect: null
}
},
computed: {
},
methods: {
getYears(){
axios.get('/api/years/getYears').then(response => {
this.years = response.data.years;
});
},
getYearComparison(){
axios.get(`/api/product/${this.tariffSelect}/comparison`).then(response => {
this.compare = response.data.compare;
})
}
},
mounted(){
this.getYears();
}
}
</script>
compare is a prop, meaning that it's to be transferred to your component, the "child" by another component (or the Vue app), the "parent". On the other end, your child component should communicate a value change to its parent using an event.
In your parent component, you can do this:
<template>
<child-component :compare="compare" #updateCompare="compare = $event"/>
</template>
<script>
export default {
data() {
return { compare: '' }; // Put a default value here that makes sense
}
};
</script>
And in your child component, you need to emit the event, so you can replace this line:
this.compare = response.data.compare;
By this one:
this.$emit('updateCompare', response.data.compare);
You can take a look at the documentation for events.

Dynamically set v-model

I'm trying to make my v-model naming dynamic, but when I run npm run watch I get this error
You are binding v-model directly to a v-for iteration alias. This will not be able to modify the v-for source array because writing to the alias is like modifying a function local variable. Consider using an array of objects and use v-model on an object property instead
and it points to my v-model.
Here is my code
<template>
<div class="content-header">
<div class="container">
<div class="row">
<div class="col-sm-12">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-lg-12">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
<tr v-for="product in products">
<td>{{ product }}</td>
<td>
<input type="text" class="form-control" v-model="product" :name="product"> month
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default{
props: [],
data(){
return {
products: []
}
},
computed:{
},
methods: {
getProducts(){
axios.get(`/api/product/all`).then(response => {
this.products = response.data.products;
});
}
},
mounted() {
}
}
</script>

The specified value cannot be parsed, or is out of range in vue

I'm trying to create a form that will update a product's pricing when adding the unit amount of the product and
a price of the product, but when I type into one of my textboxes I end up getting this error
The specified value "unit_product_1" cannot be parsed, or is out of range
I don't know what that means or how to solve it.
Here is my code
<template>
<div class="content-header">
<div class="container">
<div class="row">
<div class="col-sm-12">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-lg-12">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Unit</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr v-for="product in products">
<td>{{ product['name'] }}</td>
<td>
<input type="text" class="form-control" v-model="product['unit']">
</td>
<td>
<input type="text" class="form-control" v-model="product['price']">
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default{
props: [],
data(){
return {
products: [],
unit_product_1: null,
price_product_1: null,
}
},
computed:{
},
methods: {
getProducts(){
axios.get(`/api/product/all`).then(response => {
this.products = response.data.products;
});
}
},
mounted() {
this. getProducts();
}
}
</script>
unit_product_1: null,
price_product_1: null
You don't use them anywhere.
If you want to mutate your incoming products, you should make a new POST request and send the data back to the server. In this scenario you must know how the server waits to receive the data and send them back as requested.

How to make only one element activate vue js?

How to make only one element activate vue js?
I have 3 drop-down lists, 3 are activated at once, how do I make sure that only one is activated?
As far as I understand, this needs to be done through a loop, but this framework is not given to me
<tr class="inputs-table">
<td>Type object: </td>
<td>
<div class="select">
<div class="select-header form-control" v-on:click="AddForm">
<span class="select__current">Please select one option</span>
<span class="select__off">х</span>
</div>
<addForm v-if="addedForm" />
</div>
</td>
</tr>
<tr class="inputs-table">
<td>Type business-model: </td>
<td>
<div class="select">
<div class="select-header form-control" v-on:click="AddForm">
<span class="select__current">Please select one option</span>
<span class="select__off">х</span>
</div>
<addForm v-if="addedForm"/>
</div>
</td>
</tr>
import addForm from './modal/addForm.vue';
export default {
name: 'Index',
data() {
return {
addedForm: false
}
},
methods: {
AddForm(){
this.addedForm = true;
},
closeForm() {
this.$parent.addedForm = false;
}
},
components: {
addForm,
}
}
with your question and the given screenshot on the comment section, it seems you implement dropdown list in your addForm component and when you click on <div class="select-header form-control" v-on:click="AddForm"> in "type object" or "Type business-model" component will expand the addForm component and your problem is when you click on one header both addForm components are visible.
In that case, there may be several methods to fix this. One easy way to add numbering to each component and only activate the addForm if number equals.
<tr class="inputs-table">
<td>Type object: </td>
<td>
<div class="select">
<div class="select-header form-control" v-on:click="AddForm(1)">
<span class="select__current">Please select one option</span>
<span class="select__off">х</span>
</div>
<addForm v-if="addedForm === 1" />
</div>
</td>
</tr>
<tr class="inputs-table">
<td>Type business-model: </td>
<td>
<div class="select">
<div class="select-header form-control" v-on:click="AddForm(2)">
<span class="select__current">Please select one option</span>
<span class="select__off">х</span>
</div>
<addForm v-if="addedForm === 2"/>
</div>
</td>
</tr>
import addForm from './modal/addForm.vue';
export default {
name: 'Index',
data() {
return {
addedForm: 0
}
},
methods: {
AddForm(number){
this.addedForm = number;
},
closeForm() {
this.$parent.addedForm = false;
}
},
components: {
addForm,
}
}
Since you are using 3 form element this would be enough but if you are planning to use a dynamic number of components it would be great if you use for a method such as create list for each type object
items: [{id:1, title: 'Type Object'}, {id:2, title: 'Business Model'}]
Then use v-for in your tr component such as,
<tr class="inputs-table" v-for="item in items" key="item.id">
<td>{{item.title}}: </td>
<td>
<div class="select">
<div class="select-header form-control" v-on:click="AddForm(item.id)">
<span class="select__current">Please select one option</span>
<span class="select__off">х</span>
</div>
<addForm v-if="addedForm === item.id"/>
</div>
</td>
</tr>

Trying to put multiple conditions inside one loop vue.js

Paid Html
<th class="plan-header blue">
<div class="pricing-plan-name">Not Free and not Recommended</div>
<div class="pricing-plan-price">
<sup>$</sup>0<span>.00</span>
</div>
<div class="pricing-plan-period">month</div>
</th>
Free Html
<th class="plan-header free">
<div class="pricing-plan-name">Free</div>
<div class="pricing-plan-price">
<sup>$</sup>0<span>.00</span>
</div>
<div class="pricing-plan-period">month</div>
</th>
Not free and Recommended Html
<th class="plan-header plan-header-standard">
<div class="inner">
<!--<span class="plan-head"> </span>-->
<span class="recommended-plan-ribbon">RECOMMENDED</span>
<div class="pricing-plan-name">STANDARD</div>
<div class="pricing-plan-price">
<sup>$</sup>34<span>.99</span>
</div>
<div class="pricing-plan-period">month</div>
</div>
</th>
Below is my code in vue.js inside for loop.
<th v-for="Record in Records" class="plan-header" :class="Record.Is_Free ? 'free':'blue'">
<div class="pricing-plan-name">{{ Record.Description }}</div>
<div class="pricing-plan-price">
<sup>$</sup>0<span>.00</span>
</div>
</th>
Question
I have to put the Recommended template also inside the condition. Can I do it inside same loop?
I meant, I have free and non-free conditions already in place.
How should I incorporate the Recommended options inside same for loop line
Right now, my code supports only Free and Paid Html part inside for loop.
or please suggest
Personally I wouldn't do this inside for loop, for many reasons, and also would store type of every option which would reflect class (no need to condition update in future, just styles) but here you are:
<th v-for="Record in Records" class="plan-header" :class="{free: Record.Is_Free, 'blue': !Record.Is_Free && !Record.Is_Recommended, 'plan-header-standard': Record.Is_Recommended}">
<span v-if="Record.Is_Recommended" class="recommended-plan-ribbon">RECOMMENDED</span>
<div class="pricing-plan-name">{{ Record.Description }}</div>
<div class="pricing-plan-price">
<sup>$</sup>{{Math.floor(price)}}<span>{{(price+"").split(".")[1]}}</span>
</div>
</th>
component Paid:
<template>
<th class="plan-header blue">
<div class="pricing-plan-name">Not Free and not Recommended</div>
<div class="pricing-plan-price">
<sup>$</sup>{{item.price}}0<span>.00</span>
</div>
<div class="pricing-plan-period">month</div>
</th>
</template>
export default{
props: ['item']
}
component Free:
<template>
<th class="plan-header free">
<div class="pricing-plan-name">Free</div>
<div class="pricing-plan-price">
<sup>$</sup>{{item.price}}0<span>.00</span>
</div>
<div class="pricing-plan-period">month</div>
</th>
</template>
export default{
props: ['item']
}
component Recommended:
<template>
<th class="plan-header plan-header-standard">
<div class="inner">
<!--<span class="plan-head"> </span>-->
<span class="recommended-plan-ribbon">RECOMMENDED</span>
<div class="pricing-plan-name">STANDARD</div>
<div class="pricing-plan-price">
<sup>$</sup>{{item.price}}34<span>.99</span>
</div>
<div class="pricing-plan-period">month</div>
</div>
</th>
</template>
export default{
props: ['item']
}
your loop code:
<template v-for="Record in Records">
<component :item="Record" :is="Record.Is_Free?'Free':Record.Is_Recommended?'Recommended':'Paid'"></component>
</template>
const Free = {
template: '#Free',
props: ['item']
},Paid = {
template: '#Paid',
props: ['item']
},Recommended = {
template: '#Recommended',
props: ['item']
}
var app = new Vue({
el: '#app',
components: {Free,Paid,Recommended},
data () {
return {
list: [{
id: 1,
text: 'free product',
isFree: true,
isRecommended: true
},{
id: 2,
text: 'Not Free and not Recommended',
isFree: false,
isRecommended: false
},{
id: 3,
text: 'Recommended',
isFree: false,
isRecommended: true
}]
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<table>
<thead>
<template v-for="item in list">
<component :item="item" :is="item.isFree?'Free':item.isRecommended?'Recommended':'Paid'"></component>
</template>
</thead>
</table>
</div>
<script type="text/x-template" id="Free">
<th class="plan-header free">
<div class="pricing-plan-name">Free</div>
<div class="pricing-plan-price">
<sup>$</sup>{{item.price}}0<span>.00</span>
</div>
<div class="pricing-plan-period">month</div>
</th>
</script>
<script type="text/x-template" id="Paid">
<th class="plan-header blue">
<div class="pricing-plan-name">Not Free and not Recommended</div>
<div class="pricing-plan-price">
<sup>$</sup>{{item.price}}0<span>.00</span>
</div>
<div class="pricing-plan-period">month</div>
</th>
</script>
<script type="text/x-template" id="Recommended">
<th class="plan-header plan-header-standard">
<div class="inner">
<!--<span class="plan-head"> </span>-->
<span class="recommended-plan-ribbon">RECOMMENDED</span>
<div class="pricing-plan-name">STANDARD</div>
<div class="pricing-plan-price">
<sup>$</sup>{{item.price}}34<span>.99</span>
</div>
<div class="pricing-plan-period">month</div>
</div>
</th>
</script>