vue js ag-grid filter component not working - vue.js

I am quite new to vue-js and ag-grid, I would like to have a custom filter on my ag-grid so tried using component as filter as shown in vue-js ag-grid example: "https://www.ag-grid.com/javascript-grid-filter-component/" but its not working and giving "componentType is not a constructor" error in console.
Below is my code:
Gird:
<template>
<div class="all-devices" style="width: 100%; height: 425px;">
<ag-grid-vue
style="width: 100%; height: 100%;"
class="ag-theme-balham"
:gridOptions="gridOptions"
#grid-ready="onGridReady"
:columnDefs="columnDefs"
:defaultColDef="defaultColDef"
:rowData="rowData"
:frameworkComponents="frameworkComponents"
></ag-grid-vue>
</div>
</template>
<script>
import { AgGridVue } from "ag-grid-vue";
import PartialMatchFilter from "./PartialMatchFilter";
export default {
name: "AllDevices",
components: {},
data() {
return {
gridOptions: null,
columnDefs: null,
defaultColDef: null,
rowData: null,
frameworkComponents: null
};
},
components: {
AgGridVue
},
beforeMount() {
this.gridOptions = {};
this.columnDefs = [
{
headerName: "Row",
field: "row",
width: 450
},
{
headerName: "Filter Component",
field: "name",
width: 430,
filter: "partialMatchFilter"
}
];
this.rowData = [
{
row: "Row 1",
name: "Michael Phelps"
},
{
row: "Row 2",
name: "Natalie Coughlin"
},
{
row: "Row 3",
name: "Aleksey Nemov"
},
{
row: "Row 4",
name: "Alicia Coutts"
},
{
row: "Row 5",
name: "Missy Franklin"
},
{
row: "Row 6",
name: "Ryan Lochte"
},
{
row: "Row 7",
name: "Allison Schmitt"
},
{
row: "Row 8",
name: "Natalie Coughlin"
},
{
row: "Row 9",
name: "Ian Thorpe"
},
{
row: "Row 10",
name: "Bob Mill"
},
{
row: "Row 11",
name: "Willy Walsh"
},
{
row: "Row 12",
name: "Sarah McCoy"
},
{
row: "Row 13",
name: "Jane Jack"
},
{
row: "Row 14",
name: "Tina Wills"
}
];
this.defaultColDef = { filter: true };
this.frameworkComponents = { partialMatchFilter: PartialMatchFilter };
},
methods: {
onGridReady(params) {
params.api.sizeColumnsToFit();
}
}
};
</script>
<style>
</style>
Filter component:
<template>
<div>
<input style="height: 20px" :ref="'input'" v-model="text" />
</div>
</template>
<script>
export default {
name: "PartialMatchFilter",
data() {
return {
text: "",
valueGetter: null
};
},
methods: {
isFilterActive() {
return this.text !== null && this.text !== undefined && this.text !== "";
},
doesFilterPass(params) {
return (
!this.text ||
this.text
.toLowerCase()
.split(" ")
.every(filterWord => {
return (
this.valueGetter(params.node)
.toString()
.toLowerCase()
.indexOf(filterWord) >= 0
);
})
);
},
getModel() {
return { value: this.text };
},
setModel(model) {
if (model) {
this.text = model.value;
}
},
afterGuiAttached() {
this.$refs.input.focus();
},
componentMethod(message) {
alert(`Alert from PartialMatchFilterComponent ${message}`);
}
},
watch: {
text: function(val, oldVal) {
if (val !== oldVal) {
this.params.filterChangedCallback();
}
}
},
created() {
this.valueGetter = this.params.valueGetter;
}
};
</script>
Am I missing something? Please help! - Thanks

