Vue component not rendered on second visit - vue.js

I have a Vue component that lists a bunch of clickable tags. When you click on a tag, it takes you to another page with a list of objects containing that tag.
The relevant parts of the component code are:
<template>
<div>
<h2>All Tags</h2>
<TagList v-bind:tags="tags"/>
</div>
</template>
...
<script>
import TagList from './TagList'
export default {
name: 'AllTags',
components: {
TagList
},
data () {
return {
tags: []
}
},
mounted () {
tags = // array loaded from a database
}
}
</script>
This all works fine when I initially view the page. However if I browse away from this list, e.g. by clicking on a single tag, and then browse back, I only see the <h2>All Tags</h2> header. Using the Vue debugger in the browser, I can see that the data are still there.
I'm using <router-view :key="$route.fullPath"> to control the overall app and suspect the problem lies with the keys somehow.
Can someone point me in the right direction here? How can I get the TagList component to render every time I visit that page of the app?
EDIT: Here's the code of the TagList component:
<template>
<div class="tags">
<Tag v-for="tag in tags" v-bind:tag="tag" v-bind:key="tag" />
</div>
</template>
<script>
import Tag from './Tag'
export default {
name: 'TagList',
props: ['tags'],
components: {
Tag
}
}
</script>

You can try removing v-bind all thought its not required to use, I've checked your code it seems to work fine after visiting a tag and going back, all tags are still rendered. You can take a look at this working sample .
https://codesandbox.io/s/vue-template-3tcs4?fontsize=14

Related

Component rendering just the element name not the template contents

I have just started with Vue and am having an issue where the component isn't rendering for me.
<template>
<div>
<GalleryCollectionBlueBottles />
</div>
</template>
<script>
import GalleryCollectionBlueBottles from '#/components/collections/GalleryCollectionBlueBottles.vue'
export default {
name: 'GalleryCollections'
}
</script>
When I inspect the page in a browser all I see is an element with the component name, not the contents of the component as usual.
The component above is called GalleryCollections and the component I'm importing is called GalleryCollectionBlueBottles.
Hope someone can help, also hoping this is something simple I've overlooked :)
You need to also declare the component inside your script tag within the keyword components{ ... } like;
<script>
import GalleryCollectionBlueBottles from '#/components/collections/GalleryCollectionBlueBottles.vue'
export default {
name: 'GalleryCollections',
components: {
GalleryCollectionBlueBottles
}
}
</script>

Work process for data passing in VueJS 2 application

I am pretty new to VueJS 2, so wanted to see if I am working in the correct way. I have a system where someone uploads a file that contains data, which will then be used to create charts. So I display the uploaded files to them
<tr v-for="file in files.data" :key="file.id">
//file information
<td>
<router-link :to="{ name: file.chart, params: { fileName: file.name }}"
tag="a" exact> View Results
</router-link>
</td>
</tr>
So you can see I have a link in the table, that directs them to the chart page for the file they uploaded. It includes the params for the file name to be loaded.
On the chart page, I get the params within the created method. I then pass these to the component for the chart to be displayed
<template>
<div>
//some information
<div class="row">
<div class="col-12" id="parentDiv">
<barchart :file-name = this.fileName></barchart>
</div>
</div>
</div>
</template>
<script>
import Barchart from '../charts/Barchart';
export default {
components: {
'barchart': Barchart
},
data() {
return {
fileName: ''
}
},
created() {
this.fileName = this.$route.params.fileName;
}
}
</script>
Finally, I have the Barchart component. This is what creates the chart based on the file uploaded data.
<script>
import * as d3 from 'd3';
export default {
props: {
fileName: {
type: String,
required: true
}
},
methods: {
createBarChart() {
//d3 to create chart using the file that was uploaded
}
},
created() {
let vm = this;
d3.json('storage/' + this.fileName)
.then(function (data) {
vm.createBarChart(data);
}).catch(function (error) {
// handle error
});
}
};
</script>
To me, there seems to be a lot of passing of data from one component to the next. I pass it from the files display component (which displays all uploaded files), then to the page for the chart, which then passes it to the chart component.
The other issue is, if I am on the charts page, and I refresh the page, then the chart no longer has the filename prop and therefore the chart does not render. How would I handle this
situation?
Any advice appreciated
The reason that you are losing the chart on refresh is due to the use of the created method.
In your chart component remove the entire created method and reference the route param directly in your barchart reference, like so:
<template>
<div>
//some information
<div class="row">
<div class="col-12" id="parentDiv">
<barchart :file-name="$route.params.fileName"></barchart>
</div>
</div>
</div>
</template>
<script>
import Barchart from '../charts/Barchart';
export default {
components: {
'barchart': Barchart
},
data() {
return {
}
}
}
</script>
You may want to look into vuex to manage the data passing from parent to some deeply nested child.
Before you decide you want to persist the file in the nested component, you may want to consider if this is good UX (does it make sense that when the user refreshes the page, the old file they had uploaded is still cached?) You can look into using localStorage to store things locally so that upon refresh, the data is still there without needing the user to re-enter it.

