VueJS pass custom property to child component via property value - vue.js

I have a component named controls:
<li class="controls__item" v-if="options[0].save == 'show'">
<button class="btn" :options[0].saveAttr>Save</button>
</li>
I'm having trouble rendering an attribute defined in the options property:
<controls :options='[{ save: "show", saveAttr: "sampleAttr='0' "}]'></controls>
This is what I'm trying to achieve:
<button class="btn" sampleAttr='0'>Save</button>

That's not the correct syntax for binding in Vue.
If the name of the attribute to bind to is never going to change, you should specify the name in the controls component:
<li class="controls__item" v-if="options[0].save == 'show'">
<button class="btn" :sampleAttr="options[0].saveAttr">Save</button>
</li>
And just change the options to pass in a value for saveAttr:
<controls :options='[{ save: "show", saveAttr: "0" }]'></controls>
If the name of the attribute (or the number of attributes) could change, then you should pass an object to the v-bind directive like so:
<li class="controls__item" v-if="options[0].save == 'show'">
<button class="btn" v-bind="options[0].saveAttrs">Save</button>
</li>
And then pass in an object for saveAttrs:
<controls :options='[{save : "show", saveAttrs: { sampleAttr: 0 }]'></controls>

Let's start with your testdata (just a little clean up) let's say you have two buttons since it seems like you want to do that later on. I'm not yet sure what the save : "show" is supposed to do - so I do my best to give a flexible example.
[{
'text': 'Save',
'click': function() { alert('save'); }
,{
'text': 'Delete',
'click': function() { alert('delete'); }
}]
Not lets say you have that testdata in your component called "controls"
<controls :options="[{'text': 'Save','click': function() { alert('save'); },{'text': 'Delete','click': function() { alert('delete'); }}]"> </controls>
As we can see your controls has an property called options. So your code for your component should look like:
<template>
<div class="controls">
<li class="controls__item" v-for="control in options">
<button class="btn" #click="control.click">{{ control.text }}</button>
</li>
</div>
</template>
<script>
export default {
props: ['options']
}
</script>
You need to define the prop you want to bind on the component (options). Options is now bound according to our test date. Since it's an array we can use v-for to loop through it. We then bind the given text as button content and the given click function as on click event.
I hope this helps.

Related

How do you pass a prop inside a function inside a template in vue.js?

I am trying to pass a prop inside a function inside a template for a to-do test site I'm making. Basically I want to have a list item which includes the todo item with a button next to it that deletes the same item.
Vue.component("todo-item", {
props: ["todotext"],
template: "<li>{{todotext.text}} <button v-on:click='removeThisItem({{todotext}})'>X</button></li>",
})
var next_id = 3
var app = new Vue ({
el: "#app",
data: {
message: "",
todos: [
{id: 0, text: "Do assignment"},
]
},
methods: {
addTodoItem: function () {
this.todos.push({id: next_id, text: this.message})
next_id += 1
},
removeThisItem: function removeThisItem (item) {
this.todos.splice(this.todos.indexOf(item))
}
}
})
and the HTML
<div id="app">
<input type="text" name="" v-model="message">
<button type="button" name="button" v-on:click="addTodoItem">Add Todo Item</button>
<ul>
<todo-item
v-for="todo in todos"
v-bind:todotext="todo"
v-bind:key="todo.id">
</todo-item>
</ul>
</div>
However I get the error
invalid expression: Unexpected token '{' in removeThisItem({{todotext}})
Is there a way to pass the prop as an argument inside this function inside this template to be able to delete this list item?
Edit: Here is the JSFiddle: https://jsfiddle.net/f6sn52w8/
Thanks!
Well, trying to solve the issue in your jsfiddle I got the error
[Vue warn]: Property or method "outerHTML" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option. (found in <TodoItem>)
Anyway, I figure it out what is happening with your code and why it isn't working.
You have the parent component where you are using your todo-item component:
<!-- parent component -->
<div id="app">
<input type="text" name="" v-model="message">
<button type="button" name="button" v-on:click="addTodoItem">
Add Todo Item
</button>
<ul>
<todo-item
v-for="todo in todos"
v-bind:todotext="todo"
v-bind:key="todo.id">
</todo-item>
</ul>
</div>
The method removeThisItem is declared in this component, so it isn't available in the child component <todo-item>, that's why you see the error in the console.
So the way to handle the click to remove the item is by listening for an event in the parent component and emitting the event from the child component:
Note about shorthand: v-bind:todotext="todo" is the same as :todotext="todo", and v-on:click is the same as #click
<!-- parent component -->
<div id="app">
<input type="text" name="" v-model="message">
<button type="button" name="button" v-on:click="addTodoItem">
Add Todo Item
</button>
<ul>
<todo-item
v-for="todo in todos"
:todotext="todo"
:key="todo.id"
#removeItem="removeThisItem"> <!-- listen for the removeItem event and run the removeThisItem method when it's triggered -->
</todo-item>
</ul>
</div>
Now the child component template must be updated:
Vue.component("todo-item", {
props: ["todotext"],
template:
`<li>
{{todotext.text}}
<button #click="$emit('removeItem', todotext)">X</button>
</li>`,
})
The todo-item component will emit the event removeItem when the button is clicked, and will send todotext prop as parameter to the function that will run on the parent (removeThisItem).
An alternative way to explain better this behavior:
Vue.component("todo-item", {
props: ["todotext"],
template:
`<li>
{{todotext.text}}
<button #click="emitEventRemoveItem">X</button>
</li>`,
methods: {
emitEventRemoveItem() {
// this.$emit will emit an event to the parent
// the first parameter is the event name, the second parameter
// is the argument that is expected in the parent method that
// will run when the event is triggered, removeThisItem in this case
this.$emit('removeItem', this.todotext);
}
}
})
Try to run this in your editor, in jsfiddle I got an error. Anyway, the issue is that you're trying to run a method that is declared in the parent component from the child component.
Let me know if it works or if you get any error.

How do I fit the value of one v-model into another v-model?

In this example, I'm trying to fit the value from div id="message" into textarea using the Vue v-model construct, but this not work
<template>
<div>
<textarea v-model="text"></textarea>
</div>
<div>
<div id="message" v-model="text2">{{ comment.message }}</div>
<button #click="update(text2);">
Edit
</button>
</div>
</template>
<script>
export default {
data() {
return {
text: [],
text2: null
}
},
methods: {
/* not work */
update(text2) {
this.text = text2;
}
}
<script>
How do I make sure that when I click on the "edit" button, the value of v-model="text2" insert into v-model="text" ?
You cannot use v-model on a <div> because it isn't an input element.
It seems what you want to do is set text to the comment message when you click the edit button so that it can be edited by the textarea. All you have to do is pass comment.message as the argument:
<button #click="update(comment.message)">
A couple of other things:
You cannot have multiple root elements in your template (you have two root <div> elements). You can just wrap everything in a single <div>.
text has initial value [] which isn't compatible with a textarea's v-model; did you mean ''?

vue-drag-drop: context on target of drop event

This is probably a really naive question that is less about vue-drag-drop and more about vuejs, which I'm new to.
If I have two lists of stuff:
<ul>
<li v-for="thing in thing">
<drag :transfer-data="{ thing }">
<span>{{ thing.title }}</span>
</drag>
</li>
</ul>
<ul>
<li v-for="day in days">
<drop #drop="handleDrop" ref="day"></drop?
</li>
</ul>
In the handleDrop() method I can see the event, which include what was dragged into the list item, but I don't see how I have any context on which item in the array the dragged thing was dragged into. I tried using a ref on the drop element, but that didn't seem to be what I wanted.
How do I know which day the item was dragged into?
I figured out I need to pass the data myself. One way to do this is to wrap the function vue-drag-drop provides.
<ul>
<li v-for="day in days">
<drop #drop="function(data, event) { handleDrop(data, day, event); }" ref="day"></drop>
</li>
</ul>
That seems to work as expected, but I'm wondering if there's another way to do it like using a library attribute (similar to ref).
Maybe I missed something, but wouldn't this in handleDrop() be the component that was dropped onto? That's how it is for other event handlers in Vue.
See here for an example:
<div id="example-2">
<!-- `greet` is the name of a method defined below -->
<button v-on:click="greet">Greet</button>
</div>
var example2 = new Vue({
el: '#example-2',
data: {
name: 'Vue.js'
},
// define methods under the `methods` object
methods: {
greet: function (event) {
// `this` inside methods points to the Vue instance
alert('Hello ' + this.name + '!')
// `event` is the native DOM event
if (event) {
alert(event.target.tagName)
}
}
}
})
// you can invoke methods in JavaScript too
example2.greet() // => 'Hello Vue.js!'
Update
The above is true, but you want to know which day is that drop component. To achieve that, pass it as a prop to the child drop component like so:
Vue.component('drop', {
props: ['day'],
// ...
})
<ul>
<li v-for="day in days">
<drop #drop="handleDrop" ref="day" :day="day"></drop>
</li>
</ul>
The :day="day" is the key. Then, in handleDrop(), this.day will be the day

How to get selected row index on #change event of child Select Dropdown in Element UI?

I'm generating a list of multiple input elements in which one is <el-select>. On changing this select menu, I'm getting value bounded with that select. But here, question is, I also want to get index/row number of the parent v-for items.
You can understand what I meant from the following code:
<el-form-item v-for="(domain, index) in Prescription.domains">
<div class="col-md-2" v-if="medicineIsSelected === true">
<small>Select Brand:</small>
<el-select v-model="domain.SelectedBrand" clearable placeholder="Select" #change="updateDropdowns"> <!-- Here I want to pass {{index}} also -->
<el-option
v-for="item in selectedMedicineMetaInfo.BrandName"
:key="item.value.name"
:label="item.value.name"
:value="item.value.rxcui">
</el-option>
</el-select>
</div>
</el-form-item>
As you can see from above code, I want to pass index in updateDropdowns.
I tried passing updateDropdowns(index), here I got the index number but lost the selected value of that dropdown. How can I pass both?
Here is the solution :
#change="updateDropdowns(index, $event)"
you can use $event to reference current event.
you can use custom event to manage this
<div>
<child #clicked="ongetChild"></child>
</div>
== child ==
watch your dropdown changes
export default {
watch: {
'domain.SelectedBrand':function(value) {
this.$emit('clicked', 'value')
}
}}
=== parent ==
export default {
ongetChild (value) {
console.log(value) // someValue
}
}
}
Use #change="updateDropdowns" in el-select tag
Use vue method:
updateDropdowns(index) {
this.selectedMedicineMetaInfo.BrandName[index-1]; // this gives selected option details
}

How to make event only true for one element in Vue?

I have vue event attached to elements that are looped.
I'm having challenge trying display CRUD action on an item, instead, all the looped items display their individual CRUD
How can I make it unique to an element? any vue event modifier for this?
Below is my code
<i class="material-icons">list</i>
<div v-if="showButtons">
<ul>
<li>Edit</li>
<li>Delete</li>
<li>Stop</li>
</ul>
</div>
The showIcons method below
showIcons: function () {
this.showButtons = true
}
since you are binding showButtons property to all your looped items, when you mouse over an item theshowButtonsis toggled true and all the items bound to showButtons are displayed.
So you need to use a unique identifier to decide whether the buttons for an item should be displayed or not.
You might be looping using v-for so you can make use of index.
template
<div v-for="(item , index)">
<i class="material-icons">list</i>
<div v-if="currentlyShowing === index">
<ul>
<li>Edit</li>
<li>Delete</li>
<li>Stop</li>
</ul>
</div>
</div>
script
data(){
return{
currentlyShowing: null
}
},
methods:{
showIcons: function (index) {
this.showButtons = true
this.currentlyShowing = index;
}
}