I had the same problem.
First, make this change in your columnDefs and get rid of frameworkComponents. Its just cleaner.
filter: "partialMatchFilter" -> filterFramework: PartialMatchFilter
Then the actual fix.
In your filter component add Vue.extend:
<template>
...
</template>
<script>
import Vue from "vue";
export default Vue.extend({
...
});
The example I followed -> https://github.com/ag-grid/ag-grid-vue-example/tree/master/src/rich-grid-example
Ref for Vue.extend -> https://v2.vuejs.org/v2/api/#Vue-extend

Related

Search bar filter vue 3

I just want to add a search bar filter to my vue 3 project but I don't know why is not working as good as I wanted.
Here is my code:
App.vue
<template>
<div id="app">
<div class="header">
<img class="logo" alt="UOC logo" src="./assets/uoc-logo.png" />
<div class="app-name">Recipe book</div>
</div>
<search-bar #search="setSearchTerm" />
<recipe-list :recipeList="filteredData" #delete-recipe="deleteRecipe" />
<recipe-form
v-if="showModal"
#add-recipe="addRecipe"
#close-modal="showModal = false"
/>
</div>
</template>
<script>
import RecipeList from "./components/RecipeList.vue";
import RecipeForm from "./components/RecipeForm.vue";
import SearchBar from "./components/SearchBar.vue";
import { defineComponent } from "vue";
export default defineComponent({
name: "App",
components: {
RecipeList: RecipeList,
RecipeForm,
SearchBar,
},
data: () => ({
recipeList: [
{
id: 1,
servings: 4,
time: "30m",
difficulty: "Easy",
title: "Spaghetti",
ingredients: ["noodles", "tomato sauce", "cheese"],
directions: ["boil noodles", "cook noodles", "eat noodles"],
imageUrl:
"https://imagesvc.meredithcorp.io/v3/mm/image?q=60&c=sc&poi=face&w=2000&h=1000&url=https%3A%2F%2Fstatic.onecms.io%2Fwp-content%2Fuploads%2Fsites%2F21%2F2018%2F02%2F14%2Frecetas-4115-spaghetti-boloesa-facil-2000.jpg",
},
{
id: 2,
servings: 2,
time: "15m",
difficulty: "Medium",
title: "Pizza",
ingredients: ["dough", "tomato sauce", "cheese"],
directions: ["boil dough", "cook dough", "eat pizza"],
imageUrl:
"https://www.saborusa.com/wp-content/uploads/2019/10/Animate-a-disfrutar-una-deliciosa-pizza-de-salchicha-Foto-destacada.png",
featured: true,
},
{
id: 3,
servings: 6,
time: "1h",
difficulty: "Hard",
title: "Salad",
ingredients: ["lettuce", "tomato", "cheese"],
directions: ["cut lettuce", "cut tomato", "cut cheese"],
imageUrl:
"https://www.unileverfoodsolutions.es/dam/global-ufs/mcos/SPAIN/calcmenu/recipes/ES-recipes/In-Development/american-bbq-beef-salad/main-header.jpg",
},
],
showModal: false,
recipesData: RecipeList,
filteredData: RecipeList,
}),
methods: {
deleteRecipe(id) {
this.recipeList.splice(id, 1);
},
addRecipe(recipe) {
this.recipeList.push(recipe);
},
toggleForm() {
if (this.showModal === false) {
this.showModal = true;
}
},
setSearchTerm(value) {
console.log(value);
if (value && value.length > 0) {
this.filteredData = this.recipesData.filter((i) => {
const val = value.toLowerCase();
const title = i.title && i.title.toLowerCase();
if (val && title.indexOf(val) !== -1) {
return true;
}
return false;
});
} else {
this.filteredData = this.recipesData;
}
},
},
});
</script>
SearchBar.vue
<template>
<div class="search">
<input
type="text"
placeholder="Search for a recipe"
id="search"
#change="search"
v-model="searchText"
/>
<button #click="clearSearch">Clear search</button>
<button #click="showForm">Add a new recipe</button>
</div>
</template>
<script>
import { defineComponent } from "vue";
export default defineComponent({
name: "SearchBar",
data() {
return { searchText: "" };
},
methods: {
showForm() {
console.log("show");
this.$emit("show-form");
},
clearSearch() {
this.clearInput = "";
},
search() {
this.$emit("search", this.searchText);
},
},
});
</script>
The component of search bar is working but when I acces to my website the array object is broken and I can not see any recipe. I just see recipes If I find it.
Thanks for your help