Passing div attribute in props give 503

I am using Trustpilot to make components, which uses different widget on same pages, so every template-id is different. Following is my code but I do not see anything expect TrustPilot logo but if I remove props and give the provided template-id, it works.
What am I doing wrong?
TheWidget.vue
<div class="trustpilot-widget"
data-locale="en-GB"
data-template-id="'${tid}'"
data-businessunit-id=""/omitted because of security
data-style-height="20px"
data-style-width="100%">
Trustpilot
</div>
<script>
export default {
props: ['tid'],
created() {
//Code omitted
}
}
</script>
Home.vue
<TheWidget tid="09765291018" />

vuejs Setting Dynamic path to template's src

I have an vue application which I have divided in components kinda manner seen below.
What I want is to bind src property in <template> like below so that I could have a dynamic path every time a user asks for different template to get loaded.
The .ts file will have same code in use for every different template. which prompt me to ask this question.
Please suggest a solution to it. Or am I going into right direction or not to achieve this ?
One way to achieve dynamic templates being rendered is using dynamic component rendering:
App.vue:
<template>
<div>
<button #click="selectedComponent = 'app-quote'">Quote</button>
<button #click="selectedComponent = 'app-author'">Author</button>
<button #click="selectedComponent = 'app-new'">New</button>
<hr>
<component :is="selectedComponent"></component>
</div>
</template>
<srcript>
import Quote from './components/Quote.vue'
import Author from './components/Author.vue'
import New from './components/New.vue'
data: function() {
return {
selectedComponent: 'app-quote'
}
},
components: {
'app-quote': Quote,
'app-author': Author,
'app-new': New
}
</script>

Create component with CSS selector?

Is it possible to create a Vue Component by passing a CSS selector instead of the name of a custom HTML tag? And, in turn, is it possible to use non-custom HTML tags for template placeholders?
I ask because I am wary of the SEO implications of custom HTML tags.
Not sure if i get your questions right but vuejs does not actually render these custom-tag like template-placeholders. It will transform all template-placeholders with their actual template. See the following example:
CustomComponent.vue
<template><div class="child">Hello World</div></template>
<script>
export default {
name: 'CustomComponent'
}
</script>
Parent Component:
<template>
<div class="parent"><custom-component></custom-component></div>
</template>
<script>
import CustomComponent from './CustomComponent'
export default {
components: {
CustomComponent
}
}
</script>
This will render a dom that looks something like this
<div class="parent"><div class="child">Hello World</div></div>
If you are really concerned about CEO I would recommend looking into server-side rendering. Otherwise all your view components are rendered using javascript execution on the client. Not sure if the search engine crawlers execute javascript or even if they do, how long they will wait for your page to render.
First yes it is possible to define a component using a selector or the element #id to be specific. However, it does not work quite as you are thinking if I understand correctly what you are wanting.
The method is not widely used or even well documented, link & link, but you can use what is known as an x-template. You define the component as follows.
Vue.component('my-cool-component', {
template: '#my-cool-component', //refers to script tag id
data() {
//
},
methods: {
//
}
});
Then you include the actual template markup in your html within an 'text/x-template' script tag with the template id set from your component.
<script type="text/x-template" id="my-cool-component">
<section>
<h1>Title</h1>
<p>...</p>
</section>
</script>
In the case of above you may use just standard html tags.
However to further clarify the second part of your question, you should be able to use custom html tags when naming your components in templates without concern because these are parsed out by Vue.js during rendering. For example if you were to write all you template markup directly in the component instead using template literals as follows,
Vue.component('my-cool-component', {
template: `<section>
<h1>Title</h1>
<p>...</p>
</section>',
data() {
//
},
methods: {
//
}
});
Then in your page markup when you include your custom html element tags <my-cool-component></my-cool-component> Vue will remove the tags and only render the template markup.
<section>
<h1>Title</h1>
<p>...</p>
</section>