Set Component prop for all instances / Pass same data to all Component instances - vue.js

I have a Component where each instance needs some data being passed onto it, that Component is used multiple times and all instances of the component should receive the same data/prop.
<my-component :someProp="someValue"></my-component>
<my-component :someProp="someValue"></my-component>
<my-component :someProp="someValue"></my-component>
<!-- ...and lots more... -->
This is my current method:
Which gets kind of redundant, how can I pre-populate this someProp for ALL instances of my component?
I tried Vue.extend() but cannot figure out what syntax it expects, the documentation is not clear enough on that part.
This is how I imagined it to work:
// App.vue
import MyComponent from './components/MyComponent'
const PreConfiguredComponent = Vue.extend(MyComponent, {
props: {
someProp: "someValue"
}
})
export default {
name: 'app',
data: () => ({}),
components: {
MyComponent: PreConfiguredComponent
}
}
You get the idea, I don't know how to express it better. Doesn't have to be done with props but I don't know other methods to pass data along.

There are quite a lot of different ways to approach this. I doubt this will be anything like an exhaustive list but I can give some sense of the possibilities.
1. Store the value globally
The obvious choice here is the Vuex store. Just put the relevant data in the store and then the components can grab what they need.
However, a global store doesn't have to be Vuex. If you have no other reason to introduce Vuex then you might prefer something more ad hoc.
An alternative to the store is to hold the data on $root, an approach described in the official documentation:
https://v2.vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch
A further alternative is just to have a .js file that can hold the data as a singleton. There are various ways to do it but in its simplest form it might be something like this:
export default {
myData: null
}
You'd then import that object and read/write the value of myData as required. It won't necessarily be reactive but I'm assuming that the initial value would be set at the start, before the application is created, and wouldn't change after that.
2. Store the value on the Vue prototype
This is quite similar to the above but I think it warrants a separate mention. It'd look something like this:
Vue.prototype.$myComponentData = 'someValue'
Then within any component you could access the value via the property $myComponentData.
Documentation: https://v2.vuejs.org/v2/cookbook/adding-instance-properties.html
3. Provide/Inject
The provide/inject mechanism is one of the lesser known Vue features but it provides an alternative to using props to pass data down to child components. It has various pros and cons and typically you'd try to use props instead.
https://v2.vuejs.org/v2/api/#provide-inject
In short, it allows a component to provide a named value to all of its descendants without needing to explicitly pass it to each one individually. The descendant components can then choose which named properties they would like to have injected.
You wouldn't be able to use provide/inject to pass different values to descendant components but in your case the value is the same so it should work.
If you think this approach might be for you then I suggest some further reading:
https://blog.logrocket.com/how-to-make-provide-inject-reactive/
4. Refactor to remove the duplication
While this is unlikely to be the solution you go with I do think it is worth mentioning.
The starting premise for the question seems to be that passing the prop explicitly is a form of duplication. Extra noise and extra effort with the potential to make mistakes.
Potentially we can remove that duplication while still passing the prop. The key here is to refactor the template so that the child component only features once.
Obviously that would need to be within a loop so that we still get all the desired components. That loop would need a suitable data structure to drive it so that all the right components get created.
I assume the template in the question is a simplification. If you really do have several instances consecutively then refactoring to use a v-for should be pretty trivial. If, as seems more likely, the components are nested in various places within the layout then it can get unwieldy trying to encode that in data structures just to avoid a bit of template duplication.
Hopefully the idea is clear but if not you could give this a read:
https://michaelnthiessen.com/reducing-redundant-repetition
5. Dynamic components
This is what's alluded to in the question. There are various ways to do it but the core of the idea is that we change our component definitions at runtime. That doesn't necessarily mean creating a new component, it could equally mean prodding something into an existing component definition. We've already seen a variation of this idea with the Vue.prototype approach mentioned earlier.
In theory it could be done with the default value of a prop but it seems unnecessary to use a prop unless that prop is going to be set from the outside in the usual way in some cases.
We could set it as a property using data but personally I would be tempted to use a variation of the Vue.prototype trick to add the property to the component's own prototype:
MyComponent = Vue.extend({
template: `<div>{{ value }}</div>`
})
MyComponent.prototype.value = 'some value'
new Vue({
el: '#app',
components: {
MyComponent
}
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<my-component></my-component>
</div>
Using the prototype is relatively cheap from a performance perspective. There are potential problems with reactivity but they would only apply if the value can change, which it can't in this case.
Just to illustrate the prop and data approaches explicitly:
MyComponent = {
template: `<div>{{ value }}</div>`
}
PreConfiguredComponent = Vue.extend({
extends: MyComponent,
props: {
value: {
default: 'some prop value'
}
}
})
new Vue({
el: '#app',
components: {
MyComponent: PreConfiguredComponent
}
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<my-component></my-component>
</div>
and:
MyComponent = {
template: `<div>{{ value }}</div>`
}
PreConfiguredComponent = Vue.extend({
extends: MyComponent,
data () {
return {
value: 'some data value'
}
}
})
new Vue({
el: '#app',
components: {
MyComponent: PreConfiguredComponent
}
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<my-component></my-component>
</div>
You could equally try to tweak the imported MyComponent directly rather than extending it.

Related

Vuejs how to use repeated methods in single components

How would I use the same method across different components without rewriting the same method for reach component. I looked into mixins but the documentation says 'Use global mixins sparsely and carefully'. So I'm wondering if there is a more ideal way for this approach. Same with global computed.
<template>
<div class="wrapper">
...
Link on many templates
...
</div>
</template>
<script>
export default {
data() {
return {}
},
methods: {
goToPage(page) {
return this.$store.commit('page/push', {page:page});
}
}
}
</script>
Thanks
Global mixins are not the only type of mixin. See https://v2.vuejs.org/v2/guide/mixins.html
If you want to add a method or computed property to every component then you'd use a global mixin. This would affect all components, including those from third party libraries. You'd need to be careful when choosing a name to ensure you don't collide with anything else. There's also a small performance overhead from using a global mixin. As an example, Vuex uses a global mixin to ensure that the $store property is present on all components.
If you only need to add the method/property to a few components then you'd be much better off with a normal mixin. Typically that would have its own file and look something like this:
// my-mixin.js
export default {
methods: {
goToPage(page) {
return this.$store.commit('page/push', {page:page});
}
}
}
and then within your .vue files:
<script>
import myMixin from 'my-mixin'
export default {
mixins: [myMixin],
// ... all the other options
}
</script>
Given the example in the question seems to be a navigational link, an alternative to using a mixin might be to introduce a suitable component to handle those links. Rather than sharing code between components you'd just use the link component. It would depend on whether the method has uses beyond those links.
There are alternatives, such as sharing things globally across components using Vue.prototype, but for the example given in the question that doesn't seem a good fit.
I would also note that Vue 3 introduces some new alternatives to mixins via the composition API. However, it isn't immediately obvious that using composition would actually be an improvement in your specific use case. Vue 3 is also still in beta.

How to Initialize Data Properties with Prop Values

Still a little bit young in VueJS but I'm loving every bit of it. But now, fixated somewhere.
I want to initialize some values in data() using values passed via props. This is so that I can be able to mutate them later on, since it is not recommended to mutate props inside a component. In fact the official docs recommend this property initialization using prop values as shown below:
{
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
I have something like the one below:
<template>
<div class="well">
<!-- Use Prop value directly on the template: works (but of no help in initializing data) -->
Department: {{department.name}}
<!-- Use prop value but gotten via computed property: Works inside the template but not in the initialization -->
Department: {{fetchDepartment.name}}
<!-- Use the array I initialized with the prop value: Does not work -->
Department: {{this_department.name}}
</div>
</template>
<script>
export default {
name: 'test',
props: ['department'],
data() {
return {
this_department: this.department
// below does not work either
//this_department: this.fetchDepartment
}
},
created() {
// shows empty array
console.log(this.department)
},
mounted() {
// shows empty array
console.log(this.department)
},
computed: {
fetchDepartment() {
return this.department
}
}
}
</script>
As seen in the commented sections above, the initialization is not successful. Neither does the value of this.department appear either from the created() or the mounted() hooks. And note, I can see it is defined using the Chrome Vue Devtools. So my question is, how exactly should I initialize data() attributes using props values, or which is the best way of going around this issue?
I know my answer comes in late but it helps me and hopefully someone else coming here. When props' data are async:
// in the parent component
<template>
<child :foo="bar" v-if="bar" />
</template>
That way, you render the component when props are already available making it safer to follow the guide's recommended ways to initialize data value with props as so:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
Happy coding!
You CAN modify a prop. Use the '.sync' modifier. I use it frequently because it is convenient and intuitive. This requires emitting an event to update the value on the parent. I am not really sure the warning of how it results in maintenance issues.
Another method I use if I want to modify a value and not update the parent is using Lodash clone. For example (assuming its available on mounted)
mounted(){
this_department = _.clone(this.department)
}
If you consistently want to mutate the prop and have it change with the parent, then use a computed property. However, in most cases you will want to depend on the state of that data within the component and change it using other functions and thus a computed property will not be what you need.
A computed property is the simplest way to provide a mutable version of a prop, but you might not want to lose data when the prop is updated. You could use an explicit watch.
Watchers
While computed properties are more appropriate in most cases, there
are times when a custom watcher is necessary. That’s why Vue provides
a more generic way to react to data changes through the watch option.
This is most useful when you want to perform asynchronous or expensive
operations in response to changing data.
This is most useful when you want to perform asynchronous or expensive
operations in response to changing data.

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.)

VUE.JS - Using the vNode argument to add unknown quantity of items to a dynamic navigation

I'm currently trying to create a component which manages several linked dropdowns and elements on a page. In addition, this element supplies a rather fancy navigation element, containing anchor links which automatically scroll to the desired element in this component.
The problem is that the actual contents of the component are completely dynamic and partially determined by the content manager in the CMS. There are several sub components that are always present, but apart from that the content manager can add any number of sections, (using various named and an unnamed ) and each of these sections should be added to the navigation of the component.
I see 3 options:
For every component added, it's title and unique id is added to a property array on the parent component. This, however, is a bit prone to errors. (Unfortunately I will have no control over the eventual backend implementation, so I'm trying to create a foolproof system to avoid to much wasted time.)
Due to other components, I'm already using Vuex to manage most of the data in the app. I figured I use a simple directive, to be added on each item in the parent component. This directive is responsible for adding that element to the Vuex store. The parent component simply reads the contents of the store and generates the navigation based on this.
The problem here is that, as far as I can tell, I have to use the vNode argument in the bind hook of my directive to access the Vuex store. This seems... hacky. Is there anything wrong with this approach?
In the mounted hook of my parent component, I traverse the DOM, searching for elements with a particular data-property, and add a link to each of these elements to my navigation. Seems prone to failure and fragile.
What is the preferred approach for this situation?
As a follow up question - What is the correct use case for the vNode argument in a vue directive? The documentation seems rather sparse on this subject.
I would steer away from using a directive in this case. In Vue 2, the primary use case for directives is for low level DOM manipulation.
Note that in Vue 2.0, the primary form of code reuse and abstraction
is components - however there may be cases where you just need some
low-level DOM access on plain elements, and this is where custom
directives would still be useful.
Instead I would suggest a mixin approach, where your mixin essentially registers your components that should be included in navigation with Vuex.
Consider the following code.
const NavMixin = {
computed:{
navElement(){
return this.$el
},
title(){
return this.$vnode.tag.split("-").pop()
}
},
mounted(){
this.$store.commit('addLink', {
element: this.navElement,
title: this.title
})
}
}
This mixin defines a couple of computed values that determine the element that should be used for navigation and the title of the component. Obviously the title is a placeholder and you should modify it to suit your needs. The mounted hook registers the component with Vue. Should a component need a custom title or navElement, mixed in computed properties are overridden by the component's definition.
Next I define my components and use the mixin.
Vue.component("child1",{
mixins:[NavMixin],
template:`<h1>I am child1</h1>`
})
Vue.component("child2",{
mixins:[NavMixin],
template:`<h1>I am child2</h1>`
})
Vue.component("child3",{
template:`<h1>I am child3</h1>`
})
Note that here I am not adding the mixin to the third component, because I could conceive of a situation where you may not want all components included in navigation.
Here is a quick example of usage.
console.clear()
const store = new Vuex.Store({
state: {
links: []
},
mutations: {
addLink (state, link) {
state.links.push(link)
}
}
})
const NavMixin = {
computed:{
navElement(){
return this.$el
},
title(){
return this.$vnode.tag.split("-").pop()
}
},
mounted(){
this.$store.commit('addLink', {
element: this.navElement,
title: this.title
})
}
}
Vue.component("child1",{
mixins:[NavMixin],
template:`<h1>I am child1</h1>`,
})
Vue.component("child2",{
mixins:[NavMixin],
template:`<h1>I am child2</h1>`
})
Vue.component("child3",{
template:`<h1>I am child3</h1>`
})
Vue.component("container",{
template:`
<div>
<button v-for="link in $store.state.links" #click="scroll(link)">{{link.title}}</button>
<slot></slot>
</div>
`,
methods:{
scroll(link){
document.querySelector("body").scrollTop = link.element.offsetTop
}
},
})
new Vue({
el:"#app",
store
})
h1{
height:300px
}
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/2.3.1/vuex.js"></script>
<div id="app">
<container>
<child1></child1>
<child3></child3>
<child2></child2>
</container>
</div>
This solution is pretty robust. You do not need to parse anything. You have control over which components are added to the navigation. You handle potentially nested components. You said you don't know which types of components will be added, but you should have control over the definition of the components that will be used, which means its relatively simple to include the mixin.

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/