I'm playing with vue for the first time and have knocked up a simple test file. I have a table that is populated by data from an api call, and have used the vue router to create links to each item but this link isn't displaying the content. the network shows it's making the request to the api, but for some reason the template is not showing (not even the hard-coded content). Why? Also, why does the route content not display on the first router view, but only the router-view in the tbl component?
<!doctype html>
<html lang="en">
<head>
<title>Vue.js test</title>
<meta charset="utf-8">
</head>
<body>
<div id="app">
<!-- this route displays correctly -->
<router-link to="/foo">Go to Foo</router-link>
<table-notification/>
<!-- content is not displayed here - why? -->
<router-view></router-view>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vue-router"></script>
<script src="axios.min.js"></script>
<script>
const foo = {template:'<div>asdfasdf</div>',};
var router = new VueRouter({
routes: [
{path: '/foo', component: foo},
{path: '/notifications/:id', component: notification_view}
]
});
var app = new Vue({
router: router
}).$mount('#app');
var notification_view = new Vue({
router: router,
template: `
<div>iop
id: {{ obj.id}}<br/>
title: {{ obj.data.title}}<br/>
message: {{obj.data.message}}<br/>
</div>
`,
data: {
obj: {},
id: 0,
},
watch: {
'$route' (to, from) {
// check for id in url
if (this.$route.params.id)
this.update_notification();
}
},
methods: {
update_notification: function(){
this.id = this.$route.params.id;
axios.get('http://api2/notifications/' + this.id)
.then(response => {this.obj = response.data.packet;});
}
}
});
var tbl = new Vue({
router:router,
el: 'table-notification',
template: `
<div>
<table>
<tr>
<th>Title</th>
<th>Created</th>
<th>Actions</th>
</tr>
<tr v-for="obj in objects">
<td><router-link :to="link(obj)">{{obj.data.title}}</router-link></td>
<td>{{obj.created}}</td>
<td>actions</td>
</tr>
</table>
<router-view/>
<!-- why must router-view be here, and why isn't notification_view showing any output?-->
</div>
`,
methods: {
link: function(obj) {return '/notifications/' + obj.id;}
},
data: {
objects: [],
},
created: function(){
axios.get('http://api2/notifications').
then(response =>
{
this.objects = response.data.packet;
});
}
});
</script>
</body>
</html>
I think you are misunderstanding the idea of Vue instances...
In you example you have:
a JSON foo that I think it is a component,
An App instance, which is mounted to the #app element
Another instance called notification_view with the same router, what I think is is another component
Another tbl instance, that I think it is another component
You actually need one instance and a bunch of components as in this example:
https://jsfiddle.net/dcr3jyzo/
Related
I'm trying to get data from parent component and use them in child component v-bind:style.
Here's my code.
<body>
<div id="parentComponent">
<child-component v-bind:propsdata="parentBackground" v-bind:propsdata2="parentFontstyle"></child-component>
</div>
<script>
Vue.component('child-component', {
props: ['propsdata', 'propsdata2'],
data: function() {
return {
childBackground: this.propsdata,
childFontStyle: this.propsdata2
},
template: '<p v-bind:style="childBackgroundColor, childFontStyle">Child componnent Area</p>'
});
new Vue ({
el: '#parentComponent',
data: function() {
return {
parentBackground: 'background-color:yellow;',
parentFontStyle: 'font-style: italic;'
},
})
</body>
When I run this code, only second style(childFontStyle) is applied to Child componnent template.
I also tried v-bind:style="[childBackgroundColor, childFontStyle]" and doesn't work.
Is there any way to apply both style?
You need to pass the props property as an object and can able to bind the props directly to the child. You have some typo mistakes that need to be fixed as well. Here is the working snippet.
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="parentComponent">
<child-component v-bind:propsdata="parentBackground" v-bind:propsdata2="parentFontStyle"></child-component>
</div>
<script>
Vue.component("child-component", {
template: `<p v-bind:style="[propsdata, propsdata2]">Child componnent Area</p>`,
props: ["propsdata", "propsdata2"]
});
new Vue({
el: "#parentComponent",
data: function() {
return {
parentBackground: {
"background-color": "yellow"
},
parentFontStyle: {
"font-style": "italic"
}
};
}
});
</script>
</body>
</html>
I've run into an issue where i'm creating components that are then being applied to the root App - after creating a dynamic child w/ vanilla JS. When i look at the Vue object in the console, message is not present, which i expect it to be - Can anyone tell me why?
Create the App
Dynamically add new DOM element w/ createElement with a {{ message }} property (ex: <div id="test">{{message}}</div>)
Create a custom component using Vue.Component (ex: <custom-component><custom-component> w/ pre-populated {{ messsage }} value test message
Render the Vue w/ the component w/ update props values for {{ message }}
Below is the actual code tested:
import Vue from 'vue/dist/vue.js';
export default {
name: 'app',
components:
{
HelloWorld
},
data()
{
return this;
},
mounted()
{
// #2 Create an Html Target that contains the component's name 'custom-element'
var v = document.createElement('div');
v.setAttribute('id', 'test');
v.innerHTML = '<custom-element></custom-element>';
var $element = this.$el.prepend(v);
// #1 Create a component
var MyComponent = Vue.component(
'custom-element',
{
template: '<div v-bind:id="UID">{{message}}</div>',
prop: ['UUID', 'message'],
data() {
return {
UID: '',
message: 'test message',
}
},
}
);
// #3 Append the component to the Html Target
window.vm = new Vue({
el: '#test',
components: {
'custom-component': MyComponent,
},
beforeCreate() {
return {
UID: 'x7x7x',
message: 'test message update...'
}
},
})
window.console.log(MyComponent);
window.console.log(this);
}
}`
Here's the main index.html:
`<!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>hello-world</title>
</head>
<body>
<noscript>
<strong>We're sorry but hello-world doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>'
Here's the main.js
'use strict'
import Vue from 'vue'
import App from './App.vue'
import Reaktr from './js/reaktr.js'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
data: {
Reaktr: new Reaktr(),
},
mounted() {
}
}).$mount('#app')
Here's the Helloworld.vue
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
I'm trying to render multiple components that use inline-templates using vue-router but as soon as i define the inline-template the component renders no matter the url.
Does inline-templatesworks with vue-router?
It really depends on what you mean by "work". You can Frankenstein something like this and it will do what you want without throwing any errors, but I feel I'm making the world an uglier place by just posting this example:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Vue-router with inline-template</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
<router-view>
<foo inline-template id="foo"><div>foo</div></foo>
</router-view>
</div>
<script>
const Foo = { template: document.getElementById('foo').innerHTML };
const Bar = { template: '<div>bar</div>' };
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar },
];
const router = new VueRouter({
routes,
});
var app = new Vue({
el: '#app',
router,
});
</script>
</body>
</html>
This is pretty much the intro example from vuejs.org with one of the templates made into inline.
You can refer to your inline template using #
const Foo = { template: '#my-foo-template' }
const routes = [
{ path: '/foo', component: Foo },
];
<foo inline-template id="my-foo-template"> ... </foo>
I am using the basic tutorial of the guide/Getting started, but changed HTML code to
<div id="app">
<!-- ... all the same except router-view ... -->
</div>
<h2>Outside:</h2>
<code id="outApp"><router-view></router-view></code>
But, of course, it is not working, I need to say to mount router-view at #outApp, how to do it?
PS: when #outApp is inside #app all is working fine, on my page reproduction of tutorial.
As commented previously, and being sure you're already notified about this unpractical practice's. This is the solution I offer you.
Create a new Vue instance (#app2) and use the router in there.
Vue.config.productionTip = Vue.config.devtools = false // ignore
const router = new VueRouter({
routes: [
{
path: "/",
component: { template: "<router-view/>" },
children: [
{
path: "",
name: "foo",
component: { template: "<div>Foo</div>" }
},
{
path: "bar",
name: "bar",
component: { template: "<div>Bar</div>" }
}
]
}
]
});
// App1 without routing capabilities
new Vue({
// router, don't need it here
el: "#app1"
});
// App2
new Vue({
router,
el: "#app2"
});
<script src="https://unpkg.com/vue#2.5.16"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<fieldset>
<legend>App1</legend>
<div id="app1">
This is the app without router
</div>
</fieldset>
<fieldset>
<legend>App2</legend>
<div id="app2">
<router-link :to="{name:'foo'}">Foo</router-link>
<router-link :to="{name: 'bar'}">Bar</router-link>
<hr>
<router-view></router-view>
</div>
</fieldset>
I have experience with both single page apps and multi-page apps (classic websites). In the past I have used AngularJS 1.x on each page, it has been useful because all components can live in separate files and be executed as they appear on each page.
I'm now looking at VueJS to replace AngularJS, but not finding it easy to understand how to architect my multi-page app.
As expected I want to use some components on all the pages, and some on only a few pages.
Example:
I came across SFC - single file components using ES2015 which looked promising, but my backend is Java which outputs my html from JSPs. It appears that .vue files are precompiled by webpack, but if my templates are only ready when the page is rendered that won't be possible will it?
How would one architect a solution so that each component is modular but utilize either an x-template in the html and somehow attach it to a .vue SFC, or is there some other way to have components in separate files which can be imported using ES2015?
I hope this is making sense, can't seem to figure it out.
One possible approach would be to set the template for the Vue Component inline. So this would be to have a Component File like
Home.vue:
<script>
export default {
data() {
return {
msg: 'text',
}
}
}
</script>
import it as a global component for Vue (using require, import, etc.)
Vue.component('home', require('./components/Home.vue'));
and in your server generated HTML you'd have to use an inline template, which will have all the flexibility from normal templates
home.jsp:
<home inline-template>
<h2 v-text="msg"></h2>
</home>
Update
I've added an example on GitHub here
If I understand your question, you have want to make single file components out of HTML.
If this is the case, you should make use of the render() function and regular components.
The render function decides what to use as a template for a component:
<!DOCTYPE html>
<html>
<head>
<title>Vue</title>
</head>
<body>
<div id="app">
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<script type="text/javascript">
new Vue({
el: '#app',
render (createElement) {
return createElement({
template: '<div>Hello World</div>'
})
},
})
</script>
</body>
</html>
will render Hello World to the screen.
Now, let's see how this function is reactive:
<script type="text/javascript">
new Vue({
el: '#app',
data: {
count: 0
},
render (createElement) {
return createElement({
template: '<div>Hello World ' + this.count + '</div>'
})
},
created () {
setTimeout(() => {
this.count++
}, 2000)
}
})
</script>
Here, after 2 seconds, the counter in <div>Hello World ' + this.count + '</div> will increment from 0 to 1.
Now, what if we want to separate the template from the data?
<script type="text/javascript">
new Vue({
el: '#app',
render (createElement) {
return createElement({
template: '<div>Hello World {{ count }}</div>',
data () {
return {foo: 'bar'}
}
})
}
})
</script>
This code will display Hello World bar.
Now, let's see what happen if we try to load our template over http. We'll use the axios library to do so. Let's create a remote.html file to contain our html code:
<div>
I'm a remote component {{ foo }}
</div>
Now, let's try to load it via Ajax:
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.16.2/axios.min.js"></script>
<script type="text/javascript">
new Vue({
el: '#app',
data: {
template: null
},
render (createElement) {
return createElement({
template: this.template ? this.template : '<div>Hello World {{ foo }}</div>',
data () {
return {foo: 'bar'}
}
})
},
created () {
axios({
url: '/remote.html',
method: 'get'
}).then(response => {
this.template = response.data
})
}
})
</script>
This code will display I'm a remote component {{ foo }} as soon as remote.html has been loaded from the browser.
Note that the object passed to the createElement function is actually a component structure. You can use the same methods on it:
render (createElement) {
return createElement({
template: this.template ? this.template : '<div>Hello World {{ foo }}</div>',
data () {
return {foo: 'bar'}
},
mounted () {
alert('Hello from mounted')
}
})
}
will trigger an alert on the browser.
Anyway, here is a complete example with nested components:
Index.html
<!DOCTYPE html>
<html>
<head>
<title>Vue</title>
</head>
<body>
<div id="app">
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.16.2/axios.min.js"></script>
<script type="text/javascript">
const headerComponent = {
data () {
return {
template: '<div>Loading...</div>'
}
},
render (createElement) {
return createElement({
template: this.template,
data () {
return {
search: ''
}
}
})
},
created () {
axios('/header.html').then(response => {
this.template = response.data
})
}
}
new Vue({
el: '#app',
data: {
template: null
},
render (createElement) {
return createElement({
template: this.template ? this.template : 'Loading...',
data () {
return {foo: 'bar'}
},
components: {
'my-header': headerComponent
}
})
},
created () {
axios({
url: '/remote.html',
method: 'get'
}).then(response => {
this.template = response.data
})
}
})
</script>
</body>
</html>
header.html
<div>
<label>Search</label>
<input v-model="search" name=""> The search is: {{ search }}
</div>
I'm not sure that this is really the best approach and if I'm really responding to the question, but it will at list give you some tips on how Vue handles rendering and components...