Vue template isn't rendering in for loop - javascript

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.

Related

(Nuxt) Vue component doesn't show up until page refresh

I'm storing nav items in my Vuex store and iterating over them for conditional output, in the form of a Vue/Bulma component, as follows:
<b-navbar-item
v-for='(obj, token) in $store.state.nav'
v-if='privatePage'
class=nav-link
tag=NuxtLink
:to=token
:key=token
>
{{obj.text}}
</b-navbar-item>
As shown, it should be output only if the component's privatePage data item resolves to true, which it does:
export default {
data: ctx => ({
privatePage: ctx.$store.state.privateRoutes.includes(ctx.$route.name)
})
}
The problem I have is when I run the dev server (with ssr: false) the component doesn't show up initially when I navigate to the page via a NuxtLink tag. If I navigate to the page manually, or refresh it, the component shows.
I've seen this before in Nuxt and am not sure what causes it. Does anyone know?
recommendation :
use mapState and other vuex mapping helper to have more readable code :).
dont use v-for and v-if at the same element
use "nuxt-link" for your tag
use / for to (if your addresses dont have trailing slash)
<template v-if='privatePage'>
<b-navbar-item
v-for='(obj, token) in nav'
class=nav-link
tag="nuxt-link"
:to="token" Or "`/${token}`"
:key="token"
>
{{obj.text}}
</b-navbar-item>
</template>
and in your script :
<script>
import {mapState} from 'vuex'
export default{
data(){
return {
privatePage: false
}
},
computed:{
...mapState(['privateRoutes','nav'])
},
mounted(){
// it's better to use name as a query or params to the $route
this.privatePage = this.privateRoutes.includes(this.$route.name)
}
}
</script>
and finally if it couldn't have help you , I suggest to inspect your page via dev tools and see what is the rendered component in html. it should be an <a> tag with href property. In addition, I think you can add the link address (that work with refresh and not by nuxt link) to your question, because maybe the created href is not true in navbar-item.
NOTE: token is index of nav array . so your url with be for example yourSite.com/1.so it's what you want?
This question has been answered here: https://stackoverflow.com/a/72500720/12747502
In addition, the solution to my problem was a commented part of my HTML that was outside the wrapper div.
Example:
<template>
<!-- <div>THIS CREATES THE PROBLEM</div> -->
<div id='wrapper'> main content here </div>
</template>
Correct way:
<template>
<div id='wrapper'>
<!-- <div>THIS CREATES THE PROBLEM</div> -->
main content here
</div>
</template>

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

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>

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

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>

Vue.js 2.0: Apply vue component rendered using v-html, compile the markup

I'm using VueJS 2.0
Is there any way to make the below render as a link?
Here is my vue component:
<template>
<div v-html="markup"></div>
</template>
<script>
new Vue({
data() {
return {
markup: '<router-link :to="{path: 'https://www.google.com'}"></router-link>',
});
},
});
</script>
In the above example, I want to dynamically export a piece of markup, it contains some dynamic contents, such as router-link like above.
But that content did not compile, and exports a <router-link> tag as a final result.
Any way to make it compile programmatically?
What I really want is to find a way to compile a piece of html manually. If v-html doesn`t work, Is there any other way?
v-html works only for pre-compiled html which is basically generated text.
If you want do dynamically change content, simply use if conditions to render your list view based on prop that will tell you the type of the list view.
I don't think it's a good idea to save the markup in your db. It's rather more convenient to save some settings in your db and based on those to render the necessary html. (the prop type in your case). Maybe if you provide a more concrete example, some suggestions will follow. As you can see, the answers were based on your router-link example which I think is not enough to answer your question
I don't think you can instantiate Vue instances via v-html directive. You must override the default to do that, which would take lots of efforts.
If you just want dynamic links, why not try this:
data: {
menu: []
}
and then :
<router-link v-for="item in menu" :to="item.src">{{item.name}}</router-link>
PS: Can you give an example that you must do such things? I am really interesting in what needs it would be.
Given that you want to render a list of links, one way to do this can be like this:
<template>
<router-link v-for="list in lists" :to="{path: list}"></router-link>
</template>
<script>
new Vue({
data() {
return {
lists: ['https://www.google.com', 'https://www.stackoverflow.com']
});
},
});
</script>
Edit:
You can use an approach like following as well using with the help of dynamic components.
Vue.use(VueRouter)
new Vue({
el: "#app",
data: {
dynamicComp: "router-link"
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/2.2.0/vue-router.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js"></script>
<div id="app">
somethind
<component :is="dynamicComp" :to="{path: 'https://www.google.com'}"></component>
</div>

Categories

Resources