I have a VueJS object with data that looks like the following:
phases: [
{ id: 1, ... },
{ id: 2, ... },
{ id: 3, ... },
],
roles: [
{ phaseId: 1, title: "Account Manager" },
{ phaseId: 1, title: "Creative Director" },
{ phaseId: 2, title: "Account Manager" },
{ phaseId: 2, title: "Creative Director" },
]
And my v-for loop looks like the following:
<article v-for="phase in phases" class="phase">
... Other content ...
<div v-for="role in roles | filterBy phase.id in 'phaseId' ">
<p>${role.title}</p>
</div>
</article>
I'm trying to filter the roles array to only show roles with the "parent" phase given by the ID. Since the roles for-loop will be running multiple times, each time it will only show the appropriate roles.
Is there any way to accomplish this?
+1 #gurghet, filters are deprecated.
Use a method
data: {
phases: [
{ id: 1, ... },
{ id: 2, ... },
{ id: 3, ... },
],
roles: [
{ phaseId: 1, title: "Account Manager" },
{ phaseId: 1, title: "Creative Director" },
{ phaseId: 2, title: "Account Manager" },
{ phaseId: 2, title: "Creative Director" },
]
},
methods: {
filtered(id){
return this.roles.filter(rol => rol.phaseId === id)
}
}
Template:
<article v-for="phase in phases" class="phase">
... Other content ...
<div v-for="role in filtered(phase.id) ">
<p>${role.title}</p>
</div>
</article>
Exmaple
One can use a mixin that calls the actual filter
1. First we need to register a filter globally
Vue.filter('normalizetext', function (value) {
if (!value) return '';
value = value.toString();
value = value.replace('_', ' ').toLowerCase();
return value.charAt(0).toUpperCase() + value.slice(1);
})
2.Create a mixin
import Vue from 'vue';
export default {
/**
* The methods that the mixin used for filer functions.
*/
methods: {
/**
* Method used to check if specific element is found in an filer
*
* #param {filer} filtername filer to find created filter
*
* #returns {any} filtered output
*/
dynamicFilter(filterName, value) {
return Vue.filter(filterName)(value);
}
}
};
3.Calling filter
<template>
{{
dynamicFilter(data.FilterName, value)
}}
</template>
Related
Not sure why the data is showing up as undefined. Is the data not formatted correctly? I've tried several ways to adjust it and nest it differently but nothing is working out so far.
<template>
<main>
<AboutMeComponent v-for="i in about" :key="i.posts.id" :title="i.posts.title" :body="i.posts.body" :skills="i.posts.skills"
:img="i.images.img" />
</main>
</template>
<script>
import AboutMeComponent from '../components/AboutMeComponent.vue';
export default {
data() {
return {
about: {
posts: [
{ id: 1, title: "About Me", body: `Body 1` },
{ id: 2, title: "Skills" },
{ id: 3, title: "Hobbies", body: "Body 3" },
],
images: [
{ img: 'figma.png'},
{ img: 'figma.png'},
{ img: 'figma.png'},
]
}
}
},
components: {
AboutMeComponent
}
}
</script>
I think you are need to set the v-for loop on the about.posts, not on the about. Also, if the img is used for the post, I would suggest you keep them with the post, instead of separated.
So your data will be:
data() {
return {
about: {
posts: [
{ id: 1, title: 'About Me', body: 'Body 1', img: 'figma.png' },
{ id: 2, title: 'Skills', body: 'Body 2', img: 'figma.png' },
{ id: 3, title: 'Hobbies', body: 'Body 3', img: 'figma.png' },
],
}
}
},
And now you can loop over about.posts:
<AboutMeComponent v-for="post in about.posts" :key="post.id" :title="post.title" :body="post.body" :img="post.img" />
I left skills out, as they are not in your data.
Just a small suggestion: using variable names as i makes your code harder to understand. If you had used about in about, you would have felt that this was not what you wanted, because you wanted something to do with a post in posts.
Hope this helps.
Few Observations :
As each img in the images array corresponds to each object in the posts array. Merge that into a single one and then use v-for loop for about.posts.
As few properties are missing. Use Optional chaining (?.) operator so that you can read the value of a property located deep within a chain of connected objects without having to check that each reference in the chain is valid.
Live Demo :
Vue.component('aboutmecomponent', {
props: ['key', 'title', 'body', 'skills', 'img'],
template: '<h3>{{ title }}</h3>'
});
var app = new Vue({
el: '#app',
data: {
about: {
posts: [
{ id: 1, title: "About Me", body: "Body 1" },
{ id: 2, title: "Skills" },
{ id: 3, title: "Hobbies", body: "Body 3" },
],
images: [
{ img: 'figma.png'},
{ img: 'figma.png'},
{ img: 'figma.png'},
]
}
},
mounted() {
this.about.posts = this.about.posts.map((obj, index) => {
const newObj = {
...obj,
...this.about.images[index] }
return newObj;
});
delete this.about.images;
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<AboutMeComponent
v-for="item in about.posts"
:key="item?.id"
:title="item?.title"
:body="item?.body"
:skills="item?.skills"
:img="item?.img" >
</AboutMeComponent>
</div>
I am using Apexchart's bar chart and noticed that I am not able to change the x-axis's labels, ie the categories. Below is the component:
<template>
<div>
{{magnitudeByFreq}}
{{chartOptions}}
<apex-chart width="500" type="bar" :options="chartOptions" :series="series"></apex-chart>
</div>
</template>
<script>
export default {
props: {
processedMouseData: null,
gradientCountByType: null,
magnitudeByFreq: null,
},
data: function() {
return {
chartOptions: {
chart: {
id: 'vuechart-example'
},
xaxis: {
categories: []//['Positive', 'Neutral', 'Negative']
}
},
series: [{
name: 'series-1',
data: []
}]
}
},
mounted() {
},
watch: {
gradientCountByType: function() {
console.log(this.series.data)
this.gradientCountByType ? this.series[0].data = this.gradientCountByType : console.log("Not working")
this.gradientCountByType ? this.chartOptions.xaxis.categories = ['Positive', 'Neutral', 'Negative'] : console.log("No xaxis")
},
magnitudeByFreq: function() {
this.magnitudeByFreq ? this.series[0].data = Object.values(this.magnitudeByFreq) : console.log("ABX")
this.magnitudeByFreq ? this.chartOptions.xaxis.categories = Object.keys(this.magnitudeByFreq) : console.log("ABA")
}
}
};
</script>
Currently the categories is set to []. This is because I want it to be filled by different data depending on which prop is using it. ie gradientCountByType or magnitudeByFreq.
The two lines below which are supposed to set the category:
this.gradientCountByType ? this.chartOptions.xaxis.categories = ['Positive', 'Neutral', 'Negative'] : console.log("No xaxis")
this.magnitudeByFreq ? this.chartOptions.xaxis.categories = Object.keys(this.magnitudeByFreq) : console.log("ABA")
They don't seem to update the category at all. I should however mention that what gets displayed in the template {{magnitudeByFreq}} and {{chartOptions}}, do reflect there is a change in the category variable:
{{chartOptions}} shows:
{ "chart": { "id": "vuechart-example" }, "xaxis": { "categories": [ "Positive", "Neutral", "Negative" ], "convertedCatToNumeric": false } }
and
{ "chart": { "id": "vuechart-example" }, "xaxis": { "categories": [ "+0", "+100", "+1000", "+2000" ], "convertedCatToNumeric": false } }
Why is the categories attribute not displaying correctly? For whatever reason, the categories are showing numbers.
My guess is by changing the data attribute doesn't actually update the chart.
Instead we should create a reference to the chart:
<apex-chart ref="radar" type="radar" height="350" :options="chartOptions" :series="series"></apex-chart>
Then we can update the data with updateSeries method and update the chartOptions with updateOptions method:
this.$refs.radar.updateSeries([{
name: 'Series 1',
data: [your_new_data_here] //ie [1,2,3,4]
}])
this.$refs.radar.updateOptions({
xaxis: {
categories: [your_new_categories_here] //ie ["a","b","c","d"]
}
})
<apex-chart ref="chart" :options="options" :series="series"/>
Chart component has refresh function. So we can re-render the chart.
this.series[0].data = [yourData];
this.options.xaxis.categories = [yourCategories]
this.$refs.chart.refresh();
Here is my code below. I know there is some specific library like vue-scrollto but I want to resolve this task with no library.
I want to add isViewed true only in that certain link. But the problem is that when I clicked on links each object of that link has isViewed true instead of only one.I want it changes dynamically.
I would appreciate any help.
<template>
<div class="anchors">
<ul class="list">
<li class="item" v-for="(link, index) in anchorLinks" :key="link.id">
<a #click="goToSection(link, link.sectionId, link.id, index)" class="anchor__item-link"></a>
</li>
</ul>
</div>
</template>
export default {
name: "AnchorLinks",
data(){
return{
anchorLinks: [
{
id: 1,
sectionId: "work",
},
{
id: 2,
sectionId: "service",
},
{
id: 3,
sectionId: "partner",
},
{
id: 4,
sectionId: "partner",
},
{
id: 5,
sectionId: "partner",
},
{
id: 6,
sectionId: "partner",
},
{
id: 6,
sectionId: "partner",
}
]
}
},
methods: {
goToSection(link, sectionId, id, index){
const element = document.getElementById(sectionId);
if (element) {
link.isViewed = true;
window.scrollTo({
top: element.offsetTop,
behavior: 'smooth'
});
console.log(id)
console.log(index);
console.log(this.anchorLinks)
}
}
}
}
You should ensure that each anchorLink initially has an isViewed property,
The documentation states:
Vue cannot detect property addition or deletion
<template>
<div class="anchors">
<ul class="list">
<li class="item" v-for="(link, index) in anchorLinks" :key="link.id">
<a
#click="goToSection(link, link.sectionId, link.id, index)"
class="anchor__item-link"
></a>
</li>
</ul>
</div>
</template>
export default {
name: 'AnchorLinks',
data() {
return {
anchorLinks: [
{
id: 1,
sectionId: 'work',
isViewed: false,
},
{
id: 2,
sectionId: 'service',
isViewed: false,
},
{
id: 3,
sectionId: 'partner',
isViewed: false,
},
{
id: 4,
sectionId: 'partner',
isViewed: false,
},
{
id: 5,
sectionId: 'partner',
isViewed: false,
},
{
id: 6,
sectionId: 'partner',
isViewed: false,
},
{
id: 6,
sectionId: 'partner',
isViewed: false,
},
],
};
},
methods: {
goToSection(link, sectionId, id, index) {
const element = document.getElementById(sectionId);
if (element) {
link.isViewed = true;
window.scrollTo({
top: element.offsetTop,
behavior: 'smooth',
});
console.log(id);
console.log(index);
console.log(this.anchorLinks);
}
},
},
};
Without reinventing the wheel here, you basically need to remove the previous link.isViewed property each time you click a link.
One straightforward way to do this is to store another variable containing the active link, that you can update each time you click a new link. See simple example below (ignoring excess / cruft).
As an aside, I would consider not updating the isViewed variable at all and just storing the active link (similar to below) for reference wherever you need it.
<ul>
<li v-for="link in anchorLinks">
<a #click="goToSection(link)"></a>
</li>
</ul>
export default {
name: "AnchorLinks",
data() {
return {
activeLink: null,
// you're using an array here, lean on the index
anchorLinks: [
{ id: "work" },
{ id: "service" },
{ id: "partner" },
],
};
},
methods: {
goToSection(link) {
// remove the previous `isViewed` property
if (this.activeLink) {
delete this.activeLink.isViewed;
}
// update the active link
this.activeLink = link;
const el = document.getElementById(link.id);
if (el) {
// now this will be the only link with `isViewed = true`
link.isViewed = true;
window.scrollTo({
top: el.offsetTop,
behavior: 'smooth'
});
}
}
}
}
From what I can tell and from what you have given, I think you need to set the ids of each anchor link.
<a
:id="link.sectionId"
#click="goToSection(link, link.sectionId, link.id, index)"
class="anchor__item-link"
></a>
How can one change the value that is displayed in the UI filter elements(input, dropdown etc) in vue-good-table programmatically?
For example if I call:
this.$set(this.table.columnsFilters, 'name', 'bob')
I want the value in the HTML input field 'name' to display bob.
Unfortunatly the settting the values as i described above does not work
Edit If you are using version 2.16.0 or above, you'll need to do this:
CodePen for v2.16.0 +
// v2.16.0 or higher
...
changeFilter(field, newFilter) {
let newCols = JSON.parse(JSON.stringify(this.columns));
let found = newCols.find(c => {
return c.field == field;
});
console.log(found);
if (found) {
if (found.hasOwnProperty("filterOptions")) {
found.filterOptions.filterValue = newFilter;
this.columns = newCols;
} else {
alert(`Column '${field}' does not have filtering configured!`);
}
} else {
alert(`Field '${field}' does not exist!`);
}
}
Original Answer
If I am understanding this correctly you want to set the filter for a field programmatially... Something like this should work.. Click the button to change the filter programamaticlly... If you change this line to any valid name, it will filter by that name... Change 'Bob' to a valid name...(like Dan)...
<button
style="width:200px;"
#click.stop="changeFilter('name', 'Bob')"
>Click To Change Name Filter</button>
CodePen Mirror
const vm = new Vue({
el: "#app",
name: "my-component",
data: {
columns: [
{
label: "Name",
field: "name",
filterOptions: {
enabled: true, // enable filter for this column
placeholder: "Filter Name", // placeholder for filter input
filterValue: "", // initial populated value for this filter
filterDropdownItems: [], // dropdown (with selected values) instead of text input
filterFn: this.columnFilterFn, //custom filter function that
trigger: "enter" //only trigger on enter not on keyup
}
},
{
label: "Age",
field: "age",
type: "number"
},
{
label: "Created On",
field: "createdAt",
type: "date",
dateInputFormat: "YYYY-MM-DD",
dateOutputFormat: "MMM Do YY"
},
{
label: "Percent",
field: "score",
type: "percentage"
}
],
rows: [
{
id: 1,
name: "John",
age: 20,
createdAt: "201-10-31:9: 35 am",
score: 0.03343
},
{
id: 2,
name: "Jane",
age: 24,
createdAt: "2011-10-31",
score: 0.03343
},
{
id: 3,
name: "Susan",
age: 16,
createdAt: "2011-10-30",
score: 0.03343
},
{
id: 4,
name: "Bob",
age: 55,
createdAt: "2011-10-11",
score: 0.03343
},
{
id: 5,
name: "Dan",
age: 40,
createdAt: "2011-10-21",
score: 0.03343
},
{
id: 6,
name: "John",
age: 20,
createdAt: "2011-10-31",
score: 0.03343
}
]
},
methods: {
clearFilter(field) {
try {
let found = this.columns.find((c) => {
return c.field == field
});
found.filterOptions.filterValue = "";
} catch {
alert(`Unable to clear ${field} filter`)
}
},
changeFilter(field, newFilter) {
let found = this.columns.find((c) => {
return c.field == field
});
if(found) {
if(found.hasOwnProperty("filterOptions")) {
if(found.filterOptions.hasOwnProperty("filterValue")) {
found.filterOptions.filterValue = newFilter;
} else {
alert(`Column '${field}' does not have filterValue property!`)
}
} else {
alert(`Column '${field}' does not have filtering configured!`)
}
} else {
alert(`Field '${field}' does not exist!`)
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-good-table#2.15.3/dist/vue-good-table.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vue-good-table#2.12.2/dist/vue-good-table.css" rel="stylesheet"/>
<div id="app">
<div>
<div style="margin:10px 10px 10px 10px;">
<button
style="width:200px;"
#click.stop="changeFilter('name', 'Bob')"
>Click To Change Name Filter</button>
<button
style="width:200px;"
#click.stop="clearFilter('name')"
>Clear Name Filter</button>
</div>
<vue-good-table :columns="columns" :rows="rows" />
</div>
</div>
See: https://github.com/xaksis/vue-good-table/issues/475
Official recommended method of doing this for version >= 2.17.5:
this.$set(this.columns[foundIndex].filterOptions, 'filterValue', value);
If you want to have it done using $route query params:
for (var key in this.$route.query){
var field = key;
let foundIndex = this.columns.findIndex((c) => {
return c.field == field
});
if (foundIndex !== -1 ){
this.$set(this.columns[foundIndex].filterOptions, 'filterValue', this.$route.query[key]);
}
}
Vue cannot detect normal property additions (e.g. this.myObject.newProperty = 'hi')
Therefore, using this.$set(this.table.columnsFilters, 'name', 'bob') instead of your code.
More information about $set
Suppose I'm trying to make a simple questionnaire, where the user answers a list of questions.
new Vue(
{
el: "#app",
data:
{
questions:
[
{
id: 1,
name: "What is your favorite color?",
selectedId: 2,
choices:
[
{ id: 1, name: "red" },
{ id: 2, name: "green" },
{ id: 3, name: "blue" },
]
},
...
]
}
});
How do I go about making a question component with two-way binding. That is, if the user swaps their favorite color from green to red, by clicking on the respective input, the selectedId will automatically update. I'm not very clear on how v-model works within a component. Does it only have access to the components data? Also, I don't understand the difference between props/data.
There are lots of ways you can approach this, here's my attempt:
let id = 0;
Vue.component('question', {
template: '#question',
props: ['question'],
data() {
return {
radioGroup: `question-${id++}`,
};
},
methods: {
onChange(choice) {
this.question.selectedId = choice.id;
},
isChoiceSelected(choice) {
return this.question.selectedId === choice.id;
},
},
});
new Vue({
el: '#app',
data: {
questions: [
{
title: 'What is your favorite color?',
selectedId: null,
choices: [
{ id: 1, text: 'Red' },
{ id: 2, text: 'Green' },
{ id: 3, text: 'Blue' },
],
},
{
title: 'What is your favorite food?',
selectedId: null,
choices: [
{ id: 1, text: 'Beans' },
{ id: 2, text: 'Pizza' },
{ id: 3, text: 'Something else' },
],
},
],
},
});
.question {
margin: 20px 0;
}
<script src="https://rawgit.com/yyx990803/vue/master/dist/vue.js"></script>
<div id="app">
<question v-for="question of questions" :question="question"></question>
</div>
<template id="question">
<div class="question">
<div>{{ question.title }}</div>
<div v-for="choice of question.choices">
<input type="radio" :name="radioGroup" :checked="isChoiceSelected(choice)" #change="onChange(choice)"> {{ choice.text }}
</div>
<div>selectedId: {{ question.selectedId }}</div>
</div>
</template>