How to reload data from a component in Vue? - javascript

I have App.vue component and its child ItemsList.vue.
In App.vue I have this:
export default {
name: 'App',
components: {
ItemsList
},
data() {
return {
items_list: [
getRandomItem(),
getRandomItem(),
getRandomItem(),
]
}
}
}
The data is an array of randomly generated items of the same structure.
I want to return a new array of newly generated items when clicking a button but as I see, data() method is being called only once on start and then return just what was generated at the moment.
How do I rerun this method to return a new dataset?

The data() method is called only once when the instance of the component is created.
It's a bit like a lifecycle hook.
You can use a function to generate a new array and replace the items_list of the component
data () {
return {
items_list: []
}
},
methods {
newList () {
this.items_list = [
getRandomItem(),
getRandomItem(),
getRandomItem(),
]
}
},
mounted () {
this.newList()
}
Documentation
Component's data function
Vuejs - Lifecycle Diagram

Related

Vue3 - how to create an instance of component and pass arguments

I have a following use case: I have a form and when submitting I want to add another (new) Vue 3 component as an item (li) into the unordered list (ul). This works for me when doing that this way:
data() {
return {
some_list: []
}
},
methods: {
doSomething(){
this.some_list.push(
MyComponent
);
}
}
BUT, I need to pass some arguments to that component as well. My component has got some properties defined. I just do not know HOW to pass those arguments. Just for information: I am using Vue 3 with Javascript, not Typescript.
I think the v-bind directive would help with that
data() {
return {
some_list: []
}
},
methods: {
doSomething(){
this.some_list.push(
{component:MyComponent, props:{...theprops}}
);
}
}
<v-for="{component, props} in some_list">
<component :is="component" v-bind="props"></component>
</v-for>
references
https://vuejs.org/guide/essentials/component-basics.html#dynamic-components
https://vuejs.org/api/built-in-directives.html#v-bind

Dynamic component render in Vue for v-select

I am trying to load v-select (vuetify) component dynamically using a JSON object which contains different properties
I have multiple select components where options will be provided by an API, I want to load v-select component dynamically each time as this schema can be used for other components as well such as v-text-field
Problem is the genders array (other similar arrays as well such as departments and managers) will be provided by an API hence it needs to be dynamically loaded on to v-select component but using vm (View Modal) I can't do this.
I have tried using computed properties and methods to retrieve genders array, but no luck.
Vue
<component :is="schema.component" v-model="jsonData[schema.value]" v-bind="schema.props" v-on="schema.on">
Script
<script>
import {
VSelect
} from 'vuetify/lib';
export default {
components: {
VSelect
},
data() {
const vm = this;
return {
genders:['Male', 'Female', 'Other'],
jsonData:{},
schema: {
component: 'v-select',
value: 'GENDER',
props: {
items: vm.genders
},
on: {
}
}
}
}
}
</script>
The problem is that in vm is not what you expect. Use computed properties when you want to set value depending on another variable of the component.
data() {
return {
genders:['Male', 'Female', 'Other'],
jsonData:{}
}
},
computed: {
schema () {
return {
component: 'v-select',
value: 'GENDER',
props: { items: this.genders },
on: {}
}
}
}

Accessing an exported components data() in Vue.js

I'm just playing around with some component instance rendering within a Vue application and I was wondering, when pushing components to an array - how do we then access the data() from that given instance of the component?
So say I have something like this in App.vue (the "Grandfather" of all of my components). I have managed to push instances of both the CodeBlock and QuoteBlock components to the pageBlocks array (i.e., my front end app appends the component where I want it to be). Here is a snippet of my App.vue file:
components: {
CodeBlock,
QuoteBlock
},
data () {
return {
pageBlocks: []
}
},
methods: {
addPageBlock (componentNomen) {
this.pageBlocks.push({ componentName: componentNomen })
},
saveDraftPage () {
for (let pageBlock of this.pageBlocks) {
console.log(pageBlock.data)
}
}
}
And here is an example of my CodeBlock data (the Quote block is "modelled" almost like for like except for a few variable name changes to distinguish it inside the component):
export default {
name: 'CodeBlock',
props: [ 'type' ],
computed: {},
data () {
return {
debug: true,
codeBlock: null,
codeBlockRows: [{
'id': 1,
'text': '$ click to edit this code block'
}],
}
},
}
I've stripped out most of this component to keep things simple.
So, my question is, if the pageBlocks array in App.vue contains instances of the above exported component...how do I access the data within?
In my naiavity I thought it would be as simple as something like this:
for (let pageBlock of this.pageBlocks) {
console.log(pageBlock.data);
}
But, alas, no luck yet...any tips?

How to create pagination as child component that reusable in VueJS

