Vue open a component in a component - vue.js

In App.vue I have a button to open a component . Inside the component BigForm, I also have a button to open a component named, . But as I open the component. Vue re-render the page with the warning:
The "data" option should be a function that returns a per-instance value in component definitions.
App.Vue
<template>
<div id="app">
<button #click="showModal()">Show</button>
<BigForm v-if="modal" />
</div>
</template>
<script>
import BigForm from "./components/BigForm";
export default {
name: "App",
components: {
BigForm,
},
data() {
return {
modal: false,
};
},
methods: {
showModal() {
this.modal = !this.modal;
},
},
};
</script>
BigForm.vue:
<template>
<div class="hello">
<form style="height: 300px; width: 300px; background-color: green">
<button #click="showSmallForm()">Show small form</button>
<SmallForm
v-if="toggleSmallForm"
style="width: 100px; height: 100px; background-color: yellow"
/>
</form>
</div>
</template>
<script>
import SmallForm from "./SmallForm";
export default {
name: "HelloWorld",
components: {
SmallForm,
},
data() {
return {
toggleSmallForm: false,
};
},
methods: {
showSmallForm() {
this.toggleSmallForm = !this.toggleSmallForm;
},
},
};
</script>
And SmallForm.vue:
<template>
<form>
<input placeholder="Small form input" />
<button>This is small form</button>
</form>
</template>
This is the code to my example in codesandbox:
https://codesandbox.io/s/late-cdn-ssu7f?file=/src/App.vue

The problem is not related to Vue itself but the HTML.
When you use <button> inside <form>, the default type of the button is submit (check this) - when you click the button, the form is submitted to the server causing the page to reload.
You can prevent that by explicitly setting type <button type="button"> (HTML way) or by preventing default action (Vue way) <button #click.prevent="showSmallForm()">Show small form</button> (see event modifiers)
Also note that using <form> in another <form> is not allowed in HTML

Related

Have trouble edit my to do list content via Vuex

I have made a to do list and wrote a code to edit my to do list title via VUEX but it doesn't work properly.
I would really apreaciate if someone can help me with it.
SandBox code example
Actually i want to filter the tasks in my view to select the one is clicked and then change its content to hat user types
VUEX state
tasks: [],
VUEX mutations
EDIT_TODO(state, task) {
state.tasks.filter((o) => {
o.id = task.id;
});
}
component
<template>
<div style="background-color: white; color: black; padding: 20px">
<input type="text" v-model="inputText" />
<br />
<button #click="editToDo">update value</button>
{{ task.title }}
</div>
<script>
export default {
props: ["task"],
data() {
return {
inputText: this.task.title,
};
},
methods: {
editToDo() {
console.log("hhh");
this.$store.commit("EDIT_TODO", this.inputText);
},
}
};
</script>
link to github project

Pass object to component - vue js

