Vue replaces "open" attribute to value "open" in any tag - vue.js

I'm using vue.js (v2.6.12) components in laravel blade templates.
For the project, I'm also using MathML in which I need to use the open attribute of <mfenced> tag to be set to some custom values. Here is the example of the math expressing in mathml.
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>f</mi>
<mfenced close="]" open="[">
<mrow><mi>a</mi><mo>,</mo><mi>b</mi></mrow>
</mfenced>
</math>
But as soon as the page renders, the open attribute is converted into this open="open". I'm 100% sure there is no other library or script is loaded that updates like so, just plain vue. This actually breaks the math expression. So it looks like this:
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>f</mi>
<mfenced close="]" open="open">
<mrow><mi>a</mi><mo>,</mo><mi>b</mi></mrow>
</mfenced>
</math>
Later I realized that not only in math expression, litaratily any tag, be it <div open="anything">...</div>, <span open="anything">...</span>, <custom-element open="something">...</custom-element> having open attribute behaves the same. even if I use v-pre attribute to exclude it from vue js templete compiler.
And this do not happen, as soon I disable the vue app initialization.
The question here are:
Why vue is changing the open attribute like so?
How can I stop this behaviour, to the entire page within the vue application area or at least where I choose (something like using v-pre), is there ary config or any other way around?