vue doesn't update the child chart component in echart

I have built a vue component to show a PIE chart in echart library as showed below. The PIE chart will be initialized with a default value.
pieChart.vue
<template>
<div :class="className" :style="{height:height,width:width}" />
</template>
<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import resize from './mixins/resize'
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '300px'
},
chartData: {
type: Object,
required: true
}
},
watch: {
chartData: function(val){
console.log('chartdata handler',val);
this.setOptions(val.legend, val.data);
}
},
data() {
return {
chart: null
}
},
mounted() {
this.$nextTick(() => {
this.initChart()
})
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
this.chart = echarts.init(this.$el, 'macarons');
this.setOptions(
['group_a','group_b','group_c'],
[
{ value: 1, name: 'group_a' },
{ value: 2, name: 'group_b' },
{ value: 3, name: 'group_c' },
]
);
},
setOptions( lengend, data ) {
this.chart.setOption({
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
},
legend: {
left: 'center',
bottom: '10',
data: lengend
},
series: [
{
name: 'WEEKLY WRITE ARTICLES',
type: 'pie',
roseType: 'radius',
radius: '50%',
data: data,
animationEasing: 'cubicInOut',
animationDuration: 2600
}
]
});
}
}
}
</script>
then I use this component in a view.
<template>
<pie-chart :chartData="updateData"/>
</template>
<script>
export default {
name: 'Personalinforadm',
components: {
PieChart,
},
data() {
return {
updateData: {
data:[
{ value: 33, name: 'group_a' },
{ value: 17, name: 'group_b' },
{ value: 3, name: 'group_c' },
],
legend:['group_a','group_b','group_c']
}
}
},
created() {
this.updateData = {
data:[
{ value: 3, name: 'group_a' },
{ value: 17, name: 'group_b' },
{ value: 3, name: 'group_c' },
],
legend:['group_a','group_b','group_c']
}
}
}
</script>
however the view doesn't update the PIE chart component with the new values in created methods. why the new values doesn't pass to the PIE component and trigger the watch methods, any ideas what goes wrong with the code?
take a look at https://github.com/ecomfe/vue-echarts or solution below may help you.(if you use version >4.x)
sample.vue
<template>
//...
<v-chart
class="chart mt-7"
:option="botChartData"
:update-options="updateOpts"
/>
//...
</template>
<script>
export default {
data() {
return {
updateOpts: {
notMerge: true,
},
botChartData: {
tooltip: {
trigger: "item",
formatter: "{a} <br/>{b} : {c} ({d}%)",
},
series: [
{
name: "Active Bots",
type: "pie",
center: ["50%", "50%"],
radius: ["75%", "90%"],
itemStyle: {
borderRadius: 8,
},
data: [],
}
],
},
};
},
methods: {
connect() {
this.bots = [
{
value: 0,
name: "a",
},
{
value: 1,
name: "b",
},
{
value: 2,
name: "c",
},
];
this.botChartData.series[0].data = this.bots;
}
},
};
</script>
I called "connect" in "created" you can call it in mounted or on events!
if you need to set your chart as a child component you can easily pass this.botChartData like below
child.vue
<template>
<v-chart
class="chart mt-7"
:option="botData"
:update-options="updateConf"
/>
</template>
<script>
export default {
props: {
botChartData: {
type: Object
},
updateOpts: {
type: Object
}
},
computed: {
botData() {
return this.botChartData
},
updateConf() {
return this.updateOpts
}
}
};
</script>
in parent.vue
<template>
//...
<sample :botChartData="botChartData" :updateOpts="updateOpts" />
//...
</template>
<script>
//...the same as sample.vue js
</script>
By the way if you have multiple charts in one page dont forget notMerge then your charts will reinitialize after switching between them

