In Greet.vue
<template>
<h2> Hello {{ name }} </h2>
</template>
<script>
export default {
name: "Greet",
props:['name']
};
</script>
In App.vue
<template>
<Greet name="bruce"/>
<Greet name="leo" />
<Greet name="diana" />
</template>
<script>
import Greet from './components/Greet.vue'
export default {
name: 'App',
components: {
Greet,
}
}
</script>
First I encountered this problem. Then I follow it by.
"vetur.validation.template": false,
"vetur.validation.script": false,
"vetur.validation.style": false,
Now there is no error. But now there is only one Hello displayed in the browser. I should expect 3.
This occurs when the vetur extension cannot determine the version of Vue as it cannot resolve package.json.
The docs at https://vuejs.github.io/vetur/ state that if Vetur cannot find package.json and determine the version of Vue, it assumes 2.5. This is what generates the wrong error. Vue3 can have more than one element.
It is expecting to find this at the project root - ie where you open your editor. Try opening you editor so that package.json sits exactly on the first level. You do not need to adjust Vetur settings.
App.vue
<template>
<div>
<Greet name="bruce"/>
<Greet name="leo" />
<Greet name="diana" />
<div>
</template>
<script>
import Greet from './components/Greet.vue'
export default {
name: 'App',
components: {
Greet,
}
}
</script>
The above will solve template root requires exactly one element, while these vetur configurations only disable some code checks.
"vetur.validation.template": false,
"vetur.validation.script": false,
"vetur.validation.style": false,
I have the following 2 components
BrewTitle.vue
<template>
<h1>{{ title }}</h1>
</template>
<script>
export default {
data() {
return {
title: "Brew Title"
};
},
created() {
console.log("title created")
}
};
</script>
Snackbar.vue
<template>
<h1>{{ title }}</h1>
</template>
<script>
export default {
data() {
return {
title: "Brew Title"
};
},
created() {
console.log("snackbar created")
}
};
</script>
How they are added to the index.js file
import Vue from "vue";
import BrewTitle from "./components/BrewTitle";
import Snackbar from "./components/Snackbar";
Vue.component("brewtitle", BrewTitle);
Vue.component("snackbar", Snackbar);
const app = new Vue({
el: "#app"
});
In my html template I have the following snippet
<div id="app">
<brewtitle />
<snackbar />
</div>
<script src="main.js"></script>
The components are almost identical, but the snackbar is nowhere to be found on the html page or in the view browser extension. There are no problems with webpack and there is no message in the browser.
What am I doing wrong?
Browsers don't support self-closing tags like these:
<brewtitle />
<snackbar />
Try having explicit closing tags instead:
<brewtitle></brewtitle>
<snackbar></snackbar>
If you use a self-closing tag for a component then the browser will just treat it as an opening tag. An implicit closing tag will be created when the parent element closes. That'll work fine if there are no other siblings but it will go wrong when there are.
So taking your original code as an example:
<div id="app">
<brewtitle />
<snackbar />
</div>
The <brewtitle> won't count as closed until it reaches the closing </div>. So this is equivalent to:
<div id="app">
<brewtitle>
<snackbar></snackbar>
</brewtitle>
</div>
So <snackbar> will be treated as a child of <brewtitle>. As brewtitle doesn't have a slot the snackbar will just be discarded.
This only applies if the HTML is being parsed directly by the browser. For anything parsed by Vue itself, such as in your .vue files, this won't be a problem.
From the official Vue documentation, https://v2.vuejs.org/v2/style-guide/#Self-closing-components-strongly-recommended
Components with no content should be self-closing in single-file components, string templates, and JSX - but never in DOM templates.
...
Unfortunately, HTML doesn’t allow custom elements to be self-closing - only official “void” elements.
I have been playing with the Vue tutorial Here and I have added a simple Jquery .html function. However it is not working. I have added the jQuery plugin, and there are no errors in the console. I have my "App" component defined like this:
<template>
<div id="app">
<div id="mainMenu"> Hello </div>
</div>
</template>
<script>
import * as start from './assets/scripts/start.js'
export default {
name: 'app',
created: start.loadMainNavigation()
}
</script>
and my loadMainNavigation function like this:
function loadMainNavigation() {
$('#mainMenu').html("ASERFDASRF");
console.log("In load Nav");
}
I can see the "In load Nav" in the console. No errors, but the DIV still has the original "Hello" - What am I doing wrong?
The reason the content doesn't change is that, at the time you are executing your function, the component has not yet been rendered to the DOM. The DOM is not rendered until the mounted event.
Beyond that, however, you need to be careful when you are integrating jQuery and Vue, or avoid it altogether. The idiomatic Vue way to do this would be something like this.
console.clear()
new Vue({
el: "#app",
data:{
message: "Hello"
},
created(){
this.message = "ASERFDASRF"
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<div id="app">
<div id="mainMenu"> {{message}} </div>
</div>
There are a few times when you might mix jQuery and Vue (when you want to use a jQuery plugin for which there is no Vue counterpart, for example) but typically, there is almost always a way to do what you want without jQuery.
I'm trying to use the on click directive inside a component but it does not seem to work. When I click the component nothings happens when I should get a 'test clicked' in the console. I don't see any errors in the console, so I don't know what am I doing wrong.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vuetest</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
App.vue
<template>
<div id="app">
<test v-on:click="testFunction"></test>
</div>
</template>
<script>
import Test from './components/Test'
export default {
name: 'app',
methods: {
testFunction: function (event) {
console.log('test clicked')
}
},
components: {
Test
}
}
</script>
Test.vue (the component)
<template>
<div>
click here
</div>
</template>
<script>
export default {
name: 'test',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>
If you want to listen to a native event on the root element of a component, you have to use the .native modifier for v-on, like following:
<template>
<div id="app">
<test v-on:click.native="testFunction"></test>
</div>
</template>
or in shorthand, as suggested in comment, you can as well do:
<template>
<div id="app">
<test #click.native="testFunction"></test>
</div>
</template>
Reference to read more about native event
I think the $emit function works better for what I think you're asking for. It keeps your component separated from the Vue instance so that it is reusable in many contexts.
// Child component
<template>
<div id="app">
<test #click="$emit('test-click')"></test>
</div>
</template>
Use it in HTML
// Parent component
<test #test-click="testFunction">
It's the #Neps' answer but with details.
Note: #Saurabh's answer is more suitable if you don't want to modify your component or don't have access to it.
Why can't #click just work?
Components are complicated. One component can be a small fancy button wrapper, and another one can be an entire table with bunch of logic inside. Vue doesn't know what exactly you expect when bind v-model or use v-on so all of that should be processed by component's creator.
How to handle click event
According to Vue docs, $emit passes events to parent. Example from docs:
Main file
<blog-post
#enlarge-text="onEnlargeText"
/>
Component
<button #click="$emit('enlarge-text')">
Enlarge text
</button>
(# is the v-on shorthand)
Component handles native click event and emits parent's #enlarge-text="..."
enlarge-text can be replaced with click to make it look like we're handling a native click event:
<blog-post
#click="onEnlargeText"
></blog-post>
<button #click="$emit('click')">
Enlarge text
</button>
But that's not all. $emit allows to pass a specific value with an event. In the case of native click, the value is MouseEvent (JS event that has nothing to do with Vue).
Vue stores that event in a $event variable. So, it'd the best to emit $event with an event to create the impression of native event usage:
<button v-on:click="$emit('click', $event)">
Enlarge text
</button>
As mentioned by Chris Fritz (Vue.js Core Team Emeriti) in VueCONF US 2019
If we had Kia enter .native and then the root element of the base input changed from an input to a label suddenly this component is broken and it's not obvious and in fact, you might not even catch it right away unless you have a really good test. Instead by avoiding the use of the .native modifier which I currently consider an anti-pattern, and will be removed in Vue 3, you'll be able to explicitly define that the parent might care about which element listeners are added to...
With Vue 2
Using $listeners:
So, if you are using Vue 2, a better option to resolve this issue would be to use a fully transparent wrapper logic. For this, Vue provides a $listeners property containing an object of listeners being used on the component. For example:
{
focus: function (event) { /* ... */ }
input: function (value) { /* ... */ },
}
and then we just need to add v-on="$listeners" to the test component like:
Test.vue (child component)
<template>
<div v-on="$listeners">
click here
</div>
</template>
Now the <test> component is a fully transparent wrapper, meaning it can be used exactly like a normal <div> element: all the listeners will work, without the .native modifier.
Demo:
Vue.component('test', {
template: `
<div class="child" v-on="$listeners">
Click here
</div>`
})
new Vue({
el: "#myApp",
data: {},
methods: {
testFunction: function(event) {
console.log('test clicked')
}
}
})
div.child{border:5px dotted orange; padding:20px;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="myApp">
<test #click="testFunction"></test>
</div>
Using $emit method:
We can also use the $emit method for this purpose, which helps us to listen to a child component's events in the parent component. For this, we first need to emit a custom event from a child component, like:
Test.vue (child component)
<test #click="$emit('my-event')"></test>
Important: Always use kebab-case for event names. For more information and a demo regading this point please check out this answer: VueJS passing computed value from component to parent.
Now, we just need to listen to this emitted custom event in the parent component, like:
App.vue
<test #my-event="testFunction"></test>
So basically, instead of v-on:click or the shorthand #click we will simply use v-on:my-event or just #my-event.
Demo:
Vue.component('test', {
template: `
<div class="child" #click="$emit('my-event')">
Click here
</div>`
})
new Vue({
el: "#myApp",
data: {},
methods: {
testFunction: function(event) {
console.log('test clicked')
}
}
})
div.child{border:5px dotted orange; padding:20px;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="myApp">
<test #my-event="testFunction"></test>
</div>
With Vue 3
Using v-bind="$attrs":
Vue 3 is going to make our life much easier in many ways. One example is that it will help us create a simpler transparent wrapper with less config, by just using v-bind="$attrs". By using this on child components, not only will our listener work directly from the parent, but also any other attributes will also work just like they would with a normal <div>.
So, with respect to this question, we will not need to update anything in Vue 3 and your code will still work fine, as <div> is the root element here and it will automatically listen to all child events.
Demo #1:
const { createApp } = Vue;
const Test = {
template: `
<div class="child">
Click here
</div>`
};
const App = {
components: { Test },
setup() {
const testFunction = event => {
console.log("test clicked");
};
return { testFunction };
}
};
createApp(App).mount("#myApp");
div.child{border:5px dotted orange; padding:20px;}
<script src="//unpkg.com/vue#next"></script>
<div id="myApp">
<test v-on:click="testFunction"></test>
</div>
But, for complex components with nested elements where we need to apply attributes and events to the <input /> instead of the parent label we can simply use v-bind="$attrs"
Demo #2:
const { createApp } = Vue;
const BaseInput = {
props: ['label', 'value'],
template: `
<label>
{{ label }}
<input v-bind="$attrs">
</label>`
};
const App = {
components: { BaseInput },
setup() {
const search = event => {
console.clear();
console.log("Searching...", event.target.value);
};
return { search };
}
};
createApp(App).mount("#myApp");
input{padding:8px;}
<script src="//unpkg.com/vue#next"></script>
<div id="myApp">
<base-input
label="Search: "
placeholder="Search"
#keyup="search">
</base-input><br/>
</div>
A bit verbose but this is how I do it:
#click="$emit('click', $event)"
UPDATE: Example added by #sparkyspider
<div-container #click="doSomething"></div-container>
In div-container component...
<template>
<div #click="$emit('click', $event);">The inner div</div>
</template>
Native events of components aren't directly accessible from parent elements. Instead you should try v-on:click.native="testFunction", or you can emit an event from Test component as well. Like v-on:click="$emit('click')".
One use case of using #click.native is when you create a custom component and you want to listen to click event on the custom component. For example:
#CustomComponent.vue
<div>
<span>This is a custom component</span>
</div>
#App.vue
<custom-component #click.native="onClick"></custom-component>
#click.native always work for this situation.
App.vue
<div id="app">
<test #itemClicked="testFunction($event)"/>
</div>
Test.vue
<div #click="$emit('itemClicked', data)">
click here
</div>
From the documentation:
Due to limitations in JavaScript, Vue cannot detect the following changes to an array:
When you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue
When you modify the length of the array, e.g. vm.items.length = newLength
In my case i stumbled on this problem when migrating from Angular to VUE. Fix was quite easy, but really difficult to find:
setValue(index) {
Vue.set(this.arr, index, !this.arr[index]);
this.$forceUpdate(); // Needed to force view rerendering
}
I just want to know if there is any way I can "draw" in my index.html multiple <div id = "x" /> <div id = "y" /> with REACT, i mean.. i have all my site on index.html with all my template, so i only need to use REACT on an specifics sections...
i tried this i didnt work
HTML
<div id="test" />
<div id="app" />
<script src="public/bundle.js" charset="utf-8"></script>
JSX
import React from 'react';
import {render} from 'react-dom';
class App extends React.Component {
render () {
return (<h1>App</h1>);
}
}
class Test extends React.Component {
render () {
return (<h1>Test</h1>);
}
}
render(<App/>, document.getElementById('app'));
render(<Test/>, document.getElementById('test'));
then when i load the page only prints the <h1>Test</h1>... why?
I created a JSFiddle to try a few things out here: http://jsfiddle.net/pof580fd/1/
I found out that by explicitly closing each of the <div> tags I could get it to work, i.e.:
<div id="test"></div>
<div id="app"></div>
I did a little research and it appears that as div is not one of the HTML5 "void elements" (Listed here: https://www.w3.org/TR/html5/syntax.html#void-elements) it is not self-closing and you must use </div>. See this SO question for more details: Are (non-void) self-closing tags valid in HTML5?
Possibly some browsers are lenient about this (I'm using Chrome 52 right now) - but React is not, it appears. The error message I see in the console is:
Uncaught Error: Invariant Violation: _registerComponent(...): Target container is not a DOM element.
(Make sure you're using the "dev" version of React to see these)
You can create a new component and call from that.
import App from "./App";
import Test from "./Test";
class Program extends React.Component {
render() {
<div>
<div id="app"><App /></div>
<div id="test"><Test /></div>
</div>
}
}
and then call
render(<Program />), document.getElementById('...'));
We created a framework to do this with great success.
Have a look at React Habitat.
With it you can just register components eg:
container.register('SomeReactComponent', SomeReactComponent);
Then they auto resolve in the dom via:
<div data-component="SomeReactComponent"></div>