Why
In HTML spec there are some attributes called boolean attributes. Spec dictates what can be a value of such attribute:
If the attribute is present, its value must either be the empty string or a value that is an ASCII case-insensitive match for the attribute's canonical name, with no leading or trailing whitespace.
The values "true" and "false" are not allowed on boolean attributes. To represent a false value, the attribute has to be omitted altogether.
open is one of the boolean attributes - it is defined for the <details> element
Problem with Vue 2 is, that it treats most of the boolean attributes as global - without considering the element it is placed on. Result is that open attribute is always rendered with value "open" or removed if the value is falsy (when v-binding). This is fixed in Vue 3 as shown in 2nd example...
How
The use of v-pre is the way to go but unfortunately for you there is a bug.
See this issue. The bug was already fixed with this commit(Sep 21, 2020) but it was not released yet...
example - the "With v-pre" should work in Vue version > 2.6.12
const vm = new Vue({
el: '#app',
data() {
return {
message: 'Hi!',
html: `<div open="[" close="]">Hi from html</div>`
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.12/vue.js"></script>
<div id="app">
<div open="[" close="]">{{ message }}</div>
<div v-html="html"></div>
<div v-pre>
<p open="[" close="]">With v-pre</p>
</div>
</div>
example - it works in Vue 3 - open is treated as boolean attribute only if placed on <details>
const app = Vue.createApp({
data() {
return {
message: 'This works in Vue 3!',
}
},
})
app.mount('#app')
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.11/vue.global.js" integrity="sha512-1gHWIGJfX0pBsPJHfyoAV4NiZ0wjjE1regXVSwglTejjna0/x/XG8tg+i3ZAsDtuci24LLxW8azhp1+VYE5daw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="app">
<div open="[" close="]">{{ message }}</div>
<details open="[">
<summary>Details</summary>
open attribute on details element is treated as boolean (renders empty value)
</details>
</div>

One workaround is to create a directive (named "attr") that sets the attribute:
Vue.directive('attr', (el, binding) => el.setAttribute(binding.arg, binding.value || ''))
Then use it in your template like v-bind but with v-attr:
<mfenced v-attr:open="'['">
Vue.directive('attr', (el, binding) => el.setAttribute(binding.arg, binding.value || ''))
new Vue({ el: '#app' })
<script src="https://unpkg.com/vue#2.6.12"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?config=TeX-MML-AM_CHTML"></script>
<div id="app">
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>f</mi>
<mfenced close="]" v-attr:open="'['">
<mrow><mi>a</mi><mo>,</mo><mi>b</mi></mrow>
</mfenced>
</math>
</div>

I've found a simple hack to solve this problem.
Why hack?
Because it is eventually going to be fixed in the comming release as pointed by #Michal, so just a quick & dirty hack is enough for now to go for it.
What I did is I placed the math content in the content and also added it to the data attribute and replacing the original content after vue has done its bad work (sorry just using blade syntax here, but it will make sense). I keep it in both places just for SEO purposes.
The template where I need math expression to be displayed.
...
<div class="proxy-content" data-proxy-content="{{ $article->content }}">
{!! $article->content !!}
</div>
...
I was using it along with jQuery, but you can easily substitute with vue.js' $el. This is what it looks in my app.js file.
...
const app = new Vue({
el: '#app',
methods: {
proxyContent() {
// Set Proxy Content.
jQuery('.proxy-content').each((i, el) => {
const $el = jQuery(el);
$el.html( jQuery('<textarea />').html( $el.data('proxy-content')).text() );
});
}
loadMathJax() {
// Load & Initialize MathJax Library.
const script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://cdn.jsdelivr.net/npm/mathjax#3/es5/tex-mml-chtml.js";
document.getElementsByTagName("head")[0].appendChild(script);
}
}
mounted(){
// Enable proxy content after mount, so we are sure no more rendering issue for templates.
this.proxyContent();
// Load MathJax library with a little delay to make sure everything is ready before loading the library.
setTimeout(() => this.loadMathJax(), 10);
}
});
...
One might argue, that I'm mixing up things outside of the scope of the vue application. For me that is not an issue, as the whole page is using vue.js and also the single thing don't make any harm even if there is another scope that is using mathml (though it depends on actual implementation).
In that case, if you want to scope it well, just use $el of vue.

Related

Vue: How to perform reactive object change detection in v-for?

I read this documentation but cannot use the proposed solution.
I have a v-for loop over objects. These objects are changed dynamically over time and I need that change to show reactively in the v-for loop.
<b-row lg="12" v-for="data in objects" :key="data.id">
<div v-if="data.loading">
loading...
{{data.loading}}
</div>
<div v-else>
loaded, done
{{data.loading}}
</div>
</b-row>
In my methods, I have a for loop that downloads data for each object and changes the object value like this:
for(var i = 0; i<response.ids.length; i++){
var newId = response.ids[i].id
this.objects.newId = {"loading":true, "id": newId}
downloadSomething(newId).then(res => {
this.objects.newId = res[0] //<-- this change needs to be shown reactively.
})
}
According to Vue documentation, Object changes are not reactive:
var vm = new Vue({
data: {
a: 1
}
})
// `vm.a` is now reactive
vm.b = 2
// `vm.b` is NOT reactive
Vue propses some workaround like this:
Vue.set(vm.userProfile, 'age', 27)
UPDATE
But for my case, this just creates a new parameter in the object with the same ID and creates a duplicate key warning and other problems.
I also tried Vue.delete just before Vue.set but it is not actually deleting.
Is there a way to not replace the key/value pair but add more/change parameters to the first child of the root with the ID of newID
Thanks!
Solution: Replace this.objects.newId = res[0] with this.$set(this.objects, 'newId', res[0]) and it should work.
Explanation: this.$set is just an alias to Vue.set, available within any Vue instance. Bear in mind that vm (stands for view model) in the example is the same as this and equals to Vue component instance. Also due to ES5 JS restrictions, you have to set objects' properties explicitly via Vue.set/this.$set to manually trigger re-render. This problem will be resolved when Vue 3.0 is released.
Hope this helps, if you need any clarifications - feel free to ask.
Try that:
downloadSomething(newId).then(res => {
this.$set(this.objects, 'newId', res[0])
})
you need Vue.set() when you want to define a new property to an existing object (not directly to the data), and this function will assign the new property to the built-in watcher, and it will become reactive as well.
its all being explained in the docs: https://v2.vuejs.org/v2/guide/reactivity.html
in your case, it seems to be all you need to make it work. in my example:https://jsfiddle.net/efrat19/eywraw8t/484131/ an async function fetches the data and define a new property to contain te response.
new Vue({
el: "#app",
data: {
objects:{
property1:12
}
},
methods: {
fetchDataAndAddProperty(response){
fetch('https://free.currencyconverterapi.com/api/v6/countries').then(res =>
res.json()).then(data => Vue.set(this.objects,'property2',data))
}
}
})
and the template:
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<div id="app">
<div lg="12" v-for="(val,key,index) in objects" :key="index">
{{key}}:{{val}}
</div>
<button #click="fetchDataAndAddProperty()">fetch</button>
</div>
as you can see in the fiddle, the new property becomes reactive and being displayed as well.

How to call a JavaScript function from a Vue.js v-bind

I want to call a plain JavaScript function from inside a Vue.js v-bind attribute. I can do it by channeling it through a Vue.js data variable, but calling a plain JavaScript function directly from v-bind produces an error.
http://jsfiddle.net/edwardtanguay/2st0fruh
How can I call a JavaScript function directly without having to map to a variable or method in Vue.js?
HTML
<div id="app">
<div>
The URL is: {{url}}
</div>
<div>
<a target="_blank" v-bind:href="url">goto URL</a>
</div>
<div>
<a target="_blank" v-bind:href="url2">goto URL2</a>
</div>
<div>
<a target="_blank" v-bind:href="getUrl()">goto URL2 using javascript function</a>
</div>
</div>
JavaScript
function getUrl() {
return 'http://www.amazon.com';
}
var vm = new Vue({
el: '#app',
data: {
url: 'http://www.google.com',
url2: getUrl()
}
});
In order for Vue to be able to bind itself, it needs to be part of the Vue instance. Whilst your function might be accessible under normal circumstances, it's not part of the vue instance and in turn can't be accessed.
As you have already stated, using a data prop moves it into scope of the vue instance. You could also use a computed prop for this if the value is likely to mutate.
As per this post from the vue author(Evan You) regarding using global functions:
Because implicitly falling back to globals will be a maintainability nightmare. When you look at a template you will have no idea whether a variable belongs to the component, or some globals defined by someone you have no idea where.
It's worth noting that if you feel this is something you want to do a lot, you could have a global mixin and specify some methods on there to get the Urls.
There is a way to do this but I don't recommend it:
See this fiddle
Basically, you add your own function to the Vue.prototype (this is how things like Vue Router and Vuex work):
Vue.prototype.$myFunc = function getUrl() {
return 'http://www.amazon.com';
}
Make sure you declare it before you create your var vm = new Vue() call.
Then you can access it via $myFunc() from the template or this.$myFunc() from the vue instance itself.

Dynamically insert child components inside vuejs2 data (without $compile or abusing v-html)

I'd like to insert new vuejs components on the fly, at arbitrary points within a block of not-necessarily-predefined HTML.
Here's a slightly contrived example that demonstrates the sort of thing I'm trying to do:
Vue.component('child', {
// pretend I do something useful
template: '<span>--><slot></slot><--</span>'
})
Vue.component('parent', {
data() {
return {
input: 'lorem',
text: '<p>Lorem ipsum dolor sit amet.</p><p><i>Lorem ipsum!</i></p>'
}
},
template: `<div>
Search: <input type='text' v-model="input"><br>
<hr>
This inserts the child component but doesn't render it
or the HTML:
<div>{{output}}</div>
<hr>
This renders the HTML but of course strips out the child component:
<div v-html="output"></div>
<hr>
(This is the child component, just to show that it's usable here:
<child>hello</child>)
<hr>
This is the goal: it renders both the input html
and the inserted child components:
TODO ¯\_(ツ)_/¯
</div>`,
computed: {
output() {
/* This is the wrong approach; what do I replace it with? */
var out = this.text;
if (this.input) {
this.input = this.input.replace(/[^a-zA-Z\s]/g,'');
var regex = new RegExp(this.input, "gi");
out = out.replace(regex, '<child><b>' + this.input + '</b></child>');
}
return out;
}
}
});
new Vue({
el: '#app'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.js"></script>
<div id="app">
<parent></parent>
</div>
In the above snippet, assume data.text is sanitized HTML. <child> is some sub-component that does something useful, which I want to wrap around chunks of data.text that aren't known ahead of time. (input is just for demo here. This MCVE doesn't really resemble the code I'm building, it's just an example that shows the sort of situation I'm stuck on.)
So: how would I change either the output function or the parent component's template, such that both the HTML from input and the inserted <child> templates are rendered properly?
What I've tried
In Vue 1, the answer to this would be a straightforward $compile. I'm using vuejs2 which removed $compile (out of justifiable concern that it made it too easy to naively introduce XSS vulnerabilities.)
v-html sanitizes what you feed it, which strips the child component out. Obviously this is not the way to do this. (That page suggests using partials instead, but I'm not sure how that could be applied to this situation; in any case partials have also been removed from vue2.)
I've tried passing the results of output() into another component which would then use it as its template. This seems like a promising approach, but I can't figure out how to change that secondary component's template. template only accepts a string, not a function like many of the other component properties, so I can't pass the template html in, say, a prop. Something like rewriting this.template inside beforeMount() or bind() would have been nice, but no joy there either. Is there some other way to replace a component's template string before it's mounted?
Unlike template, I can pass data to a component's render() function... but then I'm still stuck having to parse that html string into nested createElement functions. Which is exactly what Vue is doing internally in the first place; is there some way to hook into that here short of reinventing it myself?
Vue.component('foo', {
props: ['myInput'],
render(createElement) {
console.log(this.myInput); // this works...
// ...but how to parse the html in this.myInput into a usable render function?
// return createElement('div', this.myInput);
},
})
I wasn't able to cheat my around this with inline-template, either: <foo inline-template>{{$parent.output}}</foo> does exactly the same thing as a plain old {{output}}. In retrospect that should have been obvious, but it was worth a shot.
Maybe constructing an async component on the fly is the answer? This could clearly generate a component with an arbitrary template, but how would I reasonably call that from the parent component, and feed output to the constructor? (It would need to be reusable with different input, with multiple instances potentially visible simultaneously; no globals or singletons.)
I've even considered ridiculous stuff like having output() split the input into an array at the points where it would have inserted <child>, and then doing something like this in the main template:
...
<template v-for="chunk in output">
<span v-html="chunk"></span>
<child>...</child>
</template>
....
That would be doable, if laborious -- I'd have to split out what goes in the child's slot into a separate array too and get it by index during the v-for, but that could be done... if input were plain text instead of HTML. In splitting HTML I'll often wind up with unbalanced tags in each chunk, which can mess up the formatting when v-html rebalances it for me. And anyway this whole strategy feels like a bad hack; there must be a better way.
Maybe I just drop the whole input into a v-html and then (somehow) insert the child components at the proper positions through after-the-fact DOM manipulation? I haven't explored this option too deeply because it, too, feels like a hack, and the reverse of the data-driven strategy, but maybe it's a way to go if all else fails?
A couple of pre-emptive disclaimers
I'm very well aware of the XSS risks involved in $compile-like operations. Please be assured that none of what I'm doing involves unsanitized user input in any way; the user isn't inserting arbitrary component code, instead a component needs to insert child components at user-defined positions.
I'm reasonably confident that this is not an XY problem, that I really do need to insert components on the fly. (I hope it's obvious from the number of failed attempts and blind alleys I've run down that I've put more than a little thought into this one!) That said, if there's a different approach that leads to similar results, I'm all ears. The salient point is that I know which component I need to add, but I can't know ahead of time where to add it; that decision happens at run time.
If it's relevant, in real life I'm using the single-file component structure from vue-cli webpack template, not Vue.component() as in the samples above. Answers that don't stray too far from that structure are preferred, though anything that works will work.
Progress!
#BertEvans points out in comments that Vue.compile() is a thing that exists, which is an I-can't-believe-I-missed-that if ever there was one.
But I'm still having trouble using it without resorting to global variables as in that documentation. This renders, but hardcodes the template in a global:
var precompiled = Vue.compile('<span><child>test</child></span>');
Vue.component('test', {
render: precompiled.render,
staticRenderFns: precompiled.staticRenderFns
});
But various attempts to rejigger that into something that can accept an input property have been unsuccessful (the following for example throws "Error in render function: ReferenceError: _c is not defined", I assume because the staticRenderFns aren't ready to go when render needs them?
Vue.component('test', {
props: ['input'],
render() { return Vue.compile(this.input).render()},
staticRenderFns() {return Vue.compile(this.input).staticRenderFns()}
});
(It's not because there are two separate compile()s -- doing the precompile inside beforeMount() and then returning its render and staticRenderFns throws the same error.)
This really feels like it's on the right track but I'm just stuck on a dumb syntax error or the like...
As mentioned in the my comment above, $compile was removed, but Vue.compile is available in certain builds. Using that below works as I believe you intend except in a couple cases.
Vue.component('child', {
// pretend I do something useful
template: '<span>--><slot></slot><--</span>'
})
Vue.component('parent', {
data() {
return {
input: 'lorem',
text: '<div><p>Lorem ipsum dolor sit amet.</p><p><i>Lorem ipsum!</i></p></div>'
}
},
template: `<div>
Search: <input type='text' v-model="input"><br>
<hr>
<div><component :is="output"></component></div>
</div>`,
computed: {
output() {
if (!this.input)
return Vue.compile(this.text)
/* This is the wrong approach; what do I replace it with? */
var out = this.text;
if (this.input) {
this.input = this.input.replace(/[^a-zA-Z\s]/g,'');
var regex = new RegExp(this.input, "gi");
out = out.replace(regex, '<child><b>' + this.input + '</b></child>');
out = Vue.compile(out)
}
return out;
}
}
});
new Vue({
el: '#app'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.js"></script>
<div id="app">
<parent></parent>
</div>
You mentioned you are building with webpack and I believe the default for that build is Vue without the compiler, so you would need to modify it to use a different build.
I added a dynamic component to accept the results of the compiled output.
The sample text is not a valid template because it has more than one root. I added a wrapping div to make it a valid template.
One note: this will fail if the search term matches all or part of any of the HTML tags in the text. For example, if you enter "i", or "di" or "p" the results will not be what you expect and certain combinations will throw an error on compilation.
I'm posting this as a supplement to Bert Evans's answer, for the benefit of vue-cli webpack users who want to use .vue files instead of Vue.component(). (Which is to say, I'm mostly posting this so I'll be able to find this information when I inevitably forget it...)
Getting the right Vue build
In vue-cli 2 (and possibly 1?), to ensure Vue.compile will be available in the distribution build, confirm webpack.base.conf.js contains this line:
'vue$': 'vue/dist/vue.esm.js' // or vue/dist/vue.common.js for webpack1
instead of 'vue/dist/vue.runtime.esm.js'. (If you accepted the defaults when running vue init webpack you will already have the full standalone build. The "webpack-simple" template also sets the full standalone build.)
Vue-cli 3 works somewhat differently, and does not have Vue.compile available by default; here you'll need to add the runtimeCompiler rule to vue.config.js:
module.exports = {
/* (other config here) */
runtimeCompiler: true
};
The component
The "child" component can be a normal .vue file, nothing special about that.
A bare-bones version of the "parent" component would be:
<template>
<component :is="output"></component>
</template>
<script>
import Vue from 'vue';
import Child from './Child'; // normal .vue component import
export default {
name: 'Parent',
computed: {
output() {
var input = "<span>Arbitrary single-root HTML string that depends on <child></child>. This can come from anywhere; don't use unsanitized user input though...</span>";
var ret = Vue.compile(input);
ret.components = { Child }; // add any other necessary properties similarly
ret.methods = { /* ... */ } // like so
return ret;
}
}
};
</script>
(The only significant difference between this and the non-webpack version is importing the child, then declaring the component dependencies as ret.components: {Child} before returning it.)

How to bind to attribute in Vue JS?

I got this error
Interpolation inside attributes has been removed. Use v-bind or the
colon shorthand instead. For example, instead of <div id="{{ val }}">,
use <div :id="val">.
on this line
<a href="/Library/#Model.Username/{{myVueData.Id}}">
It works in Angular 1. How do you do it in Vue?
In your template:
<a :href="href">
And you put href in data:
new Vue({
// ...
data: {
href: 'your link'
}
})
Or use a computed property:
new Vue({
// ...
computed: {
href () {
return '/foo' + this.someValue + '/bar'
}
}
})
Just complementing ... solve the interpolation error (simple solution, I am Junior front-end developer):
Example post object in a loop:
instead of
<a href="{{post.buttonLinkExt}}">
try this way
<a v-bind:href="post.buttonLinkExt">
Use javascript code inside v-bind (or shortcut ":") :
:href="'/Library/#Model.Username' + myVueData.Id"
and
:id="'/Library/#Model.Username' + myVueData.Id"
Update Answer
Some directives can take an “argument”, denoted by a colon after the directive name. For example, the v-bind directive is used to reactively update an HTML attribute:
<a v-bind:href="url"></a>
Here href is the argument, which tells the v-bind directive to bind the element’s href attribute to the value of the expression url. You may have noticed this achieves the same result as an attribute interpolation using href="{{url}}": that is correct, and in fact, attribute interpolations are translated into v-bind bindings internally.
Found in Google this topic when searching $attrib.
Question don't specify what value is used (maybe not defined before)
For ANY parent attribute or to FILTER it, use something like that:
<template>
<component
is="div"
v-bind="$attrs"
class="bg-light-gray"
>
EXAMPLE
</component>
</template>
This instruct to create specific, dynamic and context aware, wrapper:
v-bind="$attrs" instruct to take all sended params. Not needed to declare as param object in script.
Work even with valid html attribute like class
example above mix static class with parent and join it. Use ternary operator (x=1?x:y) to choose proper one.
bonus: by "is" you can dynamically set tag like header or secion instead of div
$attrs can be binded to any tag in component so this easily enable simple transmission for one tag dynamic attributes like you define class for <input /> but wrapper and actions are added in component
Source with description: https://youtu.be/7lpemgMhi0k?t=1307
you can either use the shorthand : or v-bind
<div>
<img v-bind:src="linkAddress">
</div>
new Vue({
el: '#app',
data: {
linkAddress: 'http://i3.kym-cdn.com/photos/images/newsfeed/001/217/729/f9a.jpg'
}
});
or for when you need more than just binding an attribute you can also do:
new Vue({
el: '#app',
data: {
finishedLink: ' Google '
}
});

Vue JS rc-1 Passing Data Through Props Not Working

In the release notes for Vue 1.0.0-rc.1, we are told
"The inherit option has been deprecated. Alway pass data to child
components via props."
However, the Component API section says
"$data can no longer be used as a prop."
I have been trying to pass data to child components of my root Vue instance, and have had no luck whatsoever.
In version 0.12.*, if you want/need access to a parent instance's data, methods, etc., you would simply add...
inherit: true
...to a child component.
Now, in attempting to access the parent data via props, I continue to hit a brick wall. Here is a simplified example:
app.js:
new Vue({
el: '#app',
data: {
authorized: false,
currentView: 'welcome-view'
},
components: {
'welcome-view': require('./views/welcome')
}
});
views/welcome.js:
module.exports = {
props: ['authorized'],
template: require('./welcome.template.html')
};
views/welcome.template.html:
<div v-if="authorized"><p>You Are Logged In</p></div>
<div v-else>Please Log In</div>
Main View File (app.blade.php)
...
<body id="app">
<component :is="currentView"></component>
</body>
...
The 'authorized' prop is not recognized at all this way. It works outside of the component (within the "app" id) just fine, but not within the template.
At the moment, I can access the data I need by using $root everywhere I need it. For instance:
<div v-if="$root.authorized"><p>You Are Logged In</p></div>
But, my understanding is that this is 'bad form' all around, as the docs say:
Although it’s possible to access any instance the parent chain, you
should avoid directly relying on parent data in a child component and
prefer passing data down explicitly using props.
So, what I need to know is... how can I explicitly use props? I am clearly going about it the wrong way, since they are not available to my child components if I just list them in the 'props: []' array. What am I missing here?
At the end of the day, what is the best way (standards and practices) to refactor my current code to replace 'inherit: true', and still have access to the root instance data and functions? Any help/advice on this would be most welcome. Thanks in advance.
See #StephenHallgren's answer on this page for the correct way to access props in the HTML.
As for the rest of it, (how to properly refactor code to replace 'inherit:true', I am including here the answer I received from Evan Y. on the official Vue forum, in case anyone else runs across this in the future.
His answer to the question posed above was:
If you are fairly certain about the structure, you can use
$root.authorized.
Alternatively, don't put the authorized state in the root at all. Have
a dedicated module for user state that can be imported in any
component. See
http://rc.vuejs.org/guide/application.html#State_Management
My take-away from this is that - where there are concrete, global variables that will not change, and the app structure is sound, it is okay to use $root (or $parent as the case may be), and - where elements have state that will sometimes change (such as whether or not a user is authorized/logged in), the key is to use a state management module.
Meanwhile, when passing down props between parent and child, one must declare the props in the props array, then bind them to the component in the HTML.
For example...
app.js:
new Vue({
el: '#app',
data: {
authorized: false,
currentView: 'welcome-view'
},
components: {
'welcome-view': require('./views/welcome')
}
});
views/welcome.js:
module.exports = {
props: ['authorized'],
template: require('./welcome.template.html')
}
welcome.template.html:
<div v-if="authorized"><p>You Are Logged In</p></div>
<div v-else>Please Log In</div>
main HTML
<body id="app">
<component :is="currentView" v-bind:authorized="authorized"></component>
</body>
(or shorthand)
<body id="app">
<component :is="currentView" :authorized="authorized"></component>
</body>
I was having the same issue and didn't realize that you also have to bind the value to the component prop like this:
v-bind:authorized="authorized"
or the shorthand
:authorized="authorized"
Here's an example of something that I had been working on that illustrates the solution: http://jsfiddle.net/yyx990803/2uqmj2jj/6/