VueJs Async loading templates - javascript

I Am building my first VueJs App and I want to asynchronous load my template. In our framework we have our templates stored in a database, that's why.
It is working until I have some nested dom-elements in my template without any data bound to it. So my my Vuejs is like:
var app = new Vue({
el: '#app',
data: {
finish: false,
template: null
},
render: function(createElement) {
if (!this.template) {
return createElement('div', 'loading...');
} else {
return this.template();
}
},
mounted() {
var self = this;
$.post('myUrl', {foo:'bar'}, function(response){
var tpl = response.data.template;
self.template = Vue.compile(tpl).render;
})
}
})
This is working when my template is like:
<div v-show="!finish">
<p>Test</p>
</div>
But when it's like this:
<div v-show="!finish">
<p>
<span>Test</span>
</p>
</div>
I get
[Vue warn]: Error in render: "TypeError: Cannot read property '0' of
undefined" (found in < Root >)
But when it's like this:
<div v-show="!finish">
<p v-show="!finish">
<span>Test</span>
</p>
</div>
It's working again.
Can anyone explain what is happening here? And is this the right way to do it or should I do it an other way?

My guess would be that you should try v-if instead of v-show. What v-show does is changing display property, vue is trying to render the element anyway.
docs

Related

Using Vue.js directives within component template

I'm new to Vue.js and trying to create a component that connects to one object within some global-scope data and displays differently based on the specifics of each object. I think I'm misunderstanding how the directives v-if and v-on work within component templates. (Apologies if this should actually be two different questions, but my guess is that the root of my misunderstanding is the same for both issues).
Below is a minimal working example. My goal is to have each member entry only display the Disable button if the associated member is active, and enable changing their status via the button. (I also want to keep the members data at the global scope, since in the actual tool there will be additional logic happening outside of the app itself).
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<member-display
v-for="member in members"
v-bind:member="member"
></member-display>
</div>
<script>
var members = [
{name: "Alex", status: "On"},
{name: "Bo", status: "On"},
{name: "Charley", status: "Off"}
]
Vue.component('member-display', {
props: ['member'],
computed: {
active: function() {
// Placeholder for something more complicated
return this.member.status == "On";}
},
methods: {
changeStatus: function() {
this.member.status = 'Off';
}
},
// WHERE MY BEST-GUESS FOR THE ISSUE IS:
template: `
<div>
{{member.name}} ({{member.status}})
<button v-if:active v-on:changeStatus>Disable</button>
</div>
`
});
var app = new Vue({
el: "#app",
data: {
members: members
}
})
</script>
</body>
</html>
Thanks for your help!
The code v-if and the v-on for the button just have the wrong syntax. The line should look like this:
<button v-if="active" v-on:click=changeStatus>Disable</button>

Vue.delete warning to avoid using JavaScript unary operator as property name on click

I get the following warning when using Vue.delete:
[Vue warn]: Error compiling template: avoid using JavaScript unary operator as property name: "delete(testObj,)" in expression #click="Vue.delete(testObj,'label')"
I can use Vue.delete anywhere else and it seems fine. Am I using it wrong?
new Vue({
el: '#app',
data() {
return {
testObj: {
label: "The label"
}
};
}
});
<div id="app">
{{testObj.label}}
<button #click="Vue.delete(testObj,'label')">Delete</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.11/dist/vue.js"></script>
The global Vue will not be available inside your template. Everything in a template is scoped to the current Vue instance, so you're effectively trying to access this.Vue. Trying to access any of the properties of the global Vue won't work.
You can use $delete instead of Vue.delete inside a template.
https://v2.vuejs.org/v2/api/#vm-delete
new Vue({
el: '#app',
data() {
return {
testObj: {
label: "The label"
}
};
}
});
<div id="app">
{{testObj.label}}
<button #click="$delete(testObj, 'label')">Delete</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.11/dist/vue.js"></script>
I would add that the specific error message you're seeing relates more generally to trying to use any property called delete but that's something of a moot point as you should be using $delete anyway.

Why Vue.js can't update the value once it has been set through $refs

