So In my application I am trying to load a quiz and its data from an API, which works perfectly fine. Then I try to reperesent this data in the UI, which works partially fine. There are some very weird things going on which seem like magic to me.
The first example is when I am trying to load the quiz and its questions in my main page: QuizEditor.vue. This page and its loading look like this:
export default {
components: {QuestionCard, EditorGeneralBar, EditorQuestionBar},
name: 'QuizEditor',
data() {
return {
count: 1,
editorTitle: "General",
newQuiz: true,
newQuestion: true,
currentQuestion: null,
quiz: {
tn: "",
title: "My quiz",
description: "A quiz",
timeLimit: 60,
theme: 1
},
options: {
multipleDropzonesItemsDraggingEnabled: false,
dropzoneSelector: ".q-list",
draggableSelector: ".question-card"
},
currentPosition: 1,
questions: []
}
},
mounted() {
var QE = this;
QuizTemplateRepository.getTemplate("k472ahe3r", this.$store.state.X_CSRFToken).then(function (res) {
QE.quiz = {
tn: "k472ahe3r",
title: res.data.template.label,
description: res.data.template.description,
};
QuizTemplateRepository.getTemplateContent(QE.quiz.tn, QE.$store.state.X_CSRFToken).then(function (res) {
var questions = JSON.parse(res.data.content.content).questions;
QE.quiz.theme = JSON.parse(res.data.content.content).properties.theme;
QE.quiz.timeLimit = JSON.parse(res.data.content.content).properties.timeLimit;
QE.quiz.questions = questions;
questions.forEach(question => {
QuestionRepository.getQuestion(question, QE.$store.state.X_CSRFToken).then(function (resQuest) {
var vogh = resQuest.data.var[0].vogh;
AnswerRepository.getAnswer(vogh, QE.$store.state.X_CSRFToken).then(function(resAnswer) {
var quest = {
name: resQuest.data.var[0].name,
hash: resQuest.data.var[0].vh,
vogh: resQuest.data.var[0].vogh,
label: resQuest.data.var[0].label,
position: resQuest.data.var[0].position,
description: "",
answers: []
}
resAnswer.data.varoptiongroup.forEach(answer => {
answer.place = QE.getPositionString(answer.position);
if(answer.value > 0)
answer.isCorrect = true;
else
answer.isCorrect = false;
quest.answers.push(answer);
});
QE.questions.push(quest);
QE.currentPosition++;
});
})
});
});
});
},
...
}
The data above from the API calls load just fine and I have quadrupple checked this by putting console logs on practically every line. The first issue comes from EditorGeneralBar, this will have the quiz object which it will receive like this:
<EditorGeneralBar :quiz="quiz" v-if="editorTitle === 'General'" #submitQuiz="submitQuiz" #themeSelected="selectTheme"></EditorGeneralBar>
This component looks like this:
<template>
<div class="bar-content">
<q-form
#submit="submit"
class="q-gutter-md"
>
<q-input
filled
v-model="quiz.title"
label="Title"
lazy-rules
:rules="[ val => val && val.length > 0 || 'Please type something']"
/>
<q-input
filled
type="text"
v-model="quiz.description"
label="Description"
lazy-rules
:rules="[ val => val && val.length > 0 || 'Please type something']"
/>
<q-input
filled
type="number"
v-model="quiz.timeLimit"
label="Time limit"
lazy-rules
:rules="[ val => val && val.length > 0 || 'Please type something']"
/>
<q-file filled bottom-slots v-model="quiz.thumbnail" label="Thumbnail">
<template v-slot:before>
<q-icon name="folder_open" />
</template>
<template v-slot:hint>
A thumbnail for the quiz.
</template>
<template v-slot:append>
<q-btn round dense flat icon="add" #click.stop />
</template>
</q-file>
<p>Themes</p>
<div class="theme-list">
<div class="theme-1 theme-preview" v-on:click="selectTheme(1)"></div>
<div class="theme-2 theme-preview" v-on:click="selectTheme(2)"></div>
<div class="theme-3 theme-preview" v-on:click="selectTheme(3)"></div>
<div class="theme-4 theme-preview" v-on:click="selectTheme(4)"></div>
<div class="theme-5 theme-preview" v-on:click="selectTheme(5)"></div>
</div>
<div>
<q-btn label="Save" type="submit" color="primary"/>
</div>
</q-form>
</div>
</template>
<script>
export default {
name: 'EditorGeneralBar',
props: ["quiz"],
components: [
'QBtn'
],
methods: {
submit:function() {
this.$emit("submitQuiz");
},
selectTheme:function(theme) {
this.$emit("themeSelected", theme);
}
}
}
</script>
Everything in this component gets filled just fine, but not the timeLimit, this only gets loaded when I edit something like the description or title.
This also happens on the QuizEditor.vue where I try to load a class based on the template's theme.
This will be done like below(thase are placed in methods):
/**
* Select a theme by its number.
* #param {Number} theme
*/
selectTheme: function(theme) {
this.quiz.theme = theme;
console.log(this.quiz.theme);
},
/**
* Get the selected theme of the quiz.
*/
getTheme: function() {
return "theme-"+this.quiz.theme;
},
and the html looks like this:
<div class="question-listing" v-bind:class="getTheme()">
...
</div>
Summary
So to summarize it all, each component only loads data partially. The full data is there when I load it from the API and the data which is not being shown in the UI comes when I manipulate something in that component.
Can somebody tell me what is going wrong and how I can load ALL of the data correctly into my UI.
Related
I am trying to make a drop down "select" field disabled under certain conditions in my app.
I have successfully done this with buttons already using a prop (disableBtn) that i pass up from the root of my app to the button component I made.
I try to do the EXACT same thing on a select component and it refuses to pass the prop (disableOption) back into the child component, even though its passing back many other binded props that work fine and build the options out in the drop down correctly.
I am logging the values on screen right now and can see they are updating in the main app component, but its not passing that back to the child for some reason.
Where am I off here? My understanding is you store the values you want to change in data() in the app.vue, then create a prop for them in the child component and bind them in the HTML. This has been working fine in all my other use cases.
app.vue
<template>
<div class="container">
<img alt="logo" src="./assets/logo.png">
<Header title="Provider Manager" :custTZ="custTZ" :shippingState="shippingState" :patientID="patientID" :custName="custFullName" :custEmail="custEmail" :custPhone="custPhone" />
<div v-if="providerVisibility" class="providerInfo">
<Button #btn-click="providerPicked" id="first-avail" text="First Available" />
<br />
<Select #dd-select="providerPickedSelect" :disabled="disableOption" :selectName="selectName" :id="id" :eligibleProviders="eligibleProviders" :labelFor="labelFor" :labelText="labelText" />
{{ disableOption }}
</div>
<div v-if="providerSelected" >
<hr>
<br />
<h2>Provider: {{ chosenProvider }} </h2>
<br />
</div>
<div v-if="providerSelected" >
<BookingSlots #selectSlot="removeUnselected" :slots="slots" />
<br />
<Button #btn-click="bookMeeting" text="Confirm Request" />
</div>
</div>
</template>
<script>
import { ZOHO } from "./assets/ZohoEmbededAppSDK.min.js";
import Header from './components/Header'
import Button from './components/Button'
import BookingSlots from './components/BookingSlots'
import Select from './components/Select'
const axios = require('axios');
export default {
name: 'App',
components: {
Header,
Button,
BookingSlots,
Select
},
data() {
return{
slots: [],
providerVisibility: true,
providerSelected: false,
currentProvider: 'None Assigned',
chosenProvider: '',
custFullName: '',
custEmail: '',
custPhone: '',
shippingState: '',
patientID: '',
custTZ: '',
providerZID: '',
eligibleProviders: [],
disableBtn: false,
disableOption: true,
}
},
methods: {
removeUnselected(id) {
console.log('id', id)
this.slots = this.slots.filter((slot) => slot.id === id)
},
providerPicked(id) {
console.log("id" + id)
console.log("currentProvider",this.currentProvider)
//Toggle front end visibility
this.providerSelected = true;
this.providerVisibility = false;
if(id === "first-avail"){
// hit booking engine, get name of first available
console.log("FIRST AVAIL")
this.chosenProvider = "Need to Hit Booking App";
}
if(id === "current-provider"){
// hit zoho values and get contact assigned provider
console.log("CURRENT PROVIDER")
this.chosenProvider = this.currentProvider;
}
},
providerPickedSelect(id, selectValue) {
if(this.id === "provider-select"){
// Get values from our DB for the provider selected
console.log("Provider-Select")
this.providerSelected = true;
this.providerVisibility = false;
this.chosenProvider = selectValue;
}
},
bookMeeting() {
//Book the meeting
console.log("book meeting called")
}
},
created() {
//Hit zoho and get customer info back
ZOHO.embeddedApp.on("PageLoad",(data) =>
{
console.log(data);
//Custom Business logic goes here
let entity = data.Entity;
let recordID = data.EntityId[0];
ZOHO.CRM.API.getRecord({Entity:entity,RecordID:recordID})
.then((data) => {
console.log(data.data[0])
// Set values scraped from CRM Contact profile
if(data.data[0].provider !== null && data.data[0].provider !== "None Assigned" ){
this.currentProvider = data.data[0].provider.name;
this.providerZID = data.data[0].provider.id;
}else{
//need to disable button if no doc assigned
this.disableBtn = true;
}
this.custEmail = data.data[0].Email;
this.custFullName = data.data[0].Full_Name;
this.custPhone = data.data[0].Phone;
this.patientID = data.data[0].Patient_ID;
this.shippingState = data.data[0].Mailing_State;
this.custTZ = data.data[0].GTM;
// getEligibleProviders(this.shippingState);
var data = JSON.stringify({
"state":this.shippingState,
});
axios(config)
.then((res) => {
console.log(res.data)
//this.eligibleProviders = res.data;
if(this.eligibleProviders && !this.eligibleProviders.length){
console.log("empty array")
this.eligibleProviders = [{
first_name: "None Avilable in Svc. State",
last_name: ""
}
];
this.disableOption = true;
}else{
console.log("full array")
}
console.log(this.eligibleProviders)
})
.catch((e) => {
console.log(e);
});
});
});
ZOHO.embeddedApp.init();
this.slots = [
{
id: 1,
text: 'Time Slot 1',
providerFname: 'James',
providerLname: "Appleton"
},
{
id: 2,
text: 'Time Slot 2',
providerFname: 'James',
providerLname: "Johnson"
}
];
this.selectName = "provider-select";
this.id = "provider-select";
this.labelFor = "provider-select";
this.labelText = "Choose a Provider: ";
}
}
</script>
select.vue
<template>
<br />
<label :for="labelFor">{{ labelText }} {{ disableOption }}</label>
<select v-on:change="onSelect($event, id)" class="select" :name="selectName" :id="id" :disabled="disableOption" >
<option :value="'none'" selected disabled hidden >Select One</option>
<option :key="provider.id" v-for="provider in eligibleProviders" :value="provider.first_name + ' ' + provider.last_name" >{{ provider.first_name +" "+ provider.last_name }}</option>
</select>
<br /><br />
</template>
<script>
export default {
name: 'Select',
props: {
selectValue: String,
selectName: String,
id: String,
labelFor: String,
labelText: String,
eligibleProviders: Array,
disableOption: Boolean,
},
methods: {
onSelect($event, id) {
console.log($event.target.value)
this.$emit('dd-select', id, $event.target.value);
}
},
emits: ['dd-select']
}
</script>
button.vue
<template>
<button #click="onClick(id)" class="btn" :id="id" :disabled="disableBtn" >{{ text }}</button>
</template>
<script>
export default {
name: 'Button',
props: {
text: String,
id: String,
disableBtn: Boolean,
},
methods: {
onClick(id) {
this.$emit('btn-click', id);
}
}
}
</script>
in select.vue, the props says it wants "disableOption", but you're passing disabled="disableOption"
so you can try updating app.vue with:
<Select
#dd-select="providerPickedSelect"
:disable-option="disableOption"
:select-name="selectName"
:id="id"
:eligible-providers="eligibleProviders"
:label-for="labelFor"
:label-text="labelText"
/>
I'm having problems doing a form validation. I have a start date and an end date. I need to confirm that the end date is greater than the start date.
I'm using vuejs and vuelidate.
I'm working with an Input.vue component, which has a standard input being reused in other forms. I also have a Form.vue component that has Input.vue inside. And this Form.vue component is called both in the modal of creating a new event and in the editing mode.
My problem is in the validation of when the event is edited. If you select an end date less than the start date, it doesn't allow saving the information, that's ok. However, when trying to correct it by selecting a date greater than the start date, and clicking on save again, it seems that it is no longer calling validation. With this, the error that the end date must be greater than the initial one does not disappear. I did several tests and noticed that validation is not called after the error occurs when editing. I do not know why.
component Input.vue:
<template>
<b-form-group :label="label" :label-for="labelFor">
<b-form-input
:id="labelFor"
v-model="value"
:type="type"
:placeholder="placeholder"
:class="{ 'is-invalid': this.isInvalid }"
required>
</b-form-input>
<slot></slot>
</b-form-group>
</template>
<script>
export default {
name: 'InputForm',
props: [ 'label', 'labelFor', 'vModel', 'type', 'placeholder', 'isInvalid' ],
computed: {
value: {
get() {
return this.vModel
},
set(value) {
this.newValue = value
return this.$emit('emitValue', this.newValue)
}
}
},
}
</script>
component Form.vue:
<template>
<b-form>
....
<InputForm
class="col-8 pl-0"
:label="'*Início'"
:labelFor="'event_start_at_date'"
:vModel="event.start_at_date"
:type="'date'"
#emitValue="($event) => event.start_at_date = $event"
/>
<InputForm
class="col-4 pr-0"
v-if="!event.all_day"
:labelFor="'event_start_at_time'"
:vModel="event.start_at_time"
:type="'text'"
#emitValue="($event) => event.start_at_time = $event"
/>
</b-col>
<b-col cols="12" lg="6" class="d-flex align-items-end">
<InputForm
class="col-8 pl-0"
:label="'*Fim'"
:labelFor="'event_end_at_date'"
:type="'date'"
:vModel="event.end_at_date"
#emitValue="($event) => event.end_at_date = $event"
:isInvalid="invalid.end_at_date.$error"
>
<b-form-invalid-feedback v-if="this.submitted" :state="invalid.end_at_date.$error == 0">Fim deve ser maior que início.</b-form-invalid-feedback>
</InputForm>
<InputForm
class="col-4 pr-0"
v-if="!event.all_day"
:labelFor="'event_end_at_time'"
:vModel="event.end_at_time"
:type="'text'"
:isInvalid="invalid.end_at_date.$error"
:class="{ 'error' : invalid.end_at_date.$error }"
#emitValue="($event) => event.end_at_time = $event"
/>
...
</b-form>
</template>
export default {
name: 'Form',
props: [ 'event', 'event_shared', 'error', 'submitted' ],
components: { Input },
computed: {
invalid() {
if(this.error.edit_event != undefined) {
return this.error.edit_event
} else if(this.error.event != undefined) {
return this.error.event
} else {
return false;
}
},
stateInvalid() {
if(this.error.edit_event != undefined) {
return this.error.edit_event
} else if(this.error.event != undefined) {
return this.error.event
} else {
return 0;
}
},
},
watch: {
event() {
let newEvent = this.event
this.$emit('emitObj', newEvent)
},
},
}
</script>
component ModalEditEvent.vue:
...
<VWarningErrorForm v-if="$v.$error" />
<FormEvent
:event="edit_event"
:event_shared="get_shared_users_id"
:error="this.$v"
:submitted="this.submitted"
#emitObj="($event) => edit_event = $event"
/>
...
<b-button
v-if="editEvent && !view_popover_save"
variant="primary" type="submit" #click="save()">Gravar</b-button>
...
<script>
import moment from 'moment';
import { required } from "vuelidate/lib/validators";
import isAfterDate from '#/functions/isAfterDate'
...
validations: {
edit_event: {
name: {
required
},
end_at_date: {
required: function(value) {
return isAfterDate(value, this.edit_event);
}
},
},
},
...
save() {
this.submitted = true;
this.$v.$touch();
if(!this.$v.$error) {
....
}
},
validation
import moment from 'moment';
function isAfterDate(value, vm) {
let hour = function(att) {
return `${att[0]}${att[1]}`;
}
let minute = function(att) {
return `${att[3]}${att[4]}`;
}
let start = moment(vm.start_at_date).set({hour: hour(vm.start_at_time), minute: minute(vm.start_at_time) })._d
let end = moment(vm.end_at_date).set({hour: hour(vm.end_at_time), minute: minute(vm.end_at_time) })._d
return end >= start;
}
export default isAfterDate;
Solved my issue with Vue.set. I realized that I was trying to detect the change of a property that I added later myself. So I had to use Vue.set when adding the property, so I could detect the change later, and then correctly validate the form. Thanks to whoever tried to help me!
I have a div in which I am displaying components from array of objects. Above the div I have an input[type=text] which will be filtering the displayed data depending what I will insert. It looks like that:
<div class="filters">
<input type="search" placeholder="Szukaj specjalisty"
:onchange="filterAllSpecialists">
<FilterData/>
</div>
<div class="all-fields">
<SpecialistPreview class="specialist-preview"
v-for="(talk, index) in newTalks"
:key="index"
:name="talk.name"
:speciality="talk.speciality"
:hourly-price="talk.hourlyPrice"
:half-hour-price="talk.halfHourPrice"
:avatar="talk.avatar"
:rating="talk.rating"
:is-available="talk.isAvailable"
>{{ talk }}
</SpecialistPreview>
</div>
I have created a method that takes the value of input and compares with the name from newTalks array:
<script>
export default {
data() {
return {
talks: [
{
name: 'Małgorzata Karwacka-Lewandowska',
speciality: 'Coach',
hourlyPrice: 150,
halfHourPrice: 80,
avatar: 'patrycja.jpg',
rating: 2.5,
isAvailable: true
},
... more objects
],
newTalks: []
}
},
methods: {
filterAllSpecialists(e) {
let input = e.target.value;
if (input !== '') {
this.newTalks = this.talks.filter(el =>
el.name.toLowerCase().includes(input.toLowerCase()))
console.log(this.newTalks);
return this.newTalks;
} else {
return this.newTalks;
}
}
},
mounted() {
this.newTalks = this.talks;
}
}
All I get is Uncaught SyntaxError: Function statements require a function name in the console
Since nobody answered and I have spent more time figuring out what was wrong with my code I found out that the first mistake I did was instead of using # I used : before onchange (I decided that for my purpose I needed #keyup instead of #change). Also there was an easier way of targeting the input value - simply by adding a v-model='allSpecialistInput.
So now in the template it should look like that:
<div class="filters">
<input type="search" placeholder="Szukaj specjalisty" v-model="allSpecialistsInput"
#keyup="filterAllSpecialists">
<FilterData/>
</div>
<div class="all-fields" v-cloak>
<SpecialistPreview class="specialist-preview"
v-for="(coach, index) in newCoaches"
:key="index"
:name="coach.name"
:speciality="coach.speciality"
:hourly-price="coach.hourlyPrice"
:half-hour-price="coach.halfHourPrice"
:avatar="coach.avatar"
:rating="coach.rating"
:is-available="coach.isAvailable"
>{{ coach }}
</SpecialistPreview>
</div>
In the methods all I needed to change was the first line in the filterAllSpecialists function like that:
filterAllSpecialists() {
let input = this.allSpecialistsInput; <--- here
if (input !== '') {
this.newCoaches = this.coaches.filter(el => el.name.includes(input.toLowerCase()))
return this.newCoaches;
} else {
return this.newCoaches;
}
},
I have a group of fields that are generated in a loop an indefinite number of times. Basically 2 input fields, a label, and a delete button. Then another button that says Add More.
Every time they hit that, one more set of the above-mentioned appears. They can do this as many times as they want. It looks like this. (I'm using quasar, hence why the q-"" elements)
// Click more button
<q-btn #click="onAddBarcodes" icon="add" no-caps color="primary">Add More</q-btn>
// Form
<div v-for="(barcode, index) in barcodes" :key="index">
<div class="row q-pr-lg items-center">
<div class="col-3">
<label class="text-weight-medium">Starting Roll #:</label>
<q-input v-model="barcode.start" :id="barcode.id"></q-input>
</div>
<div class="col-3">
<label class="text-weight-medium">Ending Roll #:</label>
<q-input v-model="barcode.end" :id="barcode.id"></q-input>
</div>
<div class="col-5">
<label class="text-weight-medium">Agency Name:</label>
<div v-if="barcode.agencyName">
{{ barcode.agencyName }}
</div>
</div>
<div class="col-1">
<q-btn #click="onDeleteBarcodes(barcode.id)" icon="close"/>
</div>
</div>
</div>
export default {
data() {
return {
barcodes: []
}
},
methods: {
onAddBarcodes() {
const newBarcode = {
id: Math.random() * Math.random() * 1000,
start: "",
end: "",
agencyName: ""
};
this.barcodes.push(newBarcode);
},
onDeleteBarcodes(id) {
this.barcodes = this.barcodes.filter(barcode => barcode.id !== id);
},
}
}
Now this works at its most basic form. What I want to do is to load the component with a set number of iterations already started. For example, 5 of them.
The Add More button will add the 6th on forward if they need more. The delete button should delete down to zero if they wanted to (not that they would) but it should definitely delete down to 1 at least.
How can I start the v-for at a specific number of iteration?
You can add 5 items to barcodes on created() vue instance like:
created() {
[...Array(5).keys()].forEach(() => this.onAddBarcodes())
}
You can add mounted hook and initialize collection with 5 elements here
export default {
data() {
return {
barcodes: []
}
},
mounted() {
this.barcodes = Array(5).fill(0).map(pr => ({
id: Math.random() * Math.random() * 1000,
start: "",
end: "",
agencyName: ""
}))
},
methods: {
onAddBarcodes() {
const newBarcode = {
id: Math.random() * Math.random() * 1000,
start: "",
end: "",
agencyName: ""
};
this.barcodes.push(newBarcode);
},
onDeleteBarcodes(id) {
this.barcodes = this.barcodes.filter(barcode => barcode.id !== id);
},
}
}
You can call the onAddBarcodes n times in beforeMount:
beforeMount(){
const n = 5; // Or any number
Array.from(new Array(n)).forEach(el => this.onAddBarcodes());
}
I've created a simple component named DefaultButton.
It bases on properties, that are being set up whenever this component is being created.
The point is that after mounting it, It does not react on changes connected with "defaultbutton", that is an object located in properties
<template>
<button :class="buttonClass" v-if="isActive" #click="$emit('buttonAction', defaultbutton.id)" >
{{ this.defaultbutton.text }}
</button>
<button :class="buttonClass" v-else disabled="disabled">
{{ this.defaultbutton.text }}
</button>
</template>
<script>
export default {
name: "defaultbutton",
props: {
defaultbutton: Object
},
computed: {
buttonClass() {
return `b41ngt ${this.defaultbutton.state}`;
},
isActive() {
return (this.defaultbutton.state === "BUTTON_ACTIVE" || this.defaultbutton.state === "BUTTON_ACTIVE_NOT_CHOSEN");
}
}
};
</script>
Having following component as a parent one:
<template>
<div v-if="state_items.length == 2" class="ui placeholder segment">
{{ this.state_items[0].state }}
{{ this.state_items[1].state }}
{{ this.current_active_state }}
<div class="ui two column very relaxed stackable grid">
<div class="column">
<default-button :defaultbutton="state_items[0]" #buttonAction="changecurrentstate(0)"/>
</div>
<div class="middle aligned column">
<default-button :defaultbutton="state_items[1]" #buttonAction="changecurrentstate(1)"/>
</div>
</div>
<div class="ui vertical divider">
Or
</div>
</div>
</template>
<script type="text/javascript">
import DefaultButton from '../Button/DefaultButton'
export default {
name: 'changestatebox',
data() {
return {
current_active_state: 1
}
},
props: {
state_items: []
},
components: {
DefaultButton
},
methods: {
changecurrentstate: function(index) {
if(this.current_active_state != index) {
this.state_items[this.current_active_state].state = 'BUTTON_ACTIVE_NOT_CHOSEN';
this.state_items[index].state = 'BUTTON_ACTIVE';
this.current_active_state = index;
}
},
},
mounted: function () {
this.state_items[0].state = 'BUTTON_ACTIVE';
this.state_items[1].state = 'BUTTON_ACTIVE_NOT_CHOSEN';
}
}
</script>
It clearly shows, using:
{{ this.state_items[0].state }}
{{ this.state_items[1].state }}
{{ this.current_active_state }}
that the state of these items are being changed, but I am unable to see any results on the generated "DefaultButtons". Classes of objects included in these components are not being changed.
#edit
I've completely changed the way of delivering the data.
Due to this change, I've abandoned the usage of an array; instead I've used two completely not related object.
The result is the same - class of the child component's object is not being
DefaulButton.vue:
<template>
<button :class="buttonClass" v-if="isActive" #click="$emit('buttonAction', defaultbutton.id)" >
{{ this.defaultbutton.text }}
</button>
<button :class="buttonClass" v-else disabled="disabled">
{{ this.defaultbutton.text }}
</button>
</template>
<style lang="scss">
import './DefaultButton.css';
</style>
<script>
export default {
name: "defaultbutton",
props: {
defaultbutton: {
type: Object,
default: () => ({
id: '',
text: '',
state: '',
})
}
},
computed: {
buttonClass() {
return `b41ngt ${this.defaultbutton.state}`;
},
isActive() {
return (this.defaultbutton.state === "BUTTON_ACTIVE" ||
this.defaultbutton.state === "BUTTON_ACTIVE_NOT_CHOSEN");
}
}
};
</script>
ChangeStateBox.vue:
<template>
<div class="ui placeholder segment">
{{ this.state_first.state }}
{{ this.state_second.state }}
{{ this.current_active_state }}
<div class="ui two column very relaxed stackable grid">
<div class="column">
<default-button :defaultbutton="state_first" #buttonAction="changecurrentstate(0)"/>
</div>
<div class="middle aligned column">
<default-button :defaultbutton="state_second" #buttonAction="changecurrentstate(1)"/>
</div>
</div>
<div class="ui vertical divider">
Or
</div>
</div>
</template>
<script type="text/javascript">
import DefaultButton from '../Button/DefaultButton'
export default {
name: 'changestatebox',
data() {
return {
current_active_state: 1
}
},
props: {
state_first: {
type: Object,
default: () => ({
id: '',
text: ''
})
},
state_second: {
type: Object,
default: () => ({
id: '',
text: ''
})
},
},
components: {
DefaultButton
},
methods: {
changecurrentstate: function(index) {
if(this.current_active_state != index) {
if(this.current_active_state == 1){
this.$set(this.state_first, 'state', "BUTTON_ACTIVE_NOT_CHOSEN");
this.$set(this.state_second, 'state', "BUTTON_ACTIVE");
} else {
this.$set(this.state_first, 'state', "BUTTON_ACTIVE");
this.$set(this.state_second, 'state', "BUTTON_ACTIVE_NOT_CHOSEN");
}
this.current_active_state = index;
}
},
},
created: function () {
this.state_first.state = 'BUTTON_ACTIVE';
this.state_second.state = 'BUTTON_ACTIVE_NOT_CHOSEN';
}
}
</script>
You're declaring props wrong. It is either an array of prop names or it is an object with one entry for each prop declaring its type, or it is an object with one entry for each prop declaring multiple properties.
You have
props: {
state_items: []
},
but to supply a default it should be
props: {
state_items: {
type: Array,
default: []
}
},
But your problem is most likely that you're mutating state_items in such a way that Vue can't react to the change
Your main problem is the way you are changing the button state, according with Array change detection vue can't detect mutations by indexing.
Due to limitations in JavaScript, Vue cannot detect the following
changes to an array:
When you directly set an item with the index, e.g.
vm.items[indexOfItem] = newValue When you modify the length of the
array, e.g. vm.items.length = newLength
In case someone will be having the same issue:
#Roy J as well as #DobleL were right.
The reason behind this issue was related with the wrong initialization of state objects.
According to the documentation:
Vue cannot detect property addition or deletion.
Since Vue performs the getter/setter conversion process during instance
initialization, a property must be present in the
data object in order for Vue to convert it and make it reactive.
Before reading this sentence, I used to start with following objects as an initial data:
var local_state_first = {
id: '1',
text: 'Realized',
};
var local_state_second = {
id: '2',
text: 'Active'
};
and the correct version of it looks like this:
var local_state_first = {
id: '1',
text: 'Realized',
state: 'BUTTON_ACTIVE'
};
var local_state_second = {
id: '2',
text: 'Active',
state: 'BUTTON_ACTIVE'
};
whereas declaring the main component as:
<change-state-box :state_first="local_state_first" :state_second="local_state_second" #buttonAction="onbuttonAction"/>
Rest of the code remains the same ( take a look at #edit mark in my main post )