Vue.JS data property not defined - javascript

I have been trying to show my modal but for some reason it keeps saying that the property isn't defined even though I have declared it in the Data()
I feel like I am missing something critical to my understanding on how this all works ...
The property is defined as false on load and should turn to true on click of the button.
<template>
<div class="product-item">
<h3>{{product.name}}</h3>
<p>{{product.tagline}}</p>
<img class="product-image" :src="product.image_url">
<p>PH: {{product.ph}}</p>
<button class="show-modal" #click="showModal = true">Show a tip</button>
<modal v-if="showModal" #close="showModal = false"></modal>
</div>
</template>
<script>
import Modal from "#/components/Modal.vue";
export default {
components: {
Modal
},
Data() {
showModal: false
},
props: {
product: {
type: Object
}
},
methods: {},
computed: {},
mounted() {}
};
</script>

You data Object should be returned via a function like :
data(){
return{
showModal: false
}
}
data should be in lowercase .

A component’s data option must be a function, so that each instance can maintain an independent copy of the returned data object:
data: function () {
return {
...
},
}
import Modal from "#/components/Modal.vue";
export default {
components: {
Modal
},
data: function () {
return {
showModal: false
},
}
props: {
product: {
type: Object
}
},
methods: {},
computed: {},
mounted() {}
};
<template>
<div class="product-item">
<h3>{{product.name}}</h3>
<p>{{product.tagline}}</p>
<img class="product-image" :src="product.image_url">
<p>PH: {{product.ph}}</p>
<button class="show-modal" #click="showModal = true">Show a tip</button>
<modal v-if="showModal" #close="showModal = false"></modal>
</div>
</template>

Related

Why computed property works on preserving the reactivity but attributes in data() {} does not

