Vue: Why can't I use refs within component templates? - vue.js

I'm struggling with Vue refs. If I define them in my main Vue instance's template, they work fine, but if I define them within a component template, they don't. What am I doing wrong?
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
</head>
<body>
<div id="app"></div>
<script src="main.js"></script>
</body>
</html>
main.js, version 1
const app = new Vue({
el: '#app',
template: `
<div ref="divRef">
<button ref="buttonRef">Submit</button>
</div>
`
});
result (matches expectation)
> app.$refs
> {buttonRef: button, divRef: div}
main.js, version 2
Vue.component('demo-page', {
template: `
<div ref="divRef">
<button ref="buttonRef">Submit</button>
</div>
`
});
const app = new Vue({
el: '#app',
template: '<demo-page ref="componentRef"></demo-page>'
});
expected result
> app.$refs
> {componentRef: VueComponent, buttonRef: button, divRef: div}
actual result
> app.$refs
> {componentRef: VueComponent}

$refs are scoped within a component. That's the reason, you can only see the componentRef in the $refs of the app itself. The other $refs are accessible within the scope of each component. So, try to access the $refs within the scope of that component.
If you are defining a ref on an element also having the attribute v-for on it, this.$refs.refName will return you an array of DOM elements. Keep in mind that $refs are not reactive unlike data properties.
See also the documentation to $refs with child component instances and the documentation to the ref="" attribute.

Related

Fullcalendar with Vue without CLI

I have a small Vue project that isn't in a build environment that is just a bunch of JS files and uses Vue from the CDN
I would like to use FullCalendar in my Vue project and ideally use the official FullCalendar Vue component, but this only seems to be available for projects using the CLI build environment.
Is there a Vue component available for non-build projects that I could use that still implements the normal <FullCalendar /> tag?
About CDN see
new Vue({
el: '#app',
mounted() {
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
initialView: 'dayGridMonth'
});
calendar.render();
}
})
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link href='https://cdn.jsdelivr.net/npm/fullcalendar#5.8.0/main.min.css' rel='stylesheet' />
<script src='https://cdn.jsdelivr.net/npm/fullcalendar#5.8.0/main.min.js'></script>
</head>
<body>
<div id="app">
<div id='calendar'></div>
</div>
</body>
</html>

Vue JS sub components?

Im new to VueJS and trying to figure out how I would nest components or have sub components. I don't know if im very bad at googling but I couldn't really find a clear answer.
This is my script and html :
Vue.component('card', {
template: '<div class="card"></div>'
})
Vue.component('card-text', {
props: ['text-content'],
template: '{{ text-content }}'
})
new Vue({
el: '#app',
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script defer src="script.js"></script>
</head>
<body>
<div id="app">
<card>
<card-text text-content="Text to show"></card-text>
</card>
</div>
</body>
</html>
as you can see I have 2 components, card and card-text.
I want to display these like this :
<card>
<card-text text-content="text to show"></card-text>
</card>
You would need slots to do that. Just change your card component to:
Vue.component('card', {
template: '<div class="card"><slot></slot></div>'
})
To be able to render different stuff inside <card></card>
You can read more in the docs
If you want to nest card-text inside card, just do as follows:
Vue.component('card', {
template: '<div class="card"><card-text text-content="Text to show"></card-text></div>'
})

What is the difference between this two very basic vue app?

I've been learning Vue.js and I tried to have the root instance not erase the html content I put inside. The idea being that I could have a normal html page and Vue "watching" the main wrapper and if it run into a vue component it will be render by vue. I've managed to do that when I import the CDN of vue but not with the vue cli somehow. I don't understand the difference.
I made this codepen loading vue.js by the cdn and it render without problem
<div id="app">
<h1>My Vue.js App</h1>
<p>{{ message }}</p>
</div>
new Vue({
el: '#app',
data: {
message: 'Hello world'
}
});
https://codepen.io/cvallee/pen/dLKVEP
But in codesandbox where it use vue cli nothing is render, the content of the root element flash and then disappear from the dom. No matter what I put into the main div it is erase as soon as the app mount. https://codesandbox.io/s/m5qvm40nkx
I think the issue has to do with the way that the CodeSandbox loads the Vue app and triggers the initial render. If you add an App.vue file and change the main.js file to
import Vue from "vue";
import App from "./App.vue";
// Vue.config.productionTip = false;
new Vue({
el: "#app",
render: h => h(App)
});
and the index.html to
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<title>codesandbox</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
it works for me this way. Here is the working version - https://codesandbox.io/s/84ox08k24j

When a new component is inserted all the rest disappears inside the app

I always build my SPA apps with the vue-cli.
This time I'm building a small project and I'm incluing Vue with a script tag.
But I don't understand the following behavior.
// app.js
Vue.component('todo-item', {
template: `<div>Todo Component!</div>`
})
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue App!'
}
})
The HTML index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Title</title>
</head>
<body>
<div id="app">
{{ message }}
<div>
Just some content...
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="./js/app.js"></script>
</body>
</html>
The result is this:
Now, I'll try to add '<todo-item />' Component inside the HTML:
<div id="app">
{{ message }}
<todo-item />
<div>
Just some content...
</div>
</div>
The text 'Just some content...' disappeared:
What Am I doing wrong?
TL;DR;
instead of <todo-item/> use <todo-item></todo-item>
Un-compiled vue.js does not support self-closing html tags.
see style guide:
Unfortunately, HTML doesn’t allow custom elements to be self-closing -
only official “void” elements. That’s why the strategy is only
possible when Vue’s template compiler can reach the template before
the DOM, then serve the DOM spec-compliant HTML.
https://v2.vuejs.org/v2/style-guide/#Self-closing-components-strongly-recommended
and issues in github:
https://github.com/vuejs/vue/issues/1036
https://github.com/vuejs/vue/issues/8664

Error when rendering component in Vue.js

I am new to Vue.js and am trying to create a sample component as per the code below but ending up with "[Vue warn]: Error when rendering component <my-tag>: " I have looked at stackoverflow for a similar question asked before but that did not help. The code for component is as below:
Vue.component('my-tag', {
props: ['myTagAttr'],
template: '<span>{{myTagAttr.text}}</span>'
})
var data = {
myTagAttrVal: {
text: 'foobar',
color: 'Red'
}
}
var vm = new Vue({
el: '#demo',
data: data
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script>
</head>
<body>
<span id='demo'>
<my-tag v-bind:myTagAttr='myTagAttrVal'></my-tag>
</span>
</body>
</html>
Alternatively, code can be found at JSbin
HTML attributes are case-insensitive, so when using non-string templates, camelCased prop names need to use their kebab-case (hyphen-delimited) equivalents :
<my-tag v-bind:my-tag='myTagAttrVal'></my-tag>
Here is a working fiddle: https://jsfiddle.net/nxcbm6na/
You can find the details in the documenation
https://v2.vuejs.org/v2/guide/components.html#camelCase-vs-kebab-case