v-show not working with props - vue.js

I am trying to hide or show button using props.
Here is the code
View (Blade)
<product-form-component savebutton="false" updatebutton="false"></product-form-component>
Component template
<template>
<div class="form-actions text-right col-md-12">
<button v-show="showsavebutton" class="btn btn-primary">Save</button>
<button v-show="updatemode && showupdatebutton" class="btn btn- primary">Update</button>
</div>
</template>
Javascript
export default {
props: ['showupdatebutton', 'showsavebutton', 'modalid']
}

Two points:
The props you are passing don't work the way you think they do; and
You have to create data variables (or props) in the component with the names you are using in the v-show.
Passing props
When you pass like:
<product-form-component savebutton="false" updatebutton="false"></product-form-component>
inside the component, the savebutton and updatebutton properties will be strings. In the example above, they won't be the boolean false, they will be the string "false".
To bind them to different values, use v-bind:propname or its shorthand :propname:
<product-form-component :savebutton="false" :updatebutton="false"></product-form-component>
That way, inside the component, those properties will really have the value false.
Variables inside component and v-show
The variables you use in the v-shows:
<button v-show="showsavebutton" ...
<button v-show="updatemode && showupdatebutton" ...
Don't exist in your component. You have to create data variables (or props) in the component with the names you are using in the v-show.
Considering you already have some props declared, here's an example of declaring those v-show variables in the data() using the props as initial value:
Vue.component('product-form-component', {
template: "#pfc",
props: ['updatebutton', 'savebutton', 'modalid'],
data() {
return {
updatemode: this.updatebutton, // initialized using props
showupdatebutton: this.updatebutton,
showsavebutton: this.savebutton
}
}
})
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
}
})
<script src="https://unpkg.com/vue"></script>
<template id="pfc">
<div class="form-actions text-right col-md-12">
<button v-show="showsavebutton" class="btn btn-primary">Save</button>
<button v-show="updatemode && showupdatebutton" class="btn btn- primary">Update</button>
</div>
</template>
<div id="app">
<p>{{ message }}</p>
<product-form-component :savebutton="true" :updatebutton="true"></product-form-component>
</div>

Props as passed down to child with the bind syntax :, so in your case you forgot to add it:
try:
<product-form-component :savebutton="false" :updatebutton="false"></product-form-component>

Related

VueJS: How can I pass params into a component?