Flickering of charts and getcontext error with chartjs in the context of Vuejs

Hello i am trying to display different charts using the chartjs by calling the API. Below code shows how i have formatted the chart.vue
Chart.vue:
<template>
<div class="chart-container" style="position: relative; height: 40vh; width:100%;">
<slot name="test1"></slot>
<slot name="test2"></slot>
</div>
</template>
<script>
export default {
name: 'charts',
data () {
return {
date: [],
challenge: [],
data: []
}
},
mounted () {
this.check(8, 'chart_8')
this.check(7, 'chart_7')
},
methods: {
check (id, name) {
this.$http.get(`/api_chart/${ id }/full`)
.then((response) => {
this.date = response.data.date
this.challenge = response.data.challenge
this.data = this.date.map((date, index) => ({
x: new Date(date * 1000),
y: this.challenge[index]
}))
const ctx = document.getElementById([name]).getContext('2d')
let myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [
{
label: 'Challenge',
data: this.data,
borderColor: ' #EA5455',
}
]
},
options: {
lineTension: 0,
maintainAspectRatio: false,
scales: {
yAxes: [
{
scaleLabel: {
display: false
},
ticks: {
beginAtZero: true,
callback (value) {
return `${value}%`
}
}
}
],
xAxes: [
{
type: 'time',
time: {
unit: 'month'
},
scaleLabel: {
display: true,
}
}
]
}
}
})
})
}
}
}
</script>
App.vue:
<template>
<div class="In order to display chart1">
<chart-display> <canvas slot="test1" id="chart_7" ></canvas> </chart-display>
</div>
<div class="In order to display chart1">
<chart-display> <canvas slot="test2" id="chart_8" ></canvas> </chart-display>
</div>
</template>
<script>
import chart-display from './Chart.vue'
export default {
component: {chart-display}
}
</script>
As you can see i have shared my Chart.vue and App.vue, i am able to see my chart in the browser, but whenever i run the code or refresh the page, the charts flickers and stops. And then in my console i get below error:
Please someone help me to get rid of this issue, and please tell me if any changes i should do in my code to solve it. Please send me the modification code.
As I wrote in my comment, the charts are rendered twice. This causes flickering.
// every time you use <chart-display>, 2 charts are rendered, this means chart 1 renders
// itself and chart 2, char 2 renders itself and chart 1, this is a bad pattern in Vue in general
mounted() {
this.check(8, "chart_8");
this.check(7, "chart_7");
}
Make the following changes:
ChartDisplay.vue
<template>
<div
class="chart-container"
style="position: relative; height: 40vh; width: 100%"
>
<canvas ref="chart_7"></canvas>
<canvas ref="chart_8"></canvas>
</div>
</template>
<script>
import Chart from "chart.js";
export default {
name: "ChartDisplay",
data() {
return {
date: [],
challenge: [],
data: [],
// save charts in an array
charts: [],
// charts options
options: {
lineTension: 0,
maintainAspectRatio: false,
scales: {
yAxes: [
{
scaleLabel: {
display: false,
},
ticks: {
beginAtZero: true,
callback(value) {
return `${value}%`;
},
},
},
],
xAxes: [
{
type: "time",
time: {
unit: "month",
},
scaleLabel: {
display: true,
},
},
],
},
},
};
},
mounted() {
this.render(7, this.$refs.chart_7);
this.render(8, this.$refs.chart_8);
},
methods: {
render(id, ctx) {
this.fetchData(id).then((response) => {
let data = response.date.map((date, index) => ({
x: new Date(date * 1000),
y: response.challenge[index],
}));
this.charts.push(
new Chart(ctx, {
type: "line",
data: {
datasets: [
{
label: "Challenge",
data: data,
borderColor: " #EA5455",
},
],
},
options: this.options,
})
);
});
},
fetchData(id) {
return this.$http.get(`/api_chart/${ id }/full`);
},
},
beforeDestroy() {
this.charts.forEach((chart) => chart.destroy());
},
};
</script>
<style >
[v-cloak] {
display: none;
}
</style>
App.vue
<template>
<div>
<div class="In order to display chart1">
<chart-display/>
</div>
</div>
</template>
<script>
import ChartDisplay from "./ChartDisplay.vue";
export default {
components: { ChartDisplay },
};
</script>
See it on sandbox
I found several errors on your code. I fix them in Sandbox
For Chat.vue :
I rename the file as ChartDisplay.vue as similar as the component name
import chart.js package for using Chart() function
I use a demo API
<template>
<div
class="chart-container"
style="position: relative; height: 40vh; width: 100%"
>
<slot name="test1"></slot>
<slot name="test2"></slot>
</div>
</template>
<script>
import Chart from "chart.js";
export default {
name: "ChartDisplay",
data() {
return {
date: [],
challenge: [],
data: [],
};
},
mounted() {
this.check(8, "chart_8");
this.check(7, "chart_7");
},
methods: {
check(id, name) {
fetch(
"https://api.wirespec.dev/wirespec/stackoverflow/fetchchartdataforvuejs"
)
.then((response) => response.json())
.then((response) => {
this.date = response.date;
this.challenge = response.challenge;
this.data = this.date.map((date, index) => ({
x: new Date(date * 1000),
y: this.challenge[index],
}));
const ctx = document.getElementById([name]).getContext("2d");
new Chart(ctx, {
type: "line",
data: {
datasets: [{
label: "Challenge",
data: this.data,
borderColor: " #EA5455",
}, ],
},
options: {
lineTension: 0,
maintainAspectRatio: false,
scales: {
yAxes: [{
scaleLabel: {
display: false,
},
ticks: {
beginAtZero: true,
callback(value) {
return `${value}%`;
},
},
}, ],
xAxes: [{
type: "time",
time: {
unit: "month",
},
scaleLabel: {
display: true,
},
}, ],
},
},
});
});
},
},
};
</script>
For App.vue
Your import should not carry any hyphen.
component should be components
render the component once to avoid flikering
<template>
<div>
<div class="In order to display chart1">
<chart-display>
<canvas slot="test1" id="chart_7"></canvas>
<canvas slot="test2" id="chart_8"></canvas>
</chart-display>
</div>
</div>
</template>
<script>
import ChartDisplay from "./ChartDisplay.vue";
export default {
components: {
ChartDisplay
},
};
</script>

