I have two components and I am trying to save data from one component into another but somehow the event isn't triggered by the parent component. I see no errors in the code must be something else.
When I add newTask I emit "some" event but is not triggering on the parent
App.vue file
<template>
<h1>{{ message }}</h1>
<Form></Form>
<div #some="some">
<Task :tasks="tasks" />
</div>
</template>
<script>
import Form from "./components/Form.vue";
import Task from "./components/Task.vue";
export default {
components: {
Form,
Task,
},
data() {
return {
message: "This is my first taks",
tasks: [
{ status: "To do", title: "Some task not solved" },
{ status: "Done", title: "Make shopping" },
{ status: "Blocked", title: "That blocked" },
],
};
},
methods: {
some() {
console.log("event fired");
},
},
};
</script>
Form.vue
<template>
<input type="text" placeholder="new task" v-model="task" />
<button #click="addTask">Add new Task</button>
</template>
<script>
export default {
data() {
return {
task: "",
tasks: [],
};
},
methods: {
addTask() {
// console.log(this.task);
this.$emit("some", this.task);
},
},
};
</script>```
You have to add your EventListener to the component from where the event is firing. And in your code it's the Task Component. So instead of listening from the div you may code like this,
<Task :tasks="tasks" #some="some" />
Related
I'm trying to pass an array of object to a childComponent as prop but when I add an object in it, it doesn't render. (Note: I'm working on vuejs 2.6)
I suppose it has a link with the "monitoring" of the items of the array and not the array itself? Stuff is that if I do not pass the prop and use the default value instead, it's working perfectly. I think I'm missing something here. Could someone help me ?
By curiosity is this kind of behavior still stand with vue3js ?
As you can see below:
App.vue:
<template>
<div id="app">
<Card
v-for="user in users"
:key="user.userId"
:userId="user.userId"
:username="getUsernameFromUserId(user.userId)"
:links="getUserLinksFromUserId(user.userId)"
/>
</div>
</template>
<script>
import Card from "./components/Card.vue";
export default {
name: "App",
components: {
Card,
},
data: function () {
return {
users: [
{ userId: 1, name: "Bob" },
{ userId: 2, name: "Alice" },
{ userId: 3, name: "Eliot" },
],
links: [
{ userId: 1, link: "hello->world" },
{ userId: 1, link: "world->!" },
{ userId: 3, link: "hello->back" },
{ userId: 4, link: "hello->you" },
],
};
},
methods: {
getUsernameFromUserId: function (userId) {
return this.users.filter((obj) => obj.userId == userId)?.[0]?.name ?? "Not found";
},
getUserLinksFromUserId: function (userId) {
return this.links.filter((obj) => obj.userId == userId);
},
},
};
</script>
Card.vue
<template>
<div class="card">
<h1>{{ username }}</h1>
<button #click="addLink">Add One link</button><br><br>
<span v-if="links.length == 0">No links</span>
<div class="links">
<Link v-for="link in links" :key="links.indexOf(link)" :link="link"></Link>
</div>
</div>
</template>
<script>
import Link from '../components/Link'
export default {
components:{Link},
props: {
userId: Number,
username: String,
links: { type: Array, default: () => [], required: false },
},
methods:{
addLink: function(){
this.links.push({
userId: this.userId,
link: 'newlink->cool'
});
}
}
}
</script>
Link.vue
<template>
<div>
<span>UserId: {{ this.link.userId }} Link: {{ this.link.link }</span>
</div>
</template>
<script>
export default {
props: {
link: { type: Object, default: () => [], required: false },
},
};
</script>
This is a bad way to work with props
Note: do not focus on Dev Tools too much as it can be "buggy" at times - especially if you use Vue in a wrong way. Focus on your app output
Your Card.vue component is modifying (push) a prop, which is not recommended but it sort of works if the prop is object/Array and you do not replace it, just modify it's content (as you do)
But in your case, the values passed to props are actually generated by a method! The getUserLinksFromUserId method is generating a new array every time it is called, and this array is NOT reactive. So by pushing to it, your component will not re-render and what is worse, parent's links array is not changed at all! (on top of that - if App.vue ever re-renders, it will generate new arrays, pass it to pros and your modified arrys will be forgoten)
So intead of modifying links prop in Card.vue, just emit an event and do the modification in App.vue
hi friend i have a question.
i want create todo-list app with Vue an i have 3 components now:
task component is in tasks components is in app components (task is child of tasks and tasks in child of app)
i want handle delete this tasks and set reminder this tasks
i have a tasks array with some object in it that is pass from app.vue to tasks component
i have a property of reminder in objects of tasks that is have a boolean
i want delete task with handleDelete but this function has an error
but
when i write this function in app component it work!
and set reminder in tasks worked!
why handleDelete didn't work in tasks component but setReminder worked in it?
my app components:
<template>
<div class="container">
<Header title="write your task" />
<Tasks :tasks="tasks" />
</div>
</template>
<script>
import Header from "./components/Header";
import Tasks from "./components/Tasks";
export default {
name: "App",
components: {
Header,
Tasks,
},
data() {
return {
tasks: [],
};
},
methods: {
},
created() {
this.tasks = [
{
id: 1,
text: "do project",
day: "march 3rd at 1pm",
reminder: true,
},
{
id: 2,
text: "rest",
day: "march 3rd at 1pm",
reminder: false,
},
{
id: 3,
text: "sleep",
day: "march 3rd at 1pm",
reminder: true,
},
{
id: 4,
text: "running",
day: "march 3rd at 1pm",
reminder: true,
},
{
id: 5,
text: "do homework",
day: "march 3rd at 1pm",
reminder: true,
},
{
id: 6,
text: "go home",
day: "january 3rd at 1pm",
reminder: false,
},
];
},
};
</script>
my tasks component is:
<template>
<div v-for="task in tasks" :key="task.id">
<Task :task="task" :tasks="tasks"
#DeleteTask="handleDeleteTask"
#toggle-reminder ="setReminder"
/>
</div>
</template>
<script>
import Task from "./Task";
export default {
name: "Tasks",
props: {
tasks: Array,
},
components: {
Task,
},
methods: {
handleDeleteTask(id) {
console.log(this.tasks);
this.tasks = this.tasks.filter((i) => i.id !== id);
console.log(this.tasks);
},
setReminder(id) {
const index = this.tasks.findIndex((task) => task.id === id);
console.log(this.tasks[index].reminder);
this.tasks[index].reminder = !this.tasks[index].reminder;
console.log(this.tasks[index].reminder);
},
},
};
</script>
an task component is :
<template>
<div
:class="[task.reminder ? 'reminder' : '', 'task']"
#dblclick="$emit('toggle-reminder' , task.id)"
>
<h3>
{{ task.text }}
<i class="fas fa-times" #click="this.$emit('DeleteTask', id);"></i>
</h3>
<p>{{ task.day }}</p>
</div>
</template>
<script>
export default {
name: "Task",
props: {
task: Object,
tasks: Array,
},
};
</script>
task.id
here you are passing id directly tho it should be task.id
//From
<i class="fas fa-times" #click="this.$emit('DeleteTask', id);"></i>
//To
<i class="fas fa-times" #click="this.$emit('DeleteTask', task.id);"></i>
no prop mutation
you shouldnt change prop values , any variable passed by prop shouldnt be modified , its an image of the original variable in the parent compopent , you should set up new emits in you tasks.vue -> app.vue the same way you did with task.vue -> tasks.vue
check this working sandbox
naming convention
the browser will get confused about #DeleteTask (event listener in you tasks component ) and wont link it to the event since its the sae as #deletetask for him
you already named the other one in kebab-case thats why its working
change
#click="this.$emit('DeleteTask', id);"
To #click="this.$emit('delete-task', id);"
and
#DeleteTask
To #delete-task;
Naming conventions from the Vue docs say to always name events with
kebab-case. The reason for this is that when Vue compiles its HTML,
HTML does not differentiate between upper or lowercase letters
I am attempting to add tiptap (https://github.com/scrumpy/tiptap) to a form textarea field.
I can't get this to work. I get no errors, but the form element does not display.
Here is the component:
<template>
...
<div class="field">
<label>Body</label>
<editor-content :editor="editor" />
</div>
...
<script>
...
import { Editor, EditorContent } from 'tiptap'
...
components: {
Notification,
EditorContent
},
data () {
return {
...
editor: null
}
},
mounted() {
this.editor = new Editor({
content: '<textarea name="piece_body" v-model="piece_body" rows="20" placeholder="Start composing here..."></textarea>'
})
},
beforeDestroy() {
this.editor.destroy()
},
</script
>
For some reason I can't perform actions after the sortable elements are moved. The event UIkit.util.on('#sortable', 'moved', function (item) {}); is not being called/triggered.
The app is made with vue.js, but I get no error and every other uikit functionality works just fine. There are ~600 line of code so, I'll just show the ones that matter (I think).
<template>
<div class="products">
<MainMenu />
<div id="ordering" uk-offcanvas="overlay: true">
<div class="uk-offcanvas-bar" style="width:500px">
<button class="uk-offcanvas-close" type="button" uk-close></button>
<div>Drag to re-arrange the fields order<br><br>
<ul id="sortable" class="uk-grid-small uk-child-width-1-1 uk-text-center" uk-sortable="handle: .uk-card" uk-grid>
<li v-for="(data, idx) in computedData" :key="idx" :id="data.product_field_name">
<div class="uk-card uk-card-default uk-card-body uk-padding-small">{{data.product_field_name}}</div>
</li>
</ul>
</div>
</div>
</div>
<button type="button" class="uk-button uk-button-default" uk-toggle="target: #ordering"><span uk-icon="move"></span> Field Ordering</button>
<!-- REST OF HTML GOES HERE: FORMS, MODALS, ETC. -->
</div>
</template>
<script>
UIkit.notification('test message','danger'); //THIS TRIGGERS OK
//UIKit ordering action - THIS DOES NOT
UIkit.util.on('#sortable', 'moved', function (item) {
console.log('moved triggered');
});
// # is an alias to /src
import MainMenu from "#/components/MainMenuEMVO.vue";
import axios from "axios";
export default {
name: "products",
components: {
MainMenu
},
data() {
return {
add_dependency: {
field: "",
field_selection: ""
},
remove_dependency: {
id: "",
field_name: "",
dependency_field: "",
dependency_rule: "",
dependency_value: "",
enforcing_value: "",
},
productFields: "",
lookupTables: "",
dependencies: "",
departments: "",
search: "",
computedFields: "",
};
},
mounted() {
let stateCheck = setInterval(() => {
if (document.readyState === 'complete') {
clearInterval(stateCheck);
this.getProductsConfig();
}
}, 100);
},
computed: {...},
methods: {
getProductsConfig(){...},
enableEdit(id){...},
cancelEdit(id){...},
submitEdit(id){...},
addRule(id, field_name){...},
removeDependency(fieldName, id){...}
}
};
</script>
As mentioned before there are no errors or even warnings in console, so I really don't event know where to start looking at this.
I managed to resolve the problem, so if anyone has the same issue:
Once I moved the code into the mounted() method of the vue.js it all works fine.
mounted() {
//UIKit ordering action - THIS DOES NOT
UIkit.util.on('#sortable', 'moved', function (item) {
console.log('moved triggered');
});
let stateCheck = setInterval(() => {
if (document.readyState === 'complete') {
clearInterval(stateCheck);
this.getProductsConfig();
}
}, 100);
},
the UIkit.notification works fine, but I guess since the util is attached to event it has to be made available to vue on startup.
If you added UIkit as <script src="./src/js/uikit.min.js"></script> in your index.html then you can't use it inside Vue as expected, instead do:
npm i --save uikit
then in your Component
import UIkit from "uikit";
after that you can use everywhere
methods: {
alertOnClick(msg) {
UIkit.notification(msg, "danger");
}
}
}
I have been able to accomplish a single level deep of v-model two-way binding on a custom component, but need to take it one level deeper.
Current working code:
<template lang="html">
<div class="email-edit">
<input ref="email" :value="value.email" #input="updateInput()"/>
<input ref="body" :value="value.body" #input="updateInput()"/>
</div>
</template>
<script type="text/javascript">
import LineEditor from './LineEditor.vue'
export default {
components: {
LineEditor
},
computed: {
},
methods: {
updateInput: function(){
this.$emit('input',{
email: this.$refs.email.value,
body: this.$refs.body.value
})
}
},
data: function(){
return {}
},
props: {
value: {
default: {
email: "",
body: ""
},
type:Object
}
}
}
</script>
Used like this: <email-edit-input v-model="emailModel" />
However, if I add this piece, the value no longer propagates upwards:
<div class="email-edit">
<line-editor ref="email" :title="'Email'" :value="value.email" #input="updateInput()"/>
<input ref="body" :value="value.body" #input="updateInput()"/>
</div>
</template>
<script type="text/javascript">
import LineEditor from './LineEditor.vue'
export default {
components: {
LineEditor
},
computed: {
},
methods: {
updateInput: function(){
this.$emit('input',{
email: this.$refs.email.value,
body: this.$refs.body.value
})
}
},
data: function(){
return {}
},
props: {
value: {
default: {
email: "",
body: ""
},
type:Object
}
}
}
</script>
Using this second custom component:
<template lang="html">
<div class="line-edit">
<div class="line-edit__title">{{title}}</div>
<input class="line-edit__input" ref="textInput" type="text" :value="value" #input="updateInput()" />
</div>
</template>
<script type="text/javascript">
export default {
components: {
},
computed: {
},
methods: {
updateInput: function(){
this.$emit('input', this.$refs.textInput.value)
}
},
data: function(){
return {}
},
props: {
title:{
default:"",
type:String
},
value: {
default: "",
type: String
}
}
}
</script>
The first code-block works fine with just an input. However, using two custom components does not seem to bubble up through both components, only the LineEditor. How do I get these values to bubble up through all custom components, regardless of nesting?
I've updated your code a bit to handle using v-model on your components so that you can pass values down the tree and also back up the tree. I also added watchers to your components so that if you should update the email object value from outside the email editor component, the updates will be reflected in the component.
console.clear()
const LineEditor = {
template:`
<div class="line-edit">
<div class="line-edit__title">{{title}}</div>
<input class="line-edit__input" type="text" v-model="email" #input="$emit('input',email)" />
</div>
`,
watch:{
value(newValue){
this.email = newValue
}
},
data: function(){
return {
email: this.value
}
},
props: {
title:{
default:"",
type:String
},
value: {
default: "",
type: String
}
}
}
const EmailEditor = {
components: {
LineEditor
},
template:`
<div class="email-edit">
<line-editor :title="'Email'" v-model="email" #input="updateInput"/>
<input :value="value.body" v-model="body" #input="updateInput"/>
</div>
`,
watch:{
value(newValue){console.log(newValue)
this.email = newValue.email
this.body = newValue.body
}
},
methods: {
updateInput: function(value){
this.$emit('input', {
email: this.email,
body: this.body
})
},
},
data: function(){
return {
email: this.value.email,
body: this.value.body
}
},
props: {
value: {
default: {
email: "",
body: ""
},
type: Object
}
}
}
new Vue({
el:"#app",
data:{
email: {}
},
components:{
EmailEditor
}
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="app">
<email-editor v-model="email"></email-editor>
<div>
{{email}}
</div>
<button #click="email={email:'testing#email', body: 'testing body' }">change</button>
</div>
In the example above, entering values in the inputs updates the parent. Additionally I added a button that changes the parent's value to simulate the value changing outside the component and the changes being reflected in the components.
There is no real reason to use refs at all for this code.
In my case, having the passthrough manually done on both components did not work. However, replacing my first custom component with this did:
<line-editor ref="email" :title="'Email'" v-model="value.email"/>
<input ref="body" :value="value.body" #input="updateInput()"/>
Using only v-model in the first component and then allowing the second custom component to emit upwards did the trick.