I've tried a few different things and now must ask for some help (thanks in advance).
I'm creating a simple Vue js app where a component form emits data as a object (this works) to its parent.
the parent then runs a v-for loop and passes the Object data to a component which displays the data (this does not work).
Vue Version
($ vue --version
#vue/cli 4.4.1)
Parent code:
<template>
<div class="MEDS">
<goalForm #goal_data="goal_create($event)"/>
<section>
<div v-for="(goalItem, index) in this.goals_list_container" v-bind:key="index">
<goalItem :goal="goalItem"/>
</div>
</section>
</div>
</template>
// # is an alias to /src
import goalForm from '#/components/Goal_form.vue'
import goalItem from '#/components/Goal_item.vue'
export default {
name: 'MEDS',
components: {
goalForm,
goalItem
},
data(){
return{
// Recievs goals from goalForm on submit, this is an array of objects
goals_list_container: [],
}
},
methods: {
goal_create(payload){
this.goals_list_container.push(payload);
console.log(this.goals_list_container)
}
}
}
GOAL ITEM (that should display the goal)
<template>
<div>
<h3>{{goal.title}}</h3>
</div>
</template>
export default {
prop: ['goal'],
data(){
return {
goal: {}
}
},
watch: {
}
}
Goal_form: (EDITED)
<div>
<form action="" class="goalForm" #submit.prevent="emit_Goal">
<input type="text" name="title" id="title" v-model="title">
<input type="text" name="description" id="description" v-model="des">
<input type="date" name="dueDate" id="dueDate" v-model="dueDate">
<input type="number" name="priority" id="priority" v-model="priority">
<input type="submit" class="submit">
</form>
</div>
</template>
<script>
export default {
data() {
return {
title: null,
des: null,
dueDate: null,
priority: null,
goal_data: {}
}
},
methods: {
push_goal(){
this.goal_data.title = this.title
this.goal_data.des = this.des
this.goal_data.dueDate = this.dueDate
this.goal_data.priority = this.priority
},
emit_Goal(){
// Move goal details into Object to be Emited
this.push_goal()
// Emit the form to the parent on submit
this.$emit('goal_data', this.goal_data)
}
}
}
</script>
<style lang="scss" scoped>
form {
display: flex;
flex-direction: column;
input {
&[name="title"]{
}
}
}
</style>
The for loop seems to work as each submit of the Goal_form creates a new instance of the Goal_item component.... But it does not populate with any data.
I Am either getting this totally wrong or have missed something small but any help would be greatly appreciated -
W

Vue 3 watched property does not update

In the pen i've created, the child component does two things, it watches the props and has a method which emits two values.
The parent handling the emitted values modifies the prop and changes the component. However, the child component never fires its watch event.
See this pen
The logs:
"Emiting values up"
"Form state listener fired"
"Form state has changed"
"move listener fired"
"activeComponent has changed"
"Foo: About to unmount"
"Foo: Unmounted"
I expect between
"Form state listener fired"
"Form state has changed"
a log saying "Foo: formState changed".
How come the watcher is not firing?
I built a couple of test components in my Vue 2 CLI sandbox application, but hopefully it will translate enough to Vue 3 to help you solve your problem. Also, not sure if it is contributing to your problem, but you may want to take a look at this Event Names documentation.
Parent.vue
<template>
<div class="parent">
<h3>Parent.vue</h3>
<hr>
<child :formState="formState" #form-state-event="updateFormState" />
</div>
</template>
<script>
import Child from './Child.vue'
export default {
components: {
Child
},
data() {
return {
formState: 'Initial State'
}
},
methods: {
updateFormState(newState) {
console.log('updateFormState');
this.formState = newState;
}
}
}
</script>
Child.vue
<template>
<div class="child">
<h3>Child.vue</h3>
<div class="row">
<div class="col-md-6">
<label>Form State From Parent (Prop):</label>
<span>{{ dataFormState }}</span>
</div>
</div>
<div class="row">
<div class="col-md-6">
<button type="button" class="btn btn-sm btn-secondary" #click="changeAndEmit">Emit new form state</button>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
formState: {
type: String,
required: true
}
},
data() {
return {
dataFormState: this.formState,
clickCounter: 1
}
},
watch: {
formState(newValue) {
this.dataFormState = newValue;
}
},
methods: {
changeAndEmit() {
console.log('Emitting values up')
this.$emit("form-state-event", "New form state " + this.clickCounter++);
}
},
}
</script>
<style scoped>
label {
font-weight: bold;
margin-right: 0.5rem;
}
</style>

Vue components Event Handling

I have a component button where I use that button almost everywhere in my site.
Its an SVG button with some animation on hover. The button works fine.
<template>
<a class="button"
#mouseover="buttonEnter"
#mouseout="buttonLeave">
<svg viewBox="0 0 180 60">
<path ref="btnPath" d="..." />
</svg>
<span ref="btnSpan">
<slot>Button slot</slot>
</span>
</a>
</template>
<script>
import { buttonEnter, buttonleave } from '../assets/animate'
export default {
name: 'AnimButton',
methods: {
buttonEnter(event) {
buttonEnter(this.$refs.btnPath, this.$refs.btnSpan)
},
buttonLeave(event) {
const buttonSpan = event.currentTarget.querySelector('span')
buttonleave(this.$refs.btnPath, this.$refs.btnSpan)
},
},
}
</script>
Now I wanna use this button as a submit button in my form.The hover events are happening, but seems the #click event is not triggering.
Probably i'm missing something bad.
<form>
<input type="email">
<input type="subject">
<input type="message">
<anim-button type="submit" #click="submit">
Submit
</anim-button>
</form>
<script>
import AnimButton from '~/components/AnimButton.vue'
export default {
components: {
AnimButton,
},
methods: {
submit: function() {
console.log('submit')
},
}
</script>
You can both use #click.native like suggested in comment (see here) or put #click in your AnimButton, $emit some event and catch this event in parent component.

Dynamically change <style> inside a Single File Component

Is it possible to dynamically change the content of a scoped inside a Single File Component?
You could do it using the v-html directive.
Since I don't know your actual code I will just give you the code for a basic proof of concept.
In the template...
<template>
<div>
<head v-html="styles"></head>
<div class="test">
<p>change this paragraph</p>
</div>
<textarea name="" id="" cols="30" rows="10" v-model="csscode"> </textarea>
</div>
</template>
In the script...
<script>
export default {
data() {
return{
csscode: null,
styles: null,
}
},
watch:{
csscode(val){
this.styles = '<style>' + val + '</style>';
}
}
}
</script>
Inside style tag, no. Its not possible because of build extracts a .css file.
But as an alternative you can use javascript object as an style object.
var app = new Vue({
el: '#app',
data: function(){
return {
textAreaStyle: {border: '2px solid blue', color: 'red', width: '500px', height: '300px'}
}
},
methods: {
updateStyle (event) {
this.$set(this.$data, 'textAreaStyle', JSON.parse(event.target.value));
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>
<div id="app">
<textarea :style="textAreaStyle" #change="updateStyle">{{textAreaStyle}}</textarea>
</div>