Why doesn't the title change here once the callback function finishes executing nor when I click on the Change title button?
This is an example from a Udemy VueJS course and it runs fine on the video but I just can't seem to make it work on my side no matter what I try. I hope the question is understandable and well formulated.
var vm1 = new Vue({
el: '#app1',
data: {
title: 'The VueJS Instance',
},
methods: {
updateTitle: function(title) {
this.title = title;
}
}
});
vm1.$refs.heading.innerText = 'Something else';
setTimeout(function() {
vm1.title = 'Changed by Timer';
}, 3000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<div id="app1">
<h1 ref="heading">{{ title }}</h1>
<button v-on:click="updateTitle('a')" ref="myButton">Change title</button>
</div>
$refs is non-reactive,so you can't do data binding with it.
Check the documentation https://v2.vuejs.org/v2/api/#ref
If you want to use $refs to change value, you have to directly refer to it with property.
var vm1 = new Vue({
el: '#app1',
data: {
title: 'The VueJS Instance',
},
methods: {
updateTitle: function(title) {
this.$refs.heading.innerText = title;
}
}
});
vm1.$refs.heading.innerText = 'Something else';
setTimeout(function() {
vm1.$refs.heading.innerText = 'Changed by Timer';
}, 3000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<div id="app1">
<h1 ref="heading">{{ title }}</h1>
<button v-on:click="updateTitle('a')" ref="myButton">Change title</button>
</div>
I can't say for sure, but most likely this is happening because you manually messed with the DOM outside of Vue's knowledge.
Vue does a lot of bookkeeping in order to make patching the DOM as efficient as possible. By setting the innerText of the heading element, the existing text node (that Vue controls) is being replaced by a new text node that Vue doesn't control. When Vue re-renders the component, it detects that only the title data property changed, so it patches the old text node which no longer exists in the DOM because it got replaced.

Vue 2 Prop value not rendering in the template

I have this code where I am trying to render a value from prop in the template but getting a
Uncaught ReferenceError: channel is not defined
<script>
Vue.component('itemstable', {
template: `<div>${channel}</div>`, // this is where the error occurs.
props: ['channel'],
data() {
return {
}
}
})
new Vue({
el: '#items_app',
data: {
selectedChannel: 'pos',
channels: JSON.parse(`["pos","kiosk"]`)
}
})
</script>
and here is the markup:
<div id="items_app">
<itemstable
:channel="selectedChannel"
></itemstable>
</div>
I have tried changing the name of the prop thinking 'channel' might be a reserve word, but the
same thing happens.
Change the javascript interpolation ${} to vue expression {{}}
template: `<div>{{channel}}</div>`,

VueJS - Initializing a tagsinput form field that was loaded as part of template or $el attribute?

I'm following the pattern described in the official documentation of loading views using components. One of the components has a form field I need to have a method called .tagsinput() called on since I'm using TagsInput. So, something like $('#tags').tagsinput(). Here's a simplified version of what I'm doing:
CreateBoardForm = Vue.extend
template: "<input type='text' v-text='tags' id='tags'/>"
data:
tags: ''
ready: ->
// this is where I'm hoping to access
// tags and call $('#tags').tagsinput() on it
// However, this.$el and this.template are all undefined
// I was hoping to do something like this.$el.find('#tags').tagsinput()
Vue.component('CreateBoardForm', CreateBoardForm)
vue = new Vue(
el: '#main',
data:
currentView: 'createBoardForm'
components:
createBoardForm: CreateBoardForm
)
Any help on how I could possibly initialize that form field would be greatly appreciated.
Thank you
OK, I figured this out. Basically, you have to create a new component, listen to the attached event, use computed properties and then use the v-ref tag which becomes a reference to the tags input. I switched from this tagsinput library to another, but the idea is the same. Here's a working JSFiddle and below is the code:
<div id="tags-input-example">
<tags-input v-ref="twitterUsers"></tags-input>
<input type="button" v-on="click: onSubmit" value="Submit"/>
</div>
<script type="text/x-template" id="tags-input">
<input type="text" />
</script>
Vue.component('tags-input', {
template: "#tags-input",
attached: function() {
$(this.$el).find('input').tagsInput();
},
computed: {
tags: {
get: function () {
return $(this.$el).find('input').val();
}
}
}
});
vm = new Vue({
el: '#tags-input-example',
methods: {
onSubmit: function(e) {
console.log(this.$.twitterUsers.tags);
alert("The tags are: " + this.$.twitterUsers.tags);
}
}
});

Categories

Resources