Unable to Construct Stacked Vue-ChartJS Line Plot

I'm trying to make a stacked line chart using Vue-ChartJS but am having difficulties getting it to stack.
I tried adding the following into the fill data function but saw no change.
scales: {
yAxes: [{ stacked: true}]
}
I also tried creating a this.options entry but that didn't work either. The minimal reproducible code for the chart is as follows, any advice or help would be much appreciated!
## LineChart.js
import { Line, mixins } from 'vue-chartjs'
const { reactiveProp } = mixins
export default {
extends: Line,
mixins: [reactiveProp],
props: ['options'],
mounted() {
this.renderChart(this.chartData, this.options)
}
}
## LineChart.vue
<template>
<div class="small">
<line-chart :chart-data="chartData"></line-chart>
<button #click="fillData()">Randomize</button>
</div>
</template>
<script>
import LineChart from '../store/LineChart.js'
export default {
components: {
LineChart
},
data() {
return {
chartData: null
}
},
mounted() {
this.fillData()
},
methods: {
fillData() {
this.chartData = {
labels: [this.getRandomInt(), this.getRandomInt()],
datasets: [
{
label: 'Data One',
backgroundColor: '#f87979',
data: [this.getRandomInt(), this.getRandomInt()]
},
{
label: 'Data Two',
backgroundColor: '#C23596',
data: [this.getRandomInt(), this.getRandomInt()]
}
]
}
},
getRandomInt() {
return Math.floor(Math.random() * (50 - 5 + 1)) + 5
}
}
}
</script>
<style>
.small {
max-width: 600px;
margin: 150px auto;
}
</style>
You need to pass scales in the options:
...
<div class="small">
<line-chart :chart-data="chartData" :options="options"></line-chart>
<button #click="fillData()">Randomize</button>
</div>
...
data() {
return {
chartData: null,
options: {
scales: {
yAxes: [
{
stacked: true
}
]
},
},
}
},