I am new to vueJS.
What I want to do is passing parameters to a component, depending on the selection of the routes. Here is my App.vue:
<template>
<div id="main">
<header>
<h1 style="color:red">{{msg}}</h1>
</header>
<div>
<aside class="sidebar">
<router-link v-for="el in this.$router.options.routes" :to="el">
{{el.name}}
</router-link>
</aside>
<SubMenu></SubMenu>
<div class="content">
<router-view></router-view>
</div>
</div>
</div>
</template>
<script>
import SubMenu from './components/SubMenu.vue'
export default {
components: {
'SubMenu': SubMenu
},
data() {
return {
msg: 'Welcome to Your Vue.js App' }
}
}
</script>
<style>
#import 'style.css';
#import 'grid.css';
</style>
and the SubMenu component I would like to make dynamic:
<template>
<div>
something dynamic
</div>
</template>
How can I pass some parameters to use in the component?
thank you
Your App.vue can be like this:
<template>
<div id="main">
<header>
<h1 style="color:red">{{msg}}</h1>
</header>
<div>
<aside class="sidebar">
<router-link v-for="el in this.$router.options.routes" :to="el">
{{el.name}}
</router-link>
</aside>
<SubMenu :menuTitle="subMenuTitle"></SubMenu>
<div class="content">
<router-view></router-view>
</div>
</div>
</div>
</template>
<script>
import SubMenu from './components/SubMenu.vue';
export default {
components: {
SubMenu
},
data() {
return {
subMenuTitle: "This is the sub menu",
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>
<style>
#import 'style.css';
#import 'grid.css';
</style>
The SubMenu.vue component could be like this:
<template>
<div>
<h2>{{ menuTitle }}</h2>
something dynamic
</div>
</template>
<script>
export default {
name: "SubMenu",
props: {
menuTitle: String,
}
}
</script>
In the SubMenu component that was used in App.vue, notice the colon that appears before the menuTitle attribute. When you do that before an attribute, the value of that attribute would be evaluated by Vue and passed to the component. You can pass literal Javascript expressions or items in your App.vue component.
In the SubMenu component, you can use the props in whatever way you can. If the prop's value is an array, you can use the v-for directive with it to create a list of items in the SubMenu.
Welcome to SO,
In Vue.js passing parameters to components is called "Props"
You can pass props to your SubMenu like below
<SubMenu :id="12345" someText="Some Text About Something" :dataArray="[1,2,3,4,5]" />
then inside your SubMenu component you can define Prop Types as below
props: ['dataArray']
or
props: {
dataArray: {
type: Array,
default: []
}
}
After that you can use the data you passed to your liking
You can also read up on this Vue Documentation regarding the Props, which has much more detailed explanations about various Props related stuff and sample code
Ok many thanks to both.
But what if I would like to pass something that depends on the voices in router-link? I mean, router-link prints a menu with 4 voices...what if I would like a behavior like this:
click on voice1 in router-link ---> pass this object ['input1', 'input2'] to SubMenu
click on voice2 in router-link ---> pass this other object ['input3', 'input4', 'input5'] to SubMenu
and so on.
thanks again :)

Vue - display/create component from a function

One thing that I have been struggling to figure out how to do better is modals. Currently, I am registering the modal component on each Vue that needs it. However, this feels rather sloppy, as I am having to register the component several times. Even using mix-ins just does not feel like an elegant solution. What would be optimal to be able to do is to mimic JavaScript's alert() method on the Vue instance. For example, be able to call this.ShowModal(Header, Body)
However, from my understanding, there is no way to accomplish this
Take for example my Modal example. You could have a modal template like this:
<script type="text/x-template" id="modal-template">
<transition name="modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">
default header
</slot>
</div>
<div class="modal-body">
<slot>
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
default footer
<button class="modal-default-button" #click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</script>
Then you would have to reference the component over and over again like this
<template>
<button #click="displayModal">Display the Modal Alert</button>
<modal v-if="showModal" #close="showModal = false">
<h3 slot="header"> This is a good header </h3>
<p>
Look at me I am the body! You have seen me {{displayCount}} times!
</p>
</modal>
</template>
<script>
components: {modal},
data: {
showModal: false,
displayCount: 0
},
methods: {
displayModal(){
this.displayCount++
this.showModal = true;
}
}
</script>
If you wanted to reuse the component for several messages from within the parent you would then have to add several more variables to store things such as the header and body. You could put some of the logic into a mixin but you would still have to have the clutter of adding the modal component and possibly the mixin.
This brings us to the question. Is there a way to create a function in the Vue instance that would allow for us to dynamically create a Modal component and fill in the slots with arguments passed to the function? e.g. this.ShowModal("This is a good header", "Look at me I am the body!")
Use Vue.extend() create a "modal" constructor and create a instance,you can mount it to DOM dynamically by $mount or other ways
In Modal example:
modal.vue:
<template>
<div>
{{message}} //your modal content
</div>
</template>
<script>
export default {
name: 'modal',
data(){
return {
message: '',
}
},
methods:{
/************/
close () {
/****this.$destroy()****/
}
}
}
</script>
modal.js:
import myModal from 'modal.vue'
import Vue from 'vue'
const modalConstructor = Vue.extend(myModal)
const modal = (options,DOM)=>{
const modalInstance = new modalConstructor({data:options})
modalInstance.vm = modalInstance.$mount() //get vm
const dom = DOM || document.body // default DOM is body
dom.appendChild(modalInstance.vm.$el) // mount to DOM
return modalInstance.vm
}
export default modal
now you can create a Modal component by a function like this:
import showModal from 'modal.js'
showModal({message:"..."})

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.

Adding a component by clicking a button

Vue.component('component-a', {
template: '<h3>Hello world!</h3>'
})
new Vue({
el: "#app",
data: {
arr: []
},
methods: {
add(){
this.arr.push('component-a');
console.dir(this.arr)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<component-a></component-a>
<hr>
<button #click="add">Add a component</button>
<ul>
<li v-for="component in arr"> {{ component }} </li>
</ul>
</div>
I want to insert a component a lot of times to the page by clicking a butoon, but instead of this only a component`s name is inserted. How to add a component itself?
In your code the double curly braces do not reference the component itself but just the string you added with this.arr.push('component-a'); hence just the string being displayed.
If you would like this string to call the actual component you could use dynamic components.
Replacing {{ component }} with <component :is="component"/> would achieve the effect I think you're looking for.
However if you're only going to be adding one type of component I would consider adding the v-for to the component tag itself like so:
<component-a v-for="component in arr/>
Use the component element to render your component dynamically.
The usage is very simple: <component :is="yourComponentName"></component>
The ":is" property is required, it takes a string (or a component definition).
Vue will then take that provided string and tries to render that component. Of course the provided component needs to be registered first.
All you have to do is to add the component tag as a child element of your list tag:
<li v-for="component in arr">
<component :is="component"></component>
</li>
Vue.component('component-a', {
template: '<h3>Hello world!</h3>'
})
new Vue({
el: "#app",
data: {
arr: []
},
methods: {
add() {
this.arr.push('component-a');
console.dir(this.arr)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<component-a></component-a>
<hr>
<button #click="add">Add a component</button>
<ul>
<li v-for="component in arr">
<component :is="component"></component>
</li>
</ul>
</div>

How to point to wrapping custom element with v-model?

I currently have a custom vue-form component:
In my common HTML it kind of looks something like this:
<vue-form>
<input type="text" v-model="test">
<input type="password" v-model="test2">
</vue-form>
Note that this is not a Vue template.
My v-model keeps pointing to my root component when in this case I would like my v-model to point the the actual component that is wrapping my content.
For the vue-form component I am simply using a slot like so:
<template>
<div>
<slot></slot>
</div>
</template>
Is there a way to get the v-model binding to point towards the wrapping component instead of the root element?
You can use Scoped Slots documentation.
Edit:
Here is working example:
<div id="app">
<vue-form v-bind:model="x">
<template scope="props">
<input type="text" v-model="props.model.test">
<input type="password" v-model="props.model.test2">
</template>
</vue-form>
</div>
new Vue({
el: '#app',
data: function() {
return {
x: {
test: 'John',
test2: 'Smith'
}
}
},
components: {
'vue-form': {
template: `<div><slot :model="model"></slot></div>`,
props: ['model']
}
}});
jsfiddle
Just notice that this solution requires from you to propagate property from component to slot by :model="model" part in slot declaration.