Separate an Object from its parent after pushing it to an Array - javascript

I have a form for my blog. The blog is an object with numerous content objects inside of it.
I think I am not thinking clearly because the content I push to the Array keeps its reactivity to the parent even though its not a ref or anything like that.
Here is the Vue Component:
<script setup lang="ts">
const blog = ref({
link: 'Link',
category: 'category',
title: 'title',
image: 'Image',
body: 'Body',
content: [] as any[]
})
let contentItem = {
parent: 'parent',
title: 'title',
image: 'Image',
code: 'Code',
body: 'Body',
}
// Add Content Item
function addContent(content: any) {
const item = content
blog.value.content.push(item)
}
</script>
<template>
<div class="flex p-8 text-slate-600 w-full flex-col gap-4 rounded">
<h4 class="font-bold text-red-50">Blog Content</h4>
<input type="text" v-model="contentItem.parent">
<input type="text" v-model="contentItem.title">
<input type="text" v-model="contentItem.image">
<textarea class="w-full h-[200px]" type="text" v-model="contentItem.code"> {{ contentItem.code }} </textarea>
<textarea class="w-full h-[200px]" type="text" v-model="contentItem.body"> {{ contentItem.body }} </textarea>
<button class="p-4 w-1/2 bg-slate-50" type="button" #click="addContent(contentItem)">Add Content</button>
</div>
</template>
`
I have tried using "readonly", I tried passing the value to another object (as you see in the current code) and I just don't understand why it can pass a new object, but there all the same object.
I know the solutions going to be simple, but I looked and all I kept finding is people wanting to keep the reactivity in an array (opposite of my problem).

Try to push object by value not by reference:
const { ref, computed, onMounted } = Vue
const app = Vue.createApp({
setup() {
const blog = ref({
link: 'Link',
category: 'category',
title: 'title',
image: 'Image',
body: 'Body',
content: []
})
let contentItem = ref({
parent: 'parent',
title: 'title',
image: 'Image',
code: 'Code',
body: 'Body',
})
function addContent(content) {
blog.value.content.push({...content})
}
return {
addContent,
contentItem,
blog
}
}
})
app.mount('#demo')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<div class="flex p-8 text-slate-600 w-full flex-col gap-4 rounded">
<h4 class="font-bold text-red-50">Blog Content</h4>
<input type="text" v-model="contentItem.parent">
<input type="text" v-model="contentItem.title">
<input type="text" v-model="contentItem.image">
<textarea class="w-full h-[200px]" type="text" v-model="contentItem.code"> {{ contentItem.code }} </textarea>
<textarea class="w-full h-[200px]" type="text" v-model="contentItem.body"> {{ contentItem.body }} </textarea>
<button class="p-4 w-1/2 bg-slate-50" type="button" #click="addContent(contentItem)">Add Content</button>
</div>
{{blog}}
</div>

Related

v-model inside a component is not listening to events vuejs

I need to make a dynamic v-model using v-for. I have a child component that will be used inside the father component.
I'll put the full code and the warning received.
It is funny because I use the same method in other project (NUXT) and it is working just fine. And this project (vuejs) has the same code and it is not.
It is seems to me it is not listening to 'event' and because of that the v-model is not working well. The v-model value does not update according to the changes on input space.
This is the Child Component
<template>
<div class="row p-1 g-3 d-md-flex justify-content-md-end">
<div class="col-4 d-flex flex-row-reverse">
<label for="inputPassword6" class="col-form-label">{{ profileDataItem }}</label>
</div>
<div class="col-8">
<input
type="text"
class="form-control"
:value="value"
#input="$emit('input', $event.target.value)"
>
</div>
</div>
</template>
<script>
export default {
name: 'RegisterField',
props: {
profileDataItem: {
type: String,
required: true
},
value: {
required: true
},
}
}
</script>
And this is the father component
<template>
<div class="form-register p-3 m-auto">
<form class="login-form p-3 border m-auto">
<h4 class="text-center p-2">Register yourself</h4>
<RegisterField
v-for="(item, index) in profileDataItems"
:key="index"
:profileDataItem="profileItems[index]"
v-model="item.model"
>
</RegisterField>
</form>
</div>
</template>
<script>
import RegisterField from './RegisterField.vue'
import ButtonSubmit from '../ButtonSubmit.vue'
export default {
name: 'RegisterForm',
components: {
RegisterField,
ButtonSubmit
},
data () {
return {
profileItems: ['First Name', 'Last Name', 'Email', 'Password', 'Confirm Password'],
profileDataItems: [
{ model: "firstName" },
{ model: "lastName" },
{ model: "email" },
{ model: "password" },
{ model: "confirmPassword" },
],
payloadProfileData: [],
}
},
}
</script>
Warning on console
[Vue warn]: Missing required prop: "value"
at <RegisterField key=4 profileDataItem="Confirm Password" modelValue="confirmPassword" ... >
at <RegisterForm>
at <SigninPage onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< Proxy {…} > >
at <RouterView>
at <App>
change value prop to modelValue your component's code should be this:
<template>
<div class="row p-1 g-3 d-md-flex justify-content-md-end">
<div class="col-4 d-flex flex-row-reverse">
<label for="inputPassword6" class="col-form-label">{{
profileDataItem
}}</label>
</div>
<div class="col-8">
<input
type="text"
class="form-control"
:value="modelValue"
#input="$emit('input', $event.target.value)"
/>
</div>
</div>
</template>
<script>
export default {
name: 'RegisterField',
props: {
profileDataItem: {
type: String,
required: true,
},
modelValue: {
required: true,
},
},
};
</script>

How to preload text into text area in v-for loop(vue)?

I am struggling on something that I thought would first be easy to solve.
Imagine a page loading with multiple text areas as:
<div v-for="(item, index) in articles" class="bg-white shadow-lg sm:rounded-lg gap-3 mt-2" :key="index">
<h1>{{item.title}}</h1>
<textarea v-model="form5.fullArticle" :value="item.paragraph" id="topicDescription" :rows="5" cols="80">
</textarea>
</div>
When the v-for loop runs, I am getting a lot of item.paragraph and item.title data and would like to save them to an array.
Problem 1:
I cannot work out how to preload {{item.paragraph}} into a text area. It looks like :value="" is not accepted. and I also tried this <textarea>{{item.paragraph}}</textarea> but no luck (I did not expect this behavior).
Problem 2:
How can I save in my v-for the {item.title}} and {{item.paragraph}} into a new array (but group them together).
Any idea please?
Thanks!
Try to use just v-model (it is the shortcut of :value="text"
#input="event => text = event.target.value") :
const app = Vue.createApp({
data() {
return {
articles: [{id: 1, title: "AAA", paragraph: "aaa"}, {id: 2, title: "BBB", paragraph: "bbb"},{id: 3, title: "CCC", paragraph: "ccc"}],
newArray: []
};
},
methods: {
saveArt(item) {
const found = this.newArray.find(a => a.id === item.id)
if (!found) this.newArray.push(item)
}
}
})
app.mount('#demo')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<div v-for="(item, index) in articles" class="bg-white shadow-lg sm:rounded-lg gap-3 mt-2" :key="index">
<h5>{{item.title}}</h5>
<textarea v-model="item.paragraph" id="topicDescription" :rows="3" cols="30">
</textarea>
<button #click="saveArt(item)">Add</button>
</div>
<p>new array</p>
{{ newArray }}
</div>

Vue.js Property or method "options" is not defined on the instance but referenced during render

I working with Vue.js (and Inertia.js) and I want to build an select in my form, but my select after compiling is empty and dev console in web browser throwings me this error:
Property or method "options" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property
Please, let me to show you my code for getting help - Index.vue:
<template>
<app-layout>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
<div>
<div class="p-6 sm:px-20 bg-white border-b border-gray-200">
<div class="flex items-center justify-end mt-4">
<jet-application-logo class="block h-12 w-auto items-center mx-auto" />
</div>
<div>
<div class="max-w-7xl mx-auto py-10 sm:px-6 lg:px-8">
<jet-form-section #submitted="createInvestment">
<template #title>
Investment gateway
</template>
<template #form>
<div class="mx-auto text-center">
<jet-input-amount id="amount" type="number" name="amount" placeholder="Amount for invest" v-model="investmentForm.amount" />
<jet-input-error :message="investmentForm.error('amount')" class="mt-2" />
<select v-model="currency" name="currency" class="input-currency">
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
</div>
<div class="mx-auto text-center">
<jet-input id="card_number" type="email" name="card_number" placeholder="Card Number" v-model="investmentForm.paypal" />
<jet-input-error :message="investmentForm.error('card_number')" class="mt-2" />
</div>
<div class="mx-auto text-center nomad-slash">
<jet-input-card id="card_expiration_month" type="email" name="card_expiration_month" placeholder="MM" v-model="investmentForm.paypal" />
<jet-input-error :message="investmentForm.error('card_expiration_month')" class="mt-2" />
/
<jet-input-card id="card_expiration_year" type="email" name="card_expiration_year" placeholder="YY" v-model="investmentForm.paypal" />
<jet-input-error :message="investmentForm.error('card_expiration_year')" class="mt-2" />
<jet-input-card id="card_cvv" type="email" name="card_cvv" placeholder="CVV" v-model="investmentForm.paypal" />
<jet-input-error :message="investmentForm.error('card_cvv')" class="mt-2" />
</div>
</template>
<template #actions>
<jet-action-message :on="investmentForm.recentlySuccessful" class="mr-3">
Your money are on the way!
</jet-action-message>
<jet-button :class="{ 'opacity-25': investmentForm.processing }" :disabled="investmentForm.processing">
Invest
</jet-button>
<br /><br />
</template>
</jet-form-section>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</app-layout>
</template>
<script>
import JetActionMessage from './../../Jetstream/ActionMessage'
import JetFormSection from './../../Jetstream/FormSection'
import JetButton from './../../Jetstream/Button'
import JetInput from './../../Jetstream/Input'
import JetInputAmount from './../../Jetstream/InputAmount'
import JetInputCard from './../../Jetstream/InputCard'
import JetLabel from './../../Jetstream/Label'
import JetInputError from './../../Jetstream/InputError'
import JetApplicationLogo from './../../Jetstream/ApplicationLogo'
import AppLayout from "./../../Layouts/AppLayout";
export default {
name: "Index",
components: {AppLayout, JetInput, JetLabel, JetFormSection, JetButton, JetActionMessage, JetInputError, JetApplicationLogo, JetInputAmount, JetInputCard},
data() {
return {
investmentForm: this.$inertia.form({
amount: '',
currency: '',
options: [
{ text: '$ USD', value: 'usd' },
{ text: 'R ZAR', value: 'zar' },
{ text: '€ EUR', value: 'eur' },
{ text: '£ GBP', value: 'gbp' },
{ text: '₹ INR', value: 'inr' },
{ text: '$ AUD', value: 'aud' },
],
card_number: '',
card_expiration_month: '',
card_expiration_year: '',
card_cvv: '',
}, {
bag: 'createInvestment',
resetOnSuccess: false,
}),
}
},
methods: {
createInvestment() {
this.investmentForm.post('/input/create', {
preserveScroll: true,
});
}
}
}
</script>
<style scoped>
</style>
In your v-for loop, VueJS is trying to find options key in your data object. Your options are under investmentForm key, so in your v-for, instead of
v-for="option in options"
You should write
v-for="option in investmentForm.options"
The error you are getting simply means that Vue does not know what options is as it can't find it anywhere.
options is inside investmentForm data so you need to update this
<option v-for="option in investmentForm.options" v-bind:value="option.value">
{{ option.text }}
</option>
options is inside investmentForm, so it must be invoked in the template like this investmentForm.options.
An alternative to the solutions already given would be to add a computed method, this way you don't need to change the template:
// ...
computed: {
options() {
return investmentForm.options;
}
},
// ...

How to store tiptap editor data in a php form?

I want to use the TipTap editor in a php form as a textarea field. I've created a Vue component and passed it to the blade view.
Blade view:
<form method="post" action="{{ route('dashboard.edit.postInfo', $garage) }}">
#method('PATCH')
#csrf
<div id="app">
<editor content='{{ $garage->description }}'></editor>
</div>
<button type="submit">Save</button>
</form>
My Vue Component:
<template>
<div class="editor">
<div class="card p-2 mt-1">
<editor-content :editor="editor" />
</div>
</div>
</template>
<script>
import { Editor, EditorContent } from 'tiptap'
export default {
name: "editor",
components: {
EditorContent,
},
props: [
'content'
],
data() {
return {
editor: null,
}
},
mounted() {
this.editor = new Editor({
content: this.content
})
},
beforeDestroy() {
// Always destroy your editor instance when it's no longer needed
this.editor.destroy()
},
}
</script>
How can I do this? Thanks you in advance.
This is the console.Log output:
Editor {…}
activeMarks: (...)
activeNodes: (...)
commands: (...)
defaultOptions: (...)
element: (...)
options: Object
autoFocus: (...)
content: "<p>KFZ Meisterwerkstatt...</p>"
Some lorem ipsum text for posting
Try this bind :value="editor"
<div class="card p-2 mt-1">
<editor-content :editor="editor" />
<input type="hidden" name="content" :value="editor">
</div>
then u will get content key in laravel with data

Property or method "sendResetMail" is not defined on the instance but referenced during render

I'm relatively new to Vue and super stuck with this error message when I try to make this reset email modal work in my Vue project:
Property or method "sendResetMail" is not defined on the instance but
referenced during render. Make sure that this property is reactive,
either in the data option, or for class-based components, by
initializing the property.
I have no idea what I need to do to make this work. I followed the Vue documentation and declared resetEmail in the data option.
ForgotPassword.vue:
<template>
<section>
<a #click="isComponentModalActive = true">
Forgot Password?
</a>
<b-modal :active.sync="isComponentModalActive" has-modal-card>
<modal-form v-bind="resetEmail"></modal-form>
</b-modal>
</section>
</template>
<script>
import firebase from 'firebase/app'
import 'firebase/auth'
import 'firebase/firestore'
const ModalForm = {
props: ['resetEmail'],
template: `
<form #submit.prevent="sendResetMail">
<div class="modal-card" style="width: auto">
<header class="modal-card-head">
<p class="modal-card-title">Forgot password?</p>
</header>
<section class="modal-card-body">
<b-field label="Email">
<b-input
type="email"
:value="resetEmail"
placeholder="Your email"
required>
</b-input>
</b-field>
</section>
<footer class="modal-card-foot">
<button class="button" type="button" #click="$parent.close()">Close</button>
<button class="button is-primary">Reset</button>
</footer>
</div>
</form>
`
}
export default {
components: {
ModalForm
},
data () {
return {
isComponentModalActive: false,
resetEmail: '',
feedback: null
}
},
methods: {
sendResetMail () {
var auth = firebase.auth()
var emailAddress = this.resetEmail
auth.sendPasswordResetEmail(emailAddress).then(function () {
// Email sent.
console.log('email send')
}).catch(function (error) {
// An error happened.
this.feedback = error.message
console.log(error.message)
})
}
}
}
</script>
This is the file where I use the ForgotPassword.vue component,
Login.vue:
<template>
<section class="section">
<div class="container">
<div class="columns">
<div class="column"></div>
<div class="column is-half">
<div class="box">
<h1 class="title">Login</h1>
<form #submit.prevent="login">
<b-field label="Email" :message="feedback" :type="type">
<b-input placeholder="Email" icon="email" type="email" v-model="email">
</b-input>
</b-field>
<b-field label="Password" :message="feedback" :type="type">
<b-input placeholder="Password" type="password" icon="textbox-password" password-reveal v-model="password">
</b-input>
</b-field>
<button type="submit" class="button is-primary is-fullwidth">Login</button>
<div class="field">
<div class="control">
<p class="control has-text-centered">
<ForgotPassword/>
</p>
</div>
</div>
<div class="field">
<div class="control">
<p class="control has-text-centered">
Don't have an account?
<a href="/register">
<router-link :to="{ name: 'Signup' }">
Signup
</router-link>
</a>
</p>
</div>
</div>
</form>
</div>
</div>
<div class="column"></div>
</div>
</div>
</section>
</template>
<script>
import firebase from 'firebase/app'
import 'firebase/auth'
import 'firebase/firestore'
import ForgotPassword from '#/components/auth/ForgotPassword'
export default {
name: 'login',
metaInfo: {
// specify a metaInfo.title, this title will be used
title: 'Login',
// all titles will be injected into this template
titleTemplate: '%s | Wterdrops.com'
},
components: {
ForgotPassword
},
data () {
return {
email: null,
password: null,
feedback: null,
type: null
}
},
methods: {
login () {
if (this.email && this.password) {
firebase
.auth()
.signInWithEmailAndPassword(this.email, this.password)
.then(cred => {
this.$router.push({
name: 'Dashboard'
})
})
.catch(err => {
this.feedback = err.message
})
this.feedback = null
} else {
this.feedback = 'Please fill in both fields.'
this.type = 'is-danger'
}
}
}
}
</script>
<style>
.login {
max-width: 400px;
margin-top: 60px;
}
</style>
I would be very grateful if someone can explain to me what I'm missing :)
You are referencing
<form #submit.prevent="sendResetMail">
inside your ModalForm component.
The problem is that this template is going to look for the method sendResetMail on that ModalForm component template when it gets rendered since you the code is referencing it. However this sendResetMail method is not directly associated on that.
You can consider using a mix-in if you need to use the sendResetMail in many places , or maybe just move that method directly to the same component "ModalForm" that is referencing it.
You can also look into for example eventBus to trigger the method by emitting an event.
The simplest option if you only need to call it from the MOdalForm component is to just move the sendResetMail direcly to that component. I believe that would probably fix your issue.

Categories