How can i pass a property to a directive on vue js? - javascript

how can i get the property value on a v-show or v-if directive? i've already try to pass like the example bellow but not succeed.
v-show="cabin >= {{ number }}" number="5"
i'm stuck with this and since vue.js it's kind of new its so hard to find documentation and examples.

If you're using Vue Components, then you could do something like this:
Vue.Component('my-comp', {
template: '#my-template',
props: [
'number',
],
data: function(){
return{
cabin: 4
};
}
}):
and then in your view, use it like this:
<my-comp v-show="cabin >= number" number="5"></my-comp>
<template id="my-template">
<div>Lorem Ipsum</div>
</template>

Once you used a custom attribute (number), I guess that you used a component.
So, as he said #user3324298, you need something like this:
Vue.Component('my-comp', {
template: '#my-template',
props: ['number'],
data: function() {
return {
cabin: 4
}
}
})
But the template, should be something like this:
<template id="my-template">
<div v-show="cabin >= number" number="5">
<div>Lorem Ipsum</div>
</div>
</template>
<my-comp></my-comp>
v-show should be into the scope of component.

Related

Vue js render text with html content

What i'm trying to do is have a root element with a prop that holds inner html like: hello<b>hey</b>
but i can't use v-html because this element also has children for example:
<template>
<component :is="element.tag" contenteditable="true">
<div contenteditable="false">
<span class="delete-obj" :id="'delete'+element.id" >delete</span>
</div>
<RenderString :string="element.content" />
</component>
</template>
<script>
import Vue from "vue";
Vue.component("RenderString", {
props: {
string: {
required: true,
type: String
}
},
render(h) {
const render = {
template: this.string ,
methods: {
markComplete() {
console.log('the method called')
}
}
}
return h(render)
}
})
export default {
name: "VElement",
props: {
element: {
required: false,
default: null
},
},
}
</script>
I have tried the above and I have tried using slots. I can solve it with vanilla JavaScript like element.innerText, but I don't want to. The main goal is that the element is editable when they type they are editing element.content that will be rendered and the div that's inside it is normal HTML that I also need.
The main problem is that the inner HTML that I want doesn't have a root element.
The element is something like:
{id:1,tag:"div",content:"hello<b>hey</b>"}
I want the final result to be:
<div contenteditable="true">
<div contenteditable="false">
<span class="delete-obj" :id="'delete'+element.id" >delete</span>
</div>
hello<b>hey</b>
<div>
And I want to edit hello<b>hey</b> when I click inside I don't want it wrapped in anything else and if I put v-html on the outer div the inner div is gone.
If you really aren't able to wrap the content in a parent element, maybe a good approach is to write a VUE directive that render the text.
JS FIDDLE FULL DEMO
//DIRECTIVE
Vue.directive('string', {
inserted(el, bind) {
/**
* Here you can manipulate the element as you need.
*/
el.insertAdjacentText('beforeend', bind.value);
}
});
//TEMPLATE
<template>
<component :is="element.tag" contenteditable="true" v-string="element.content">
<div contenteditable="false">
<span>delete</span>
</div>
</component>
</template>
Like Seblor said, v-html will work with nested html strings.
Simply replacing the RenderString component with a <div v-html="element.content"/> should get you what you want.
Tested with the given example of hello<b>hey</b>:
Vue.component('VElement', {
name: "VElement",
props: {
element: {
required: false,
default: null
},
},
template: '\
<component :is="element.tag" contenteditable="true">\
<div contenteditable="false">\
<span class="delete-obj" :id="\'delete\'+element.id">delete</span>\
</div>\
<div v-html="element.content"/>\
</component>'
})
new Vue({
el: '#app'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<v-element :element="{id:1, tag:'div', content:'hello<b>hey</b>'}" />
</div>

vue.js and slot in attribute

I'm trying to build a vue.js template that implements following:
<MyComponent></MyComponent> generates <div class="a"></div>
<MyComponent>b</MyComponent> generates <div class="a" data-text="b"></div>.
Is such a thing possible?
EDIT
Here is the best I can reach:
props: {
text: {
type: [Boolean, String],
default: false
}
},
and template
<template>
<div :class="classes()" :data-text="text">
<slot v-bind:text="text"></slot>
</div>
</template>
but the binding does not work, text always contains false.
You can use the mounted() method to get text using $slot.default property of the component to get the enclosing text. Create a text field in data and update inside mounted() method like this :
Vue.component('mycomponent', {
data: () => ({
text: ""
}),
template: '<div class="a" :data-text=text></div>',
mounted(){
let slot = this.$slots.default[0];
this.text=slot.text;
}
});
Note: It will only work for text, not for Html tags or components.
You're mixing slots and properties here. You'll have to pass whatever you want to end up as your data-text attribute as a prop to your component.
<MyComponent text="'b'"></MyComponent>
And in your template you can remove the slot
<template>
<div :class="classes()" :data-text="text"></div>
</template>
Another thing: it looks like your binding your classes via a method. This could be done via computed properties, take a look if you're not familiar.
You can try this.
<template>
<div :class="classes()">
<slot name="body" v-bind:text="text" v-if="hasDefaultSlot">
</slot>
</div>
</template>
computed: {
hasDefaultSlot() {
console.log(this)
return this.$scopedSlots.hasOwnProperty("body");
},
}
Calling
<MyComponent>
<template v-slot:body="props">
b
</template>
</MyComponent>

v-model on input change is heavy on performance

So I have a page rendering a v-list based on an array like so :
<v-list-tile v-for="item in array">
{{item}}
</v-list-tile>
and a dialog with a v-text-field :
<v-dialog>
<v-text-field v-model="myInput">
</v-text-field>
</v-dialog>
For now it's pretty normal.
But with a performance test, I saw that for every event triggered by a change on myInput model (like a key press) the v-for is also triggered re-rendering the list when they are actually not related.
On my huge array, it's a serious problem and make the UI really laggy. I think it's a normal behavior for a vuejs application, but I was wondering if I could precisely tell wish element to check for re-rendering.
I tried some v-if statements but it didn't do the trick.
I hope that there is an answer to that, i guess i'm missing something.
If you want to test what i'm talking about here is a ready to go html file, please debug it with your debug console, you will see a [vue warn] message of the duplicated key attesting of the fact that the v-for is indeed called for every key press.
Imagine now if the array (here items) is way bigger than that, and wrapped into complex components, making that call is just too heavy on performance when we are just aiming to change the "myInput" value.
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{data}}
<ul>
<li v-for="item in items" :key="item">
{{ item.message }}
</li>
</ul>
<input v-model="data"></input>
</div>
</body>
<script>
new Vue({
el: '#app',
data: () => ({
data: '',
items: [{
message: 'Foo'
},
{
message: 'Bar'
}
]
})
})
</script>
</html>
Here's a codepen showing the inner loop in its own component
Codepen.io
I've added Date.now() after items[x].message list items to show when the list is being rerendered.
In case codepen ever goes down:
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.js"></script>
</head>
<body>
<div id="app">
Main vue: {{data}}
<loop-component :data="loopdata"></loop-component>
<input v-model="data"></input>
<input v-model="loopdata"></input>
</div>
<script>
Vue.component('loop-component', {
props: ['data'],
data() {
return {
items: [
{message: 'Foo'},
{message: 'Bar'}
]
}
},
template: `
<div>
Loop component: {{ data }}
<ul>
<li v-for="(item, index) in items" :key="index">
{{ item.message + ' Date.now(): ' + Date.now() }}
</li>
</ul>
</div>
`
});
let app = new Vue({
el: '#app',
data: () => ({
data: '',
'loopdata': '',
items: [
{message: 'Foo'},
{message: 'Bar'},
]
}),
});
</script>
</body>
</html>
Try using .lazy modifier to sync after change events.
<input v-model.lazy="data"></input>
https://v2.vuejs.org/v2/guide/forms.html#lazy
EDIT
#IVO GELOV is right, when a component changes, this re-render. The solution is split your component into several child components.
https://v2.vuejs.org/v2/guide/reactivity.html
This is a code using slots to make it look like your example.
HTML
<div id="app">
<new-component>
<ul>
<li v-for="item in items" :key="item">
{{ item.message }}
</li>
</ul>
</new-component>
</div>
Javascript
Vue.component('new-component', {
data: () => {
return {
data: ''
}
},
template: `
<div>
<div>{{ data }}</div>
<slot></slot>
<input v-model="data"></input>
</div>`
})
new Vue({
el: '#app',
data: () => ({
items: [{
message: 'Foo'
},
{
message: 'Bar'
}
]
})
})
Since Vue 2.0+ whenever a change is detected - the whole component is re-rendered. If you want to avoid that - split your component into several child components.
Your example does not prove your point - the fact that there is a warning about duplicate keys inside the v-for does not mean that v-for is re-evaluated on each keypress. To confirm my statement - just change your code like this:
<li v-for="(item,idx) in items" :key="idx">
Now there is no warning.

How to use Vue.js variable/parameter inside v-if condition

I need to pass a dynamic variable into v-if attribute.
I tried multiple ways but it doesn't produce the expected result.
So far, I have this: v-if="customDropdown === {{row.name}}"
How can I conditionally and dynamically render the element, please?
Thank you in advance.
You cannot use interpolation in Vue directives/attributes.
To bind to v-if or v-for use variables directly:
<div v-if="value.someProperty"></div>
To bind to other attributes/properties use v-bind: or shorthand : as follows:
<div :directive="value"></div>
Template Syntax documentation
You need binding the row.name to data object in vue:
export default {
data() {
return {
customDropdown: 'something',
row: {name: 'something'},
}
}
}
and then use it in the v-if statement:
<div v-if="customDropdown == row.name">You see me if customDropdown is equal to row.name</div>
First, you cannot interpolate directives/attributes and second, it's not clear, but I think you want to check the condition while iterating by a array.
You can use the template structure below:
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div
v-for="row in arrayList"
v-if="customDropdown === row.name"
>
Show row content: {{ row }}
</div>
</div>
With the data structure below to achieve you goal:
new Vue({
el: '#app',
data: {
customDropdown: 'first',
arrayList: [
{
name: 'first',
},
{
name: 'second',
},
],
},
})
Just use the properties name,
For example
export default {
data() {
return {
var: true
}
}
}
html
<span v-if="var">I only appear when the condition is true</span>

How pass a v-model on the parent into a template

I'm trying to compose the UI for a search page, but wanna use components to reuse code. However, I need a way to pass the model of the page to the search component, and don't see how:
In index.html:
<template id="search">
<q-search inverted placeholder="Look" float-label="Search" v-model="search" /> <-- BIND HERE
</template>
<template id="ListCustomersPage">
<q-layout>
<q-layout-header>
<search v-model="search"></search> <-- HOW PASS INTO THIS
</q-layout-header>
</q-layout>
</template>
And the code:
const search = {
template: '#search',
props: ['search']
};
const ListCustomersPage = {
key: 'ListCustomersPage',
template: '#ListCustomersPage',
components: { search },
data() {
return {
title: 'Select Customer',
search:'' <-- FROM THIS TO 'BIND HERE'
}
}
};
I'm not sure if I'm 100% following what you're asking, but it seems like you just want to pass a property to a child component?
<search :search="search"></search> <-- HOW PASS THIS
Passing a prop to a child is done with v-bind or the colon short hand for it.
<child-component :property="parent_data"></child-component>
<child-component v-bind:property="parent_data"></child-component>
See the documentation here.

Categories

Resources