Currently i am using Element-UI for rapid development in my apps and i want to create pagination as child component, so i can reusable it in any parent component and reduce the size of my app.
Example for my parent component that has 'inline' pagination.
parent.vue
<template>
<ol>
<li v-for="student in studentList>{{ student.full_name }}</li>
</ol>
<el-pagination
layout="sizes, prev, pager, next"
:total="totalPages"
:page-size="pageSize"
:page-sizes="[10, 25, 50, 100]"
#size-change="handleSizeChange"
#current-change="handleCurrentChange">
</el-pagination>
</template>
<script>
export default {
data () {
return {
pageSize: 10,
currentPage: 1,
students: [] // this filled from backend when component created
}
},
created () {
// Axios HTTP request to fetch data from server
// to filled students variable
},
computed: {
totalPages () {
return this.students.length
},
studentList () {
return this.students.slice(((this.currentPage - 1) * this.pageSize), (this.pageSize * this.currentPage))
}
},
methods: {
handleSizeChange (size) {
this.pageSize = size
},
handleCurrentChange (value) {
this.currentPage = value
}
}
}
</script>
It works very well, but it can be pain because I should repeat all of that stuff in any component that want to use pagination.
Anyone can give me example how to create this pagination as child component while the collections still remain on parent component.
I've already try to create it, but stuck to how pass returned value from computed property to parent component.
Example, my failed trial create child component
child.vue
... // all of template element and functions
computed: {
studentList () {}
},
methods: {
updateStudentList () {
this.$emit('changed-value', this.studentList) // as you can see, studentList is computed function like parent code above
}
}
parent.vue
// there's no more studentList function on computed property in parent component
// instead, it moved on child component
<li v-for="student in students">{{ student.full_name }}</li>
<child-pagination v-on:changed-value="rebuildStudent"></child-pagination>
...
methods: {
rebuildStudent (newCollection) {
this.students = newCollection
}
}
UPDATED
After tinkering a bit, finally i can solve it.
Computed array should be keep on parent component, and add an object that store begin and end index for sliced on collection array. Which that object will changed based on child events using methods.
parent.vue
<template>
<pagination v-model="pageIndex"
v-on:paginationInit="paginationInit"
collections="students">
</pagination>
</template>
data () {
return {
students: [] // collection
pageIndex: {}
}
},
computed: {
studentList () {
return this.students.slice(pageIndex.begin, pageIndex.end)
}
},
methods: {
// This method emitted from child component
paginationInit (pageIndex) {
this.pageIndex.begin = pageIndex.begin
this.pageIndex.end = pageIndex.end
}
}
And then in child component, the computed logic moved on a method here that handle event from clicked pagination element.
child.vue
data () {
return {
pageIndex: { begin: 0, end: 10 }
}
},
created () {
this.init()
},
methods: {
init () {
this.$emit('paginationInit', this.pageIndex)
},
handleCurrentChange (page) {
this.pageIndex.begin = (page - 1) * this.pageSize
this.pageIndex.end = this.pageSize * page
this.$emit('input', this.pageIndex)
}
}

Vue reactive props on programmatic component

Given a component:
Vue.component('my-comp', {
props: ['input'],
watch: { input: function(){...} },
});
What is the programmatic method for the following?
<my-comp :input="map[key]"></my-comp> map[key] change triggers watch
I have tried:
new (Vue.component('my-comp'))({
propsData: { input:map[key] }, // map[key] change doesn't trigger watch
});
The context for this is inserting zero-to-many components into markdown-generated HTML. I call .$mount() for each component, and move its node with a native DOM replaceChild() call when markdown is re-rendered. See also Vue components in user-defined markdown
If prop input is a primitive value, we have to manipulate the component with child.$props.input = x as Roy J suggests, but in this case we need input = map[key]. Hence this solution:
Vue.component('my-comp', {
props: ['map','key'],
computed: { input: function() { return this.map[this.key] } },
watch: { input: function(a, b) {...} }, // triggered on map[key] change
});
new (Vue.component('my-comp'))({
propsData: { map:theMap, key:theKey }, // theMap must be reactive
});
A render function is the programmatic means of creating and inserting a component. Using new with propsData is primarily for unit testing, where the component will not necessarily have a Vue instance as a parent.
$mount doesn't establish a parent-child relationship, it just mounts the component free-standing to the DOM. You will need to set up the parent-child props management.
Vue.component('my-comp', {
template: '<div>{{ input }}</div>',
props: ['input']
});
new Vue({
el: '#app',
data: {
thingy: 5,
child: null
},
created() {
this.child = new(Vue.component('my-comp'))({
propsData: {
input: this.thingy
}
});
this.$watch('thingy', (newValue) => this.child.$props.input = newValue);
setInterval(() => ++this.thingy, 2000);
},
mounted() {
this.child.$mount(this.$el);
}
});
<script src="//unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
<div>

Categories