i have this select:
<b-form-group
label-cols-sm="2"
label-cols-lg="2"
content-cols-sm
content-cols-lg="10"
v-bind:label="$t('vue.contract_terms')"
label-for="contract_terms"
v-show="contract">
<b-form-select
:options="contract_terms"
v-model="form.contract_data.contract_terms"
id="contract_terms"
:state="validate(form.contract_data.contract_terms)">
</b-form-select>
</b-form-group>
i want to show it when in my other select i choose all values except "vat" and "international_contract"
this is my js (hiring_types are the options of my other select) setSelected is my method to show my select above but i want to show it when i choose all values except "vat" and "international_contract":
data: (instance) => {
return {
contract: true,
}
},
computed:{
hiring_types () {
return [
{value: 'contract' , text: this.$t('vue.freelance')},
{value:'vat' , text: this.$t('vue.employment')},
{value: 'apprenticeship', text: this.$t('vue.apprenticeship')},
{value:'co_co_co' , text: this.$t('vue.co_co_co')},
{value: 'international_contract' , text: this.$t('vue.international_contract')}
]
},
},
methods:{
setSelected(value) {
this.hiring_contract_category = value;
this.contract = value === 'contract';
},
}
How can i do?
You can use Vue's template conditionals:
<template v-if='["vat", "international_contract"].map(val => !hiring_types.map(entry => entry.value).includes(val)).every(v => v)'>
<!-- insert b-form-group -->
</template>
Better to make this conditional a computed property and use it in your template for brevity:
// in your script
{
computed: {
showFormGroup () {
return ["vat", "international_contract"]
.map(val => !hiring_types.map(entry => entry.value)includes(val))
.every(v => v)
}
}
// in your template
<template v-if='showFormGroup'> ... </template>
Related
I would like to make select input which will be able to search items in data source. According documentation to select it seems it is not possible to do it via select component. I tried to use autocomplete component instead of it, but it does not work.
The code with both examples is here. non of them works as expected (select with search with key as v-model value)
<b-autocomplete
v-model="company.country"
:data="selectCountries"
:field="value"
:keep-first="keepFirst"
placeholder="Select country"
:open-on-focus="openOnFocus"
<b-field label="Find a name">
#select="option => (selected = option)"
:clearable="clearable"
>
</b-autocomplete>
</b-field>
<b-select v-model="company.location" expanded>
<option v-for="option in selectCountries"
:value="option.key"
:key="option.key">
{{ option.value }}
</option>
</b-select>
</field>
...
data() {
return {
selectCountries: [
{"value": "slovakia", "key": "SK"}, {"value": "England", "key": "EN"}
],
}
}
In order for the autocomplete to work, you need to filter the list yourself and pass the results back to the component. To do this, we created a computed property like this:
<template>
<section>
<b-autocomplete v-model="country" :data="filteredCountries" field="name" :keep-first="true" placeholder="Select Country" :open-on-focus="true"></b-autocomplete>
</section>
</template>
<script type="text/javascript">
export default {
data: () => ({
country: '',
countries: [
{
name: 'Slovakia',
key: 'SK'
},
{
name: 'England',
key: 'EN'
},
{
name: 'United States of America',
key: 'USA'
}
]
}),
computed: {
filteredCountries () {
if (this.country !== null) {
return this.countries.filter((option) => {
return option.name.toString().toLowerCase().indexOf(this.country.toLowerCase()) >= 0
})
} else {
return this.countries
}
}
}
}
</script>
I'm trying to test the following label using Jest:
<label class="no-content-label" v-if="products.length===0 && !isTableBusy">No Content To Show</label>
with the following test:
it('Test No Content To Show When table is not empty', async () => {
const wrapper = mount(productTable, {
propsData: {
products: items1,
fields: fields1
}
})
expect(wrapper.find(".no-content-label").exists()).toBeFalsy()
wrapper.destroy();
})
where items are just: [{ a: 1, b: 2, c: 3 }, { a: 4, b: 5, c: 6 }] nut I always getting True even that the components is not showing and should get false.
The full components code:
<template>
<div id="ProductTable" class="container mt-3 mb-5">
<h1 class="mainTitle">Product Table Home</h1>
<b-alert variant="danger" :show="showError" v-for="(error,index) in errorArray" :key="index" dismissible>
{{ error }}
</b-alert>
<b-row class="mb-3">
<b-col md="3">
<b-form-input v-model="filterInput" type="search" id="filterInput" placeholder="Type to Search"></b-form-input>
</b-col>
</b-row>
<b-table id="product-table" striped hover head-variant="light"
:busy="isTableBusy"
:items="products"
:filter="filterInput"
:fields="fields"
:bordered="bordered"
:borderless="bordered"
:total-rows="rows">
<template #table-busy>
<div class="text-center text-danger my-2">
<b-spinner class="align-middle"></b-spinner>
<strong>Loading...</strong>
</div>
</template>
<template #cell(cogs.unitManufacturingCost)="row">
{{
row.item.cogs.unitManufacturingCost ? Number(row.item.cogs.unitManufacturingCost).toLocaleString() + '$' : '0$'
}}
</template>
<template #cell(cogs.shipmentUnitCost)="row">
{{ row.item.cogs.shipmentUnitCost ? Number(row.item.cogs.shipmentUnitCost).toLocaleString() + '$' : '0$' }}
</template>
<template #cell(cogs.monthlyAdvertismentCost)="row">
{{
row.item.cogs.monthlyAdvertismentCost ? Number(row.item.cogs.monthlyAdvertismentCost).toLocaleString() + '$' : '0$'
}}
</template>
<template #cell(cogs.manufacturingCountry)="row">
{{ row.item.cogs.manufacturingCountry ? row.item.cogs.manufacturingCountry : '-' }}
</template>
<template #cell(actions)="row">
<b-button size="md" variant="primary" #click="openEditProductModal(row.item)">
Edit
</b-button>
<edit-product-modal ref="EditProductModal" :item="row.item" #fetchProducts="fetchProducts"></edit-product-modal>
</template>
</b-table>
<label class="no-content-label" v-if="products.length===0 && !isTableBusy">No Content To Show</label>
</div>
</template>
<script>
import EditProductModal from "#/components/EditProductModal";
import {BAlert} from "bootstrap-vue";
import {BRow} from "bootstrap-vue";
import {BCol} from "bootstrap-vue";
import {BFormInput} from "bootstrap-vue";
import {BTable} from "bootstrap-vue";
export default {
name: "BootstrapProductTable",
components: {
EditProductModal: EditProductModal,
'b-alert': BAlert,
'b-row': BRow,
'b-col': BCol,
'b-form-input': BFormInput,
'b-table': BTable,
},
data() {
return {
filterInput: "",
bordered: true,
isTableBusy: false,
products: [],
fields: [
{key: 'productName', label: 'Product Name', sortable: true, sortDirection: 'desc'},
{key: 'cogs.unitManufacturingCost', label: 'Unit manufacturing cost', sortable: true,},
{key: 'cogs.shipmentUnitCost', label: 'Shipment unit cost', sortable: true},
{key: 'cogs.monthlyAdvertismentCost', label: 'Monthly advertising cost', sortable: true,},
{key: 'cogs.manufacturingCountry', label: 'Manufacturing country', sortable: true,},
{key: 'actions', label: 'Actions'}
],
errorArray: [],
showError: false
}
},
computed: {
rows() {
return this.products.length;
}
},
created() {
this.fetchProducts();
},
methods: {
openEditProductModal(row) {
this.$refs.EditProductModal.show(row)
},
async fetchProducts() {
try {
this.isTableBusy = true;
//delay example
// const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
// await delay(1000)
let response;
response = await this.axios.get(
"http://localhost:3000/products"
);
this.products = response.data;
} catch (error) {
this.showError = true;
this.errorArray = error.response ? error.response.data.errors : [error]
}
this.isTableBusy = false;
}
}
}
</script>
You are trying to pass products, but your component is not expecting any props.
I see that products are fetched in fetchProducts method. All api calls must be mocked. fields are already set in the component. So all you have to do is to mock api call. That's why I suggest you to mock fetchProducts method so it will be always returning the same array of products:
const mockProducts = [{ a: 1, b: 2, c: 3 }, { a: 4, b: 5, c: 6 }];
it('Test No Content To Show When table is not empty', async () => {
const wrapper = mount(productTable, {
mocks: {
fetchProducts: () => mockProducts
}
})
expect(wrapper.find(".no-content-label").exists()).toBeFalsy()
wrapper.destroy();
})
I was finally been able to fix the test by adding an id attribute to the label and finding it by id:
the label:
<label id="no-content-label" v-if="products.length===0 && !isTableBusy">No Content To Show</label>
The working tests:
let wrapper = null
beforeEach(() => {
wrapper = mount(ProductTable)
})
afterEach(() => {
wrapper.destroy()
})
it('Test "No Content To" Show appear When Products are empty', async () => {
await wrapper.setData({products: []})
expect(wrapper.find('[id="no-content-label"]').exists()).toBe(true)
expect(wrapper.find('[id="no-content-label"]').text()).toBe("No Content To Show");
})
it('Test "No Content To" not Show do not appear When Products are not empty', async () => {
await wrapper.setData({products: mockProduct})
expect(wrapper.find('[id="no-content-label"]').exists()).toBe(false);
})
I have a simple application that uses a v-for in a select statement that generates two select tags. The groupedSKUAttributes variable that creates the select statement looks like this:
groupedSKUAttributes = {colour: [{id: 1, name: 'colour', value: 'red'},
{id: 2, name: 'colour', value: 'blue'}],
size: [{id: 3, name: 'size', value: '40'},
{id: 4, name: 'size', value: '42'}]}
I also have a button that I want to clear the select fields. How do I get the clear method to make each of the select fields choose their default <option value='null' selected>select a {{ attributeName }}</option> value? I can't figure out if I'm meant to use a v-model here for the groupedSKUAttributes. Any advice would be appreciated.
The template looks like this:
<template>
<div>
<select
v-for='(attribute, attributeName) in groupedSKUAttributes'
:key='attribute'
#change='update(attributeName, $event.target.value)'>
<option value='null' selected>select a {{ attributeName }}</option>
<option
v-for='a in attribute'
:value='a.id'
:label='a.value'
:key='a.id'>
</option>
</select>
</div>
<button #click='clear'>clear</button>
</template>
And the JS script looks like this:
<script>
export default {
name: 'app',
data() {
return {
groupedSKUAttributes: null,
}
},
methods: {
clear() {
console.log('clear');
},
update(attributeName, attributeValue) {
console.log(attributeName, attributeValue);
},
getSKUAttributes() {
API
.get('/sku_attribute/get')
.then((res) => {
this.skuAttributes = res.data;
this.groupedSKUAttributes = this.groupBy(this.skuAttributes, 'name');
})
.catch((error) => {
console.error(error);
});
},
},
created() {
this.getSKUAttributes();
}
}
</script>
The v-model directive works within the v-for without any issues.
<script>
export default {
name: 'app',
data() {
return {
groupedSKUAttributes: null,
selected: {}
}
},
methods: {
clear() {
this.generateDefaultSelected(this.generateDefaultSelected);
},
update(attributeName, attributeValue) {
this.selected[attributeName] = attributeValue;
},
getSKUAttributes() {
API
.get('/sku_attribute/get')
.then((res) => {
this.skuAttributes = res.data;
this.groupedSKUAttributes = this.groupBy(this.skuAttributes, 'name');
// Call this method to reset v-model
this.generateDefaultSelected(this.groupedSKUAttributes);
})
.catch((error) => {
console.error(error);
});
},
generateDefaultSelected(groupedSKUAttributes) {
// Reset the object that maintains the v-model reference;
this.selected = {};
Object.keys(groupedSKUAttributes).forEach((name) => {
// Or, set it to the default value, you need to select
this.selected[name] = '';
});
}
},
created() {
this.getSKUAttributes();
}
}
</script>
In the above code, generateDefaultSelected method resets the selected object that maintains the v-model for all your selects.
In the template, you can use v-model or unidirectional value/#change pair:
<!-- Using v-model -->
<select
v-for='(attribute, attributeName) in groupedSKUAttributes'
:key='attributeName' v-model="selected[attributeName]">
<!-- Unidirection flow without v-model -->
<select
v-for='(attribute, attributeName) in groupedSKUAttributes'
:key='attributeName' :value="selected[attributeName]"
#change='update(attributeName, $event.target.value)'>
I am trying to pass a function into my component and I keep getting this error back. "Invalid prop: type check failed for prop "form_type". Expected Array, got Function." My function returns an array so I am a little lost on how to fix this.
The function I am referencing is selectedType & the component in question is ChildTab
<template>
<div class="row">
<q-field
label="Contact Type"
:labelWidth="3"
error-label="Please select a contact type"
:error="!!failed_validations.contact_type"
>
<q-select v-model="contact_type" :options="contact_types"/>
</q-field>
</div>
<ChildTabs
:form_type="selectedType"
/>
<q-field class="float-right">
<q-btn color="faded" v-on:click="goBack()">Cancel</q-btn>
<q-btn color="green-6" v-on:click="selectedType()">Submit</q-btn>
</q-field>
</div>
</div>
</template>
<script>
'use strict';
import ChildTabs from '../tabs';
export default {
name: 'contacts-view',
data: function () {
return {
contact_type: '',
contact_types: [
{
label: 'Pregnancy',
value: 'pregnancy',
form_type: [
'BreastFeeding',
'Pregnancy'
]
},
{
label: 'Post Partum (Includes Birth)',
value: 'postpartum',
form_type: [
'Birth',
'BreastFeeding',
'PostPartum'
]
},
{
label: '1 - 2 Month',
value: '1_2_months',
form_type: [
'BreastFeeding',
'DuoMonths'
]
},
{
label: '6 Month',
value: '6_months',
form_type: [
'SixMonth'
]
}
],
}
},
props: {
},
computed: {
selectedType: function ()
{
var values = this.contact_types.map(function(o) { return o.value });
var index = values.indexOf(this.contact_type);
this.selectedForms = this.contact_types[index].form_type
// console.log(this.selectedForms);
return this.selectedForms;
}
},
methods: {
},
created: function () {
this.selectedType();
},
components: {
ChildTabs
}
}
</script>
As you try to call selectedType on click "Submit", maybe you should call it as a method.
Inside selectedType you bind a selectedForms property. Why don't you just initialize this property inside data as an empty array and pass it as a props of your ChildTabs component ?
<template>
<div class="row">
<ChildTabs :form_type="selectedForms" />
</div>
</template>
export default {
name: 'contacts-view',
data: function () {
return {
selectedForms: [],
// ...
}
},
methods: {
selectedType() {
var values = this.contact_types.map(function(o) { return o.value });
var index = values.indexOf(this.contact_type);
this.selectedForms = this.contact_types[index].form_type
}
},
//...
}
Fiddle example
What you bind as a prop in a component goes as same in the component. So as you're referencing selectedType in your ChildTabs component - the method selectedType will be received by ChildTabs as a prop. So either you edit your propType in ChildTabs component and invoke that passed method as needed or you call the selectedType method on the fly when passed in as a prop like
<ChildTabs :form_type="selectedType()" />
This will call that method then and will bind the resulting array as prop
I am creating an application using Quasar and VueJS. I am able to generate a dynamic form on click of the add button, but not able to delete any of the newly generated form based on the click of the delete button.Find the code below:
<template>
<div v-for="h in htmlList">
<div v-for="r in h" >
<div v-html="r" v-on:click="useRemoveFromProject(1)" v-bind:id="r.id">
</div>
</div>
</div>
</template>
<script>
/*
* Root component
*/
import Vue from 'vue'
export default {
name: 'q-app',
data () {
return {
flag: 0,
htmlList: [],
select: 'fb',
select1: 'fb1',
multipleSelect: ['goog', 'twtr'],
usersInProject: [],
selectOptions: [
{
label: 'Google',
value: 'goog'
},
{
label: 'Select',
value: 'fb'
},
{
label: 'Twitter',
value: 'twtr'
},
{
label: 'Apple Inc.',
value: 'appl'
},
{
label: 'Oracle',
value: 'ora'
}
],
selectOptions1: [
{
label: 'Integer',
value: 'goog1'
},
{
label: 'Float',
value: 'fb1'
},
{
label: 'String',
value: 'twtr1'
}
]
}
},
methods: {
useRemoveFromProject: function (id) {
console.log('hi....')
Vue.delete(this.htmlList, id)
},
identifyMe: function (event) {
alert('hi - ' + event.target.id)
},
process: function () {
this.flag += 1
let temp = []
temp.push('<div class="card" id="a_' + this.flag + '"> <div class="card-content content-center "> <large id="l4">Expression LHS:</large> <input><br> <large id="l5">Operators:</large> <q-select type="radio" v-model="this.select" :options="this.selectOptions"></q-select><br><large id="l4">Expression RHS:</large> <input><br><large id="l5">Data type:</large> <q-select type="radio" v-model="select1" :options="selectOptions1"></q-select><br></div><button class="cordova-hide circular red " style="margin-bottom:5px; margin-right:30px;" v-on:click="userRemoveFromProject(i)"><i>delete</i></button><input value="click" type="button"> </div>')
let ids = ['a_' + this.flag]
console.log(ids)
this.htmlList.push(temp)
}
}
}
</script>
After looking to your code i noticed that you have some errors:
Call function useRemoveFromProject without the 'r' of 'user'
Call userRemoveFromProject when clicking on the element and not only the delete button
Call userRemoveFromProject(i) with a 'i' variable, but what is 'i' ?
Why using a double v-for? The first level is enough.
I propose to you a working example on a fiddle. Please let me know if it's useful for you (and mark it as resolve if it's the case).
EDIT: for Vue.js 2 https://jsfiddle.net/86216oko/