dynamic interpolation computed method name in v-for element in vuejs

In my project vuejs a create a list element with ul li and v-for directive vuejs like this:
<ul>
<li :class="{active: 'isActive+index'}" v-for="(car, index) in cars"></li>
</ul>
Those elements are dynamics. Sometimes are 2 sometimes are 3 or 4 elements
But I need to have a specific logic active class css for each like this:
'isActive+index'
Where this represent a dynamic computed name (already exist). But obviously this code not run and generate basic string word not a link to computed method. I want to execute those computed methods:
computed:
{
isActive1: function ()
{
return myLogic
},
isActive2: function ()
{
return myLogic
},
isActive3: function ()
{
return myLogic
},
isActive4: function ()
{
return myLogic
},
}
How can I link element with dynamic method name for execute computed with vuejs ?
new Vue({
el: '#app',
template: `
<div>
<ul>
<li v-for="(item, index) in cars" :key="index" :class="{ active: statusActive[index] }">
<strong>Car:</strong> {{item.name}} ,
</li>
</ul>
<button #click="changeCars">Change cars</button>
</div>
`,
data() {
return {
cars1: [{
name: "car1",
},
{
name: "car2",
},
{
name: "car3",
},
],
cars2: [{
name: "car1",
},
{
name: "car2",
},
{
name: "car3",
},
{
name: "car4",
},
],
cars3: [{
name: "car1",
},
{
name: "car2",
},
],
carsIndex: 1,
};
},
computed: {
cars() {
return this["cars" + this.carsIndex];
},
statusActive() {
return {
0: this.statusActive0,
1: this.statusActive1,
2: this.statusActive2,
3: this.statusActive3,
};
},
statusActive0() {
return false;
},
statusActive1() {
return true;
},
statusActive2() {
return false;
},
statusActive3() {
return true;
},
},
methods: {
changeCars() {
if (this.carsIndex < 3) {
this.carsIndex++;
} else {
this.carsIndex = 1;
}
},
},
})
.active {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app"></div>
or
new Vue({
el: '#app',
template: `
<div>
<ul>
<li v-for="(item, index) in cars" :key="index" :class="{ active: isActive(index) }">
<strong>Car:</strong> {{item.name}} ,
</li>
</ul>
<button #click="changeCars">Change cars</button>
</div>
`,
data() {
return {
cars1: [{
name: "car1",
},
{
name: "car2",
},
{
name: "car3",
},
],
cars2: [{
name: "car1",
},
{
name: "car2",
},
{
name: "car3",
},
{
name: "car4",
},
],
cars3: [{
name: "car1",
},
{
name: "car2",
},
],
carsIndex: 1,
};
},
computed: {
cars() {
return this["cars" + this.carsIndex];
},
statusActive0() {
return false;
},
statusActive1() {
return true;
},
statusActive2() {
return false;
},
statusActive3() {
return true;
},
},
methods: {
changeCars() {
if (this.carsIndex < 3) {
this.carsIndex++;
} else {
this.carsIndex = 1;
}
},
isActive(index) {
return this["statusActive" + index];
},
},
})
.active {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app"></div>