How can I pass data from .js to .vue? - javascript

I am trying to load data into my .vue component file from its parent .js file.
This is my .html file:
<div class="modal-container">
<section class="modal">
<modal-component></modal-component>
</section>
</div>
This is my .js file:
var modal = new Vue({
el: 'section.modal',
data: {
msg: 'Hello world!'
},
components: {
'modal-component': httpVueLoader('./templates/test.vue')
}
});
This is my .vue file:
<template>
<section>
<p class="test">{{ msg }}</p>
</section>
</template>
However, when I load the page, this error appears in the console and the value of 'msg' is blank:
[Vue warn]: Property or method "msg" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.

There are two different Vue instances in play here. The one you create directly using new Vue has a property called msg. The instance corresponding to the child component, modal-component, does not.
There are 3 main types of properties in Vue. data, computed and props.
If you have a property defined on the parent in data then you can pass it down to the child using a prop.
<modal-component :msg="msg"></modal-component>
In your .vue file you would then need to define msg as a prop.
props: ['msg']
Properties are not automatically inherited, you always need to pass them down using props.
Documentation: https://v2.vuejs.org/v2/guide/components.html#Passing-Data-to-Child-Components-with-Props
Without wishing to muddy the waters, this particular example might be better served using a slot but as it's just an example it's difficult to say for sure whether that would really be appropriate.
Update:
In full the file test.vue would be:
<template>
<section>
<p class="test">{{ msg }}</p>
</section>
</template>
<script>
export default {
name: 'modal-component',
props: ['msg']
}
</script>

Related

Vue template isn't rendering in for loop

So after following a beginner Vue tutorial to setup a Todo app, I decided to try to adapt some parts of it for a website I'm trying to make. What I'm stuck on is that despite everything saying my for-loop is supposed to work, it doesn't.
The project itself was created using the vue-cli, and most of the code copy-pasted from the tutorial. (which is working fine with its own for-loop)
It seems like the data might be not passed onto the template maybe?
I have tried:
having the info inside the props and data sections
passing whole object and only parameters to the template
tried with hard-coded values inside array which is iterated on
(After setting up a new vue-cli project:)
App.vue:
<template>
<div id="app">
<create-section v-on:create-section="addSection" />
<section v-for="section in sections" v-bind:key="section.title" :info="section"></section>
</div>
</template>
<script>
import CreateSection from "./components/CreateSection";
import Section from "./components/Section";
export default {
name: "App",
components: {
CreateSection,
Section
},
data() {
return {
sections: []
};
},
methods: {
addSection(section) {
this.sections.push({
title: section.title,
description: section.description
});
console.log(
"Added to sections! : " + section.title + " | " + section.description
);
console.log("Sections length: " + this.sections.length);
}
}
};
</script>
Section.vue
<template>
<div class="ui centered card">
<div class="content">
<div class="header">{{ info.title }}</div>
<div>{{ info.description }}</div>
</div>
</div>
</template>
<script type = "text/javascript" >
export default {
props: {info: Object},
data() {
return {};
}
};
</script>
Expected result:
Display Section template on the website (after creating it with addSection that another script calls. Not included for brevity)
Actual result:
Nothing is displayed, only a empty tag is added
I believe the problem is that you've called it Section. As <section> is a standard HTML element you can't use it as a component name.
There is a warning built into the library but it seems to be case sensitive, which isn't entirely helpful. Try changing your components section to this:
components: {
CreateSection,
section: Section
},
You should then see the warning.
The fix would just be to call it something else.
This is mentioned in the first entry in the style guide:
https://v2.vuejs.org/v2/style-guide/#Multi-word-component-names-essential
section is an existing HTML5 element, you should name your section component something different.
If you really want to name the component Section, register it as 'v-section'
The problem is that when you do the loop in the <section v-for="section in sections" v-bind:key="section.title" :info="section"></section> the Array sections is not ready, there is nothing there.. so when you add new things to this array you need to trigger (computed prop) to send again the data to the section component.
Aside from the issue with using an existing HTML5 command as a name for your Vue component (you should change that to another name by the way), you should also look into how you declared the props within Section.vue. The code below shows the correct way to do it:
<script type = "text/javascript" >
export default {
props: ['info'],
data() {
return {};
}
};
</script>
The props take in the name of the property being declared from the parent component and should be a string.
Hope this helps.