I'm following this tutorial, where they point out a small bug with the checkbox status of forgetting the state of the ticked/unticked which can be solved by using the computed property.
I would like to ask, even if the attribute isDone in Data (ToDoItem.vue) has been changed to true (by ticking the checkbox), why the box is still unticked after clicking edit then cancel, and why computed property could solve this bug.
Below are parts of the scripts.
ToDoItem.vue
<template>
<div class="stack-small" v-if="!isEditing">
<div class="custom-checkbox">
<input
type="checkbox"
class="checkbox"
:id="id"
:checked="isDone"
#change="$emit('checkbox-changed')"
/>
<label :for="id" class="checkbox-label">{{ label }}</label>
</div>
<div class="btn-group">
<button
type="button"
class="btn"
ref="editButton"
#click="toggleToItemEditForm"
>
Edit <span class="visually-hidden">{{ label }}</span>
</button>
<button type="button" class="btn btn__danger" #click="deleteToDo">
Delete <span class="visually-hidden">{{ label }}</span>
</button>
</div>
</div>
<to-do-item-edit-form
v-else
:id="id"
:label="label"
#item-edited="itemEdited"
#edit-cancelled="editCancelled"
></to-do-item-edit-form>
</template>
<script>
import ToDoItemEditForm from "./ToDoItemEditForm";
export default {
components: {
ToDoItemEditForm,
},
props: {
label: { required: true, type: String },
done: { default: false, type: Boolean },
id: { required: true, type: String },
},
data() {
return {
isEditing: false,
isDone: this.done, // after deleting this line and use
//computed: {} below, the bug is solved.
};
},
// computed: {
// isDone() {
// return this.done;
// },
// },
};
</script>
ToDoItem.vue
<template>
<div id="app">
<h1>To-Do List</h1>
<to-do-form #todo-added="addToDo"></to-do-form>
<h2 id="list-summary" ref="listSummary" tabindex="-1"> {{ listSummary }} </h2>
<ul aria-labelledby="list-summary" class="stack-large">
<li v-for="item in ToDoItems" :key="item.id">
<to-do-item
:label="item.label"
:done="item.done"
:id="item.id"
#checkbox-changed="updateDoneStatus(item.id)"
#item-deleted="deleteToDo(item.id)"
#item-edited="editToDo(item.id, $event)"
>
</to-do-item>
</li>
</ul>
</div>
</template>
<script>
import ToDoItem from "./components/ToDoItem.vue";
import ToDoForm from "./components/ToDoForm.vue";
import uniqueId from "lodash.uniqueid";
export default {
name: "app",
components: {
ToDoItem,
ToDoForm,
},
data() {
return {
ToDoItems: [],
};
},
methods: {
updateDoneStatus(toDoId) {
const toDoToUpdate = this.ToDoItems.find((item) => item.id === toDoId);
toDoToUpdate.done = !toDoToUpdate.done;
console.dir(toDoToUpdate.done)
},
};
</script>
I'm not an expert in vue, but I believe that the this.done being assigned to isDone: is only done once in data(), and it wouldn't be done if the props change (the value of isDone in data() won't change when the prop done changes). While in computed, isDone will watch the done prop value, and if that prop value changes, the computed will be notified and thus changes the isDone data.

How to pass dynamically props from one child component to another on the parent page

I have two child components I have to pass dynamically props from first child to parent and from parent to second child.
Parent
<script>
data: () => ({
model: {}
}),
methods: {
changeData(payload) {
this.model.personalData = {...payload}
}
}
</script>
<template>
<first-child #changeData="(payload) => changeData(payload)"/>
<second-child :enter-object="model" />
</template>
Child one
<script>
data: () => ({
model: {}
}),
methods: {
changeData() {
this.$emit("changeData", this.model);
}
}
</script>
<template>
<v-text-field v-model="model.name" #input="changeData()">
<v-text-field v-model="model.email" #input="changeData()">
</template>
Child two
<script>
props: {
enterObject: {
type: Object,
required: false,
default: () => ({})
}
},
data: () => ({
model: {}
}),
watch: {
enterObject: {
immediate: true,
handler() {
Object.assign(this.model.personalData, this.enterObject.personalData);
}
}
</script>
<template>
<div>
<div v-if="model.personalData.name || model.personalData.email">
<span class="mr-3">{{ model.personalData.name }}</span>
<span>{{ model.personalData.email }}</span>
</div>
<div v-else>
No data
</div>
</div>
</template>
I get data in parent component with no problem, but this data doesn't pass to second child, why I have always "No data" ?
I tested your code and found a few things:
You need to create "personalData" inside the model in "childTwo".
<template>
<div>
// I changed the validation for personalData
<div v-if="model.personalData">
<span class="mr-3">{{ model.personalData.name }}</span>
<span>{{ model.personalData.email }}</span>
</div>
<div v-else>No data</div>
</div>
</template>
export default {
props: {
enterObject: {
type: Object,
required: false,
default: () => ({})
}
},
data: () => ({
model: {
personalData: {}
}
}),
watch: {
enterObject: {
deep: true,
handler() {
// Add a validation in the handler, you can use Object assign inside the validation.
if(this.enterObject) {
Object.assign(this.model.personalData, this.enterObject.personalData)
}
}
}
}
It's worked for me.I hope it helps you.
You have to assign the value of the object using this.$set for more about object reactivity click here
your Parent component should be like this:-
here is the working example
<template>
<div>
<first-child #change-data="(payload) => changeData(payload)" />
<second-child :enter-object="model" />
</div>
</template>
<script>
import FirstChild from "./FirstChild";
import SecondChild from "./SecondChild";
export default {
data: () => ({
model: {},
compKey: 0,
}),
components: {
FirstChild,
SecondChild,
},
methods: {
changeData(payload) {
this.$set(this.model, "test", payload);
//this.model.test = payload;
},
},
};
</script>

Clear v-model input from method in Vue2

I have a form with two datepickers and a button to clear each one of them as follows:
<template>
<form>
<div>
<datetime id="someDate" v-model="fields.some_date"></datetime>
<button #click.prevent="clearSomeDate()">X</button>
</div>
<div>
<datetime id="anotherDate" v-model="fields.another_date"></datetime>
<button #click.prevent="clearAnotherDate()">X</button>
</div>
</form>
</template>
<script>
export default {
data() {
return {
fields: {
some_date: null,
another_date: null
},
};
},
methods: {
clearSomeDate() {
this.fields.some_date = null;
},
clearAnotherDate() {
this.fields.another_date = null;
},
},
}
</script>
And works pretty well, but it's not so much reusable.
Is there a way to achieve this with a single clearField() function and pass the model as a parameter or something? Should I do my own custom component to make it work?
You could completely get rid of the methods by just doing the assignment directly in the template:
<div>
<datetime id="someDate" v-model="fields.some_date"></datetime>
<button #click.prevent="fields.some_date = null">X</button>
</div>
That way you have the clearing logic directly next to the model.
If you want to make it reusable you could also extract it into a separate component:
<template>
<div>
<datetime v-bind="$attrs" :value="value" v-on="eventHandlers"></datetime>
<button #click.prevent="$emit('input', null)">X</button>
</div>
</template>
<script>
export default {
name: "datetime-with-x",
model: { prop: "value", event: "input" },
props: ["value"],
inheritAttrs: false,
computed: {
eventHandlers() {
return {
...this.$listeners,
input: ev => this.$emit('input', ev)
};
}
}
};
</script>
and then use it in your component like this:
<template>
<form>
<datetime-with-x id="someDate" v-model="fields.some_date" />
<datetime-with-x id="anotherDate" v-model="fields.another_date" />
</form>
</template>
<script>
import DatetimeWithX from "./datetime-with-x";
export default {
name: "your-form",
components: { DatetimeWithX },
data() {
return {
fields: {
some_date: null,
another_date: null
}
};
}
};
</script>
You could pass the field property name as parameter
clearField(name) {
this.fields[name] = null;
}
and call it with argument
<button #click.prevent="clearField('some_date')">X</button>
Though a cleaner approach would be to build another reusable component and bind it with v-model
<div>
<datetime :value="value" #change="$emit('input', $event)"></datetime>
<button #click.prevent="$emit('input', null)">X</button>
</div>

Vue 3: How to update component props value correctly using composition api?

I have like this component:
<template>
<div>
<p>Current coords: <strong>{{ coords }}</strong></p>
<button type="button" #click="updateCoords">
</div>
</template>
<script>
export default {
props: {
coords: {
type: Array,
required: true
}
},
setup(props) {
const updateCoords = () => {
props.coords = [38.561785, -121.449756]
// props.coords.value = [38.561785, -121.449756]
}
return { updateCoords }
},
}
</script>
I tried update prop coords value with updateCoords method but I get error message:
Uncaught TypeError: Cannot set properties of undefined (setting
'coords')
How I can correctly update props value in my case?
Props are readonly:
https://v3.vuejs.org/guide/component-props.html#one-way-data-flow
If you want to have two way binding for props, you'll need to implement the v-model pattern:
https://v3-migration.vuejs.org/breaking-changes/v-model.html#_3-x-syntax
<template>
<div>
<p>Current coords: <strong>{{ coords }}</strong></p>
<button type="button" #click="updateCoords">
</div>
</template>
<script>
export default {
props: {
modelValue: {
type: Array,
required: true
}
},
emits: ['update:modelValue'],
setup(props, {emit}) {
const updateCoords = () => {
emit('update:modelValue', [38.561785, -121.449756])
}
return { updateCoords }
},
}
</script>

Component not being initialized initially vuejs

In
https://codesandbox.io/s/rmh2n?file=/src/components/NestedDraggable.vue
, in:
<template>
<draggable class="dragArea" tag="ul" :list="tasks" :group="{ name: 'g1' }">
<li v-for="el in tasks" :key="el.name">
<generic-item :taskItem="el"> </generic-item>
<p>{{ el.name }}</p>
<nested-draggable :tasks="el.tasks" />
</li>
</draggable>
</template>
why isn't generic-item rendering el?
GenericItem:
<template>
<div v-if="taskItemLocal['itemSectionCategoryId']">
<p>
Section: {{taskItemLocal['itemSectionCategoryName']}}
</p>
</div>
<div v-else-if="taskItemLocal['itemSectionCategoryItemId']">
<p>
Item: {{taskItemLocal['itemSectionCategoryItemName']}}
</p>
</div>
<div v-else>
Not set
</div>
</template>
<script>
export default {
name: "generic-item",
props: {
taskItem: {
required: true,
type: Object,
default() {
return {};
},
},
},
components: {},
watch: {
taskItem: {
deep: true,
handler(val) {
this.taskItemLocal = Object.assign({}, val);
},
},
},
computed: {
getTaskItemLocal: {
get() {
if (!this.taskItemLocal) {
return Object.assign({}, this.taskItem);
}
return Object.values(this.taskItemLocal);
},
set(value) {
this.taskItemLocal = value;
},
},
},
data() {
return {
taskItemLocal: {
type: Object,
default: {},
},
};
},
};
</script>
<style scoped></style>
It looks like it isn't computing taskItemLocal. This component will have to modify props (they can't be used directly in the template). How can I "directly" copy props to a local variable and use it in the template "on the first run"?

Categories