I am new to VueJS and I am confused why a simple example from the docs is not working for me.
This is all the code. I am expecting it to display "Howdie Partners" on the page.
HTML
<div id="app">
<greeting></greeting>
</div>
JS
Vue.component('greeting', {
template: '<h1>{{message}}</h1>',
props: ['message']
});
var vm = new Vue({
el: '#app',
data: {
message: "Howdie Partners!"
}
});
JSFiddle Link: https://jsfiddle.net/hq1yu0ct/
From the documentation:
we can also use v-bind for dynamically binding props to data on the parent. Whenever the data is updated in the parent, it will also flow down to the child.
So you need to pass the props in the greeting component like following:
<div id="app">
<greeting :message="message"></greeting>
</div>
check working fiddle here.
Related
Right now, I'm trying to make a website that shows recent news posts which is supplied my NodeJS API.
I've tried the following:
HTML
<div id="news" class="media" v-for="item in posts">
<div>
<h4 class="media-heading">{{item.title}}</h4>
<p>{{item.msg}}</p>
</div>
</div>
JavaScript
const news = new Vue({
el: '#news',
data: {
posts: [
{title: 'My First News post', msg: 'This is your fist news!'},
{title: 'Cakes are great food', msg: 'Yummy Yummy Yummy'},
{title: 'How to learnVueJS', msg: 'Start Learning!'},
]
}
})
Apparently, the above didn't work because Vue can't render multiple root elements.
I've looked up the VueJS's official manual and couldn't come up with a solution.
After googling a while, I've understood that it was impossible to render multiple root element, however, I yet to have been able to come up with a solution.
The simplest way I've found of adding multiple root elements is to add a single <div> wrapper element and make it disappear with some CSS magic for the purposes of rendering.
For this we can use the "display: contents" CSS property. The effect is that it makes the container disappear, making the child elements children of the element the next level up in the DOM.
Therefore, in your Vue component template you can have something like this:
<template>
<div style="display: contents"> <!-- my wrapper div is rendered invisible -->
<tr>...</tr>
<tr>...</tr>
<tr>...</tr>
</div>
</template>
I can now use my component without the browser messing up formatting because the wrapping <div> root element will be ignored by the browser for display purposes:
<table>
<my-component></my-component> <!-- the wrapping div will be ignored -->
</table>
Note however, that although this should work in most browsers, you may want to check here to make sure it can handle your target browser.
You can have multiple root elements (or components) using render functions
A simple example is having a component which renders multiple <li> elements:
<template>
<li>Item</li>
<li>Item2</li>
... etc
</template>
However the above will throw an error. To solve this error the above template can be converted to:
export default {
functional: true,
render(createElement) {
return [
createElement('li', 'Item'),
createElement('li', 'Item2'),
]
}
}
But again as you probably noticed this can get very tedious if for example you want to display 50 li items. So, eventually, to dynamically display elements you can do:
export default {
functional: true,
props: ['listItems'], //this is an array of `<li>` names (e.g. ['Item', 'Item2'])
render(createElement, { props }) {
return props.listItems.map(name => {
return createElement('li', name)
})
}
}
INFO in those examples i have used the property functional: true but it is not required of course to use "render functions". Please consider learning more about functional componentshere
Define a custom directive:
Vue.directive('fragments', {
inserted: function(el) {
const children = Array.from(el.children)
const parent = el.parentElement
children.forEach((item) => { parent.appendChild(item) })
parent.removeChild(el)
}
});
then you can use it in root element of a component
<div v-fragments>
<tr v-for="post in posts">...</tr>
</div>
The root element will not be rendered in DOM, which is especially effective when rendering table.
Vue requires that there be a single root node. However, try changing your html to this:
<div id="news" >
<div class="media" v-for="item in posts">
<h4 class="media-heading">{{item.title}}</h4>
<p>{{item.msg}}</p>
</div>
</div>
This change allows for a single root node id="news" and yet still allows for rendering the lists of recent posts.
In Vue 3, this is supported as you were trying:
In 3.x, components now can have multiple root nodes! However, this does require developers to explicitly define where attributes should be distributed.
<!-- Layout.vue -->
<template>
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
</template>
Multiple root elements are not supported by Vue (which caused by your v-for directive, beacause it may render more than 1 elements). And is also very simple to solve, just wrap your HTML into another Element will do.
For example:
<div id="app">
<!-- your HTML code -->
</div>
and the js:
var app = new Vue({
el: '#app', // it must be a single root!
// ...
})
I have a simple component handled by <script type="text/x-template"..., and it has its own data object that has only one property to control whether or not to show a div.
I am getting the following warning in Console:
[Vue warn]: Property or method "showDiv" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option. (found in root instance)
Live demo: https://jsbin.com/zucaqog/edit?html,js,output
Js:
Vue.component('my-comp', {
template: '#my-comp',
data: function() {
return { showDiv: false };
}
});
new Vue({
el: '#app',
data: {}
});
If I add showDiv: false to the parent's data object, I no longer receive the warning. But, I'd like to avoid doing that since this data property is relevant only to the local scope of the component. I have a feeling if I put the html of the component as a string in the template: ... then it might work, but, I'd rather have it inside of a script.
It's because you have your child component's template definition as a child of the #app div. The parent component is seeing that there is a reference to the showDiv variable inside of its template, so it's throwing the error.
Pull that x-template outside of the #app div:
<div id="app">
<my-comp></my-comp>
</div>
<script type="text/x-template" id="my-comp">
<div>
Value of showDiv is {{showDiv}}.
<button #click="showDiv=!showDiv">Toggle showDiv</button>
</div>
</script>
I want to render the pure HTML coming from some external source into react component. I saw few solutions where people are talking about some conversion tools (HTML to JSX) but I want to handle everything in my component so while mounting it will get the HTML response and that needs to render.
You can use dangerouslySetInnerHTML for this:
function createMarkup() { return {__html: 'First ยท Second'}; };
<div dangerouslySetInnerHTML={createMarkup()} />
But as the method name suggests: you should be very sure of what you are doing there and the security implications it has.
This shouldn't be difficult to do . Assign your HTML to a div and then render it using {variable name} JSX allows you to do this and with ES6 integration you can also use class instead of className.
var Hello = React.createClass({
render: function() {
var htmlDiv = <div>How are you</div>
return <div>Hello {this.props.name}
{htmlDiv}
</div>;
}
});
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
I am building an app using express handlebars for server-side templating. On the client side, I want to use vue.js. However, they both share the same double brace notation {{ variable }}. Right now, my vue.js variables are not showing because my handlebars template is overriding it. For example:
home.html:
<div id="app">
{{message}} //this will not show up
</div>
home.js:
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
}
});
HOWEVER, if I define message in my server side controller:
res.render('../views/home', {
message: 'message from handlebars'
});
the message will show up.
So in summary, is there a way i can use vue.js for client side templating while still using express-handlebars for server side templating?
Thanks in advance!
This can be solved by escaping the first brace in your handlebars template.
<div id="app">
\{{message}} //this will not be replaced by handlebars
</div>
You can change the Vue demlimiters to something else, e.g.:
Vue.config.delimiters = ['${', '}']
You can "escape" the Vue delimiters in Handlebars by using a raw block helper:
Handlebars.registerHelper('vue-js', function(options) {
return options.fn();
});
Usage in the Handlebars template:
{{{{vue-js}}}}
<div id="app">
{{message}}
</div>
{{{{/vue-js}}}}
I have a problem with bootstrap modal window when using AngularJS together with NodeJS and socket.io. I have been googling and it seems like it is issue that has a solution, but for some reason it doesn't work when I am trying to implement it together with Socket.io. I used modals on two different places - when I click on a static div (works perfectly), when I receive a message from webSockets (opens only once and then nothing). I guess I might have a problem in my JS code since the modal when I click on a static div works fine, but I don't know.
I have an address and I am sending some data via WebSockets to the client when this link is visited. The client event looks like this:
socket.on('patient', function(data){
modalInstance = $modal.open({
templateUrl: 'templates/patient.js',
controller: 'patientModalCtrl',
resolve: {
details: function(){return data;}
}
});
});
and:
socket.on('alergy',function(data){
modalInstance = $modal.open({
templateUrl: "templates/alergy.js",
controller: 'alergyModalCtrl'
});
});
Both of these work only once and then the modal window stops to appear. Interesting is, that when I emit "alergy", then again and then "patient" I get an "alergy" window and then patient window the second "alergy" window under it.
emiting looks like this:
app.get('/api/socket/hash/:hash', function(req, res){
var hash = req.params.hash;
//allergy
if(hash === "3fDecCD"){
connected_sockets[0].emit('alergy', {alergy: true});
res.json({status: true});
}
//patient detail
else if(hash === "Vc43Sf"){
connected_sockets[0].emit('patient', {name: 'Jan', surname: 'Bjornstad'});
res.json({status: true});
}
else{
res.json({status: false});
}
});
My template looks like this:
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header"></div>
<div class="modal-body" style="background-color: #FFD1D1;">
<h1 style="text-align: center; color: red;">Allergy!</h1>
<h2 style="text-align: center; color: red;">The patient is allergic to opium!</h2>
</div>
<div class="modal-footer">
<button class="btn btn-warning" ng-click="cancel()">Close</button>
</div>
</div>
</div>
I am using AngularJS 1.0.7, Bootstrap CSS 2.3.1
I would say that your socket-event listeners are firing "outside of AngularJS world" and as such AngularJS machinery is not kicking-in to do its 2-way data binding "magic". In precise terms, you are not entering AngularJS $digest loop so bindings are not updated, promises not resolved etc.
The easy fix is to wrap calls to AngularJS-specific code (here - call to the $modal service) into Scope.$apply method, ex.:
socket.on('alergy',function(data){
$scope.$apply(function(){
modalInstance = $modal.open({
templateUrl: "templates/alergy.js",
controller: 'alergyModalCtrl'
});
});
});