Include component from parent app in component contained in node_modules directory

I am working on Vue app that incorporates Vue Bootstrap Calendar, and I would like to be able to override the content of the day cell (handled by the Day.vue component) to add my own custom content inside. My thought was initially to modify the Day component to include <slot></slot> tags and pass in the custom content that way.
The problem has to do with accessing the Day component. To include the calendar in your app, you include the Calendar.vue component, which includes Week.vue, which in turn includes Day.vue. As I understand slots, I have to have the child component (Day.vue in this case) included in the component where I'm passing the data, which means it would need to be included in my own component.
If this is not possible, my other thought is to perhaps modify the library by adding another configuration prop (something like dayCustomContent) to the Calendar.vue that indicates that the Day cell content is custom content, pass that in to Calendar.vue, and then down to Day.vue, and then in the Day.vue template, have a v-if conditional based on this prop that either displays the custom content or the default cell content, something like:
<template>
<div class="day-cell" v-if="dayCustomContent">
...my custom content here...
</div>
<div class="day-cell" v-else>
...default events from my.events goes here...
</div>
</template>
I would probably then need to define a custom component to render whatever custom content I want to display, and somehow include that component within Day.vue.
So to sum up, my questions are these:
1) Is there a way to do what I need with slots?
2) For my second option, am I going down the right path? I'm open to suggestions.
UPDATE: I was able to get this done by adding a boolean customDayContent prop in Calendar.vue like so and passing it down to Week.vue and then to Day.vue:
<template>
...
<div class="dates" ref="dates">
<Week
v-for="(week, index) in Weeks"
:firstDay="firstDay"
:key="week + index"
:week="week"
:canAddEvent="canAddEvent"
:canDeleteEvent="canDeleteEvent"
:customDayContent="customDayContent"
:displayWeekNumber="displayWeekNumber"
#eventAdded="eventAdded"
#eventDeleted="eventDeleted"
></Week>
</div>
...
</template>
<script>
export default {
...
props: {
...
customDayContent: {
type: Boolean,
default: false
}
},
}
</script>
and then in Day.vue, do like I had suggested with v-if:
<template>
<div class="day-cell" v-if="customDayContent">
<custom-day></custom-day>
</div>
<div
class="day-cell"
:class="{'today' : day.isToday, 'current-month' : day.isCurrentMonth, 'weekend': day.isWeekEnd, 'selected-day':isDaySelected}"
#click="showDayOptions"
v-else
>
... existing code goes here...
</div>
</template>
The last part is referencing the CustomDay.vue component referenced in my v-if block. I want the user to be able to define the content of their own custom CustomDay.vue template in their own parent app. However, I'm having trouble trying to figure out how to do that. Following the pattern of including components already in this component, I added this in the components section of Day.vue:
CustomDay: require("../../../../src/Components/CustomDay.vue").default
? require("../../../../src/Components/CustomDay.vue").default
: require("../../../../src/Components/CustomDay.vue")
However, no matter what I try along these lines, I get an error that the relative module was not found. On top of that, I need to add it to the componentsarray only if customDayContent is true. What is the best way to do that? In a watcher or computer property, perhaps? Or another way?

Using props to set different text on every page in vue.js

I am using laravel + vue. I want to do title of page in navbar, so when you are in index, in navbar is text index. When you are in settings, navbar says settings etc.
I think props is good for it, but when I use that, it works not good. Look here:
blade.php of index:
#extends('layout.app')
#section('content')
<index :msg="msg"></index>
#endsection
index.vue:
props: [
'msg'
],
Now navbar:
<template>
<nav class="navbar">
<a></a>
<p>{{msg}}</p>
</nav>
</template>
<script>
export default {
props: [
],
data() {
return {
}
},
}
</script>
and layout:
<body>
<div id="app">
<navbar></navbar>
#yield('content')
</div>
</body>
How I can change that {{msg}} paragraph in navbar when we are on different pages? My code doesn't work.
[Vue warn]: Property or method "msg" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
If you want to use a prop then you need to define it in the props object of your component. In your NavBar you are referencing to msg, but the props object in NavBar is empty. Define it and pass the prop in your layout.blade.php.
Or you could also define a computed property where you take a look at the current route and return a string fitting your business.
https://v2.vuejs.org/v2/guide/computed.html
If you want to share data between multiple components, then use a store (VUEX) as proposed :)

Using custom components as router-link tag in Vue

I'm trying to make the following Vue component work:
<template>
<div>
<cx-button href="/new">
Create
</cx-button>
<router-link tag="cx-button" to="/new" class="cx-raised">Create</router-link>
</div>
</template>
<script>
import cxButton from '../elements/cxButton/cxButton';
export default {
// ...
components: {
cxButton
}
}
</script>
But it throws: [Vue warn]: Unknown custom element: <cx-button> - did you register the component correctly? For recursive components, make sure to provide the "name" option. found in ---> <RouterLink>.
Name of the button element is provided.
cx-button component is rendered fine by itself, same for router-link without using cx-button as value in tag property. The question is: what should I do to make router-link use custom elements, such as my button, as its tag?

Vue.js mounted function not accessing component properties

I'm not very new to Vue.js which is probably why I feel like I've been running mad all morning :). While creating a component, which I usually do, quite frequently, in this case, I had to initialize Google Maps within the mounted function, which seems like the right place to do that. In the mounted function, I would access the id property of a nested input field and attach an event listener to it. Pretty simple right?
Well, I figured that when I try to use the component multiple times on my page, I'm somehow accessing the same (seemingly shared) this variable within the mounted function.
Not sure why exactly this happens and/or if it's a feature but to make it even weirder, the props yield correct values within the template. (and within the methods as well)
Component Definition
<template>
<div class="LocationInput">
<input
type="text"
:id="id"
/>
</div>
</template>
<script>
export default {
name: 'LocationInput',
props: ['id'],
mounted() {
console.log('Component object representation ==> ', this)
console.log('ID ==> ', this.id)
}
}
</script>
Using my component...
<template>
<div class="MyTravelApp">
<LocationInput id="id1"/>
<LocationInput id="id2"/>
</div>
</template>
<script>
import LocationInput from './components/LocationInput';
export default {
components: { LocationInput }
}
</script>
What I get at the end of the day is the correct id values in the template but in my console, the exact same object and id are logged as you can see below. Notice how the _uid property is the same thing for both.
To make matters even worse, after modifying the this variable in the mounted function, while inspecting, I observed that the second component has that property modified as well. So they are essentially sharing the same object, which is extremely weird.
I would like to know if anyone has had similar issues and how to deal with it.
No self-closing tags for components.
Vue templates need to be valid HTML. There are no "self closing tags"
in HTML5, it's an XHTML syntax which is now outdated and you should
never use it.
(Later note:)
FYI self-closing tags works in 2.0 as long as you don't use in-dom
templates.
You may also be having an issue with camelCase vs. kebab-case. The snippet below behaves as expected.
Vue.component('locationInput', {
template: '#location-input-template',
props: ['id'],
mounted() {
console.log('Component object representation ==> ', this._uid)
console.log('ID ==> ', this.id)
}
});
new Vue({
el: '#my-travel-app'
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.6/vue.min.js"></script>
<template id="location-input-template">
<div class="LocationInput">
<input type="text" :id="id">
</div>
</template>
<div id="my-travel-app">
<location-input id="id1"></location-input>
<location-input id="id2"></location-input>
</div>

Categories

Resources