We need to attach a custom plugin to vue-chart. Please guide us how to implement on the same
import { Line, mixins } from 'vue-chartjs'
export default {
namespaced: true,
extends: Line,
props: ['chartData', 'options'],
mounted() {
this.renderChart(this.chartData, this.chartData.options)
}
}
This is how we are using the line chart of Vue-chart. How to attach the plugin here
https://blog.larapulse.com/javascript/creating-chart-js-plugins
We want to try this. But since we are using vue-chart which internally uses chart.js. Need some help to attach the plugin. please guide us
I want to apply some background color to the chart for one specific column in the chart
vue-chart-js provide method to attach plugins. Use this way:
import the plugin
import ChartDataLabels from 'chartjs-plugin-datalabels';
then, call addPlugin in mounted
mounted() {
this.addPlugin(ChartDataLabels);
this.renderChart(
this.chartData,
this.options,
);
}
Below is PieChart.vue script in case you create pie chart :
<script>
import { Pie, mixins } from 'vue-chartjs';
import ChartDataLabels from 'chartjs-plugin-datalabels';
Chart.plugins.unregister(ChartDataLabels);
const { reactiveProp } = mixins;
export default {
extends: Pie,
mixins: [reactiveProp],
props: {
options: {
type: Object,
default: null,
},
},
mounted() {
this.addPlugin(ChartDataLabels);
this.renderChart(
this.chartData,
this.options,
);
},
};
</script>
Using the annotation plugin for Chart.js as example, you can use the addPlugin function to attach it:
import { Line, mixins } from 'vue-chartjs'
import chartjsPluginAnnotation from "chartjs-plugin-annotation"
export default {
namespaced: true,
extends: Line,
props: ['chartData', 'options'],
mounted() {
//Arguments is an Array of Plugins (https://vue-chartjs.org/api/#addplugin)
this.addPlugin([chartjsPluginAnnotation]),
this.renderChart(this.chartData, this.chartData.options)
}
}
After this just pass the plugin's options on your component as usual. In this case, if you wanted to draw a vertical line, it would be something like this:
computed: {
chart() {
return {
chartData: {
labels: this.data.labels,
datasets: [
{
label: "Score",
data: this.data.data
}
],
options: {
annotation: {
annotations: [
{
type: "line",
mode: "vertical",
scaleID: "x-axis-0",
borderColor: "#4ecca3",
value: parseInt(this.data.line),
borderDash: [4, 4],
label: {
content: this.data.line,
enabled: true,
position: "top",
xAdjust: 15,
backgroundColor: '#4ecca3',
fontSize: 10,
}
}
]
}
},
}
};
}
import chartjsPluginAnnotation from "chartjs-plugin-annotation";
And:
mounted() {
Chart.plugins.register(chartjsPluginAnnotation);
this.addPlugin(chartjsPluginAnnotation);
this.renderChart(this.chartData, this.options);
}
Related
I'm using Mui and Lodash in my CRA project with Storybook 5.
I successfully tree-shake both Mui and Lodash imports using babel-plugin-import in CRA, using the following .bablerc.js
module.exports = {
presets: ["react-app", "#babel/preset-typescript"],
plugins: [
[
"babel-plugin-import",
{
libraryName: "#material-ui/core",
libraryDirectory: "esm",
camel2DashComponentName: false,
},
"core",
],
[
"babel-plugin-import",
{
libraryName: "#material-ui/icons",
libraryDirectory: "esm",
camel2DashComponentName: false,
},
"icons",
],
["lodash"],
],
};
However I am unable to do the same in Storybook 5. When I try using the same config in Storybook (copying over to .storybook/.babelrc.js), the babel config gets loaded, but results in no improvement in Storybook load times (Mui icons still seem to be loading for about a minute). Removing the presets
from babel config doesn't help either.
Storybook is hosted in the same root directory as my CRA, sharing node_modules.
How can I get the import tree-shaking to work in Storybook 5?
Here are my configs:
// .storybook/presets.js
module.exports = ['#storybook/preset-create-react-app'];
// .storybook/webpack.config.js
module.exports = function({ config }) {
// https://github.com/storybookjs/storybook/issues/6974#issuecomment-499903328
config.module.rules.unshift({
test: /\.stories.js$|\.stories.jsx$|\.stories.tsx$|\.stories.ts$/,
loaders: [require.resolve('#storybook/source-loader')],
enforce: 'pre',
});
return config;
};
// .storybook/.babelrc.js
module.exports = {
presets: ["react-app", "#babel/preset-typescript"],
plugins: [
[
"babel-plugin-import",
{
libraryName: "#material-ui/core",
libraryDirectory: "esm",
camel2DashComponentName: false,
},
"core",
],
[
"babel-plugin-import",
{
libraryName: "#material-ui/icons",
libraryDirectory: "esm",
camel2DashComponentName: false,
},
"icons",
],
["lodash"],
],
};
// .storybook/config.js
iimport { addDecorator, addParameters, configure } from "#storybook/react";
import { withKnobs } from "#storybook/addon-knobs";
import { withConsole } from "#storybook/addon-console";
import { INITIAL_VIEWPORTS } from "#storybook/addon-viewport";
import "../src/config";
import withIntl from "./decorators/intl";
import withReduxStoreAndConnectedRouter from "./decorators/reduxStoreWithDependentProviders";
import stylesProvider from "./decorators/stylesProvider";
import setup from "./decorators/setup";
const req = require.context("../src", true, /stories.tsx$|stories.ts$/);
const loadStories = () => req.keys().forEach((filename) => req(filename));
addDecorator(setup);
addDecorator(stylesProvider);
addDecorator(withKnobs);
addDecorator(withIntl);
addDecorator(withReduxStoreAndConnectedRouter);
addDecorator((storyFn, context) =>
withConsole(consoleConfig)(storyFn)(context)
);
addParameters({
viewport: {
viewports: INITIAL_VIEWPORTS,
},
});
addParameters({
backgrounds: [
{ name: "darkGray", value: "#34373c" },
{ name: "gray", value: "#A0A0A0" },
{ name: "lightGray", value: "#F0F0F0" },
],
});
configure(loadStories, module);
i am working on chart.js in ionic with angular and i am generating a chart which is line chart , i dnt want to show dots for each point and show tooltip on hover i want to show values alway without hover, i tried many ways which are mentioned on stackover flow as well but none of them working so i thought to share my code
following is my code
import { Component, OnInit, ViewChild, ElementRef } from "#angular/core";
import { Chart } from "chart.js";
#Component({
selector: 'app-bp',
templateUrl: './bp.page.html',
styleUrls: ['./bp.page.scss'],
})
export class BpPage implements OnInit {
#ViewChild("barCanvas") barCanvas: ElementRef;
private barChart: Chart;
constructor() {}
ngOnInit() {
setTimeout(() =>{
this.barChart = new Chart(this.barCanvas.nativeElement, {
type: "line",
data: {
labels: ["12-Apr", "13-Apr", "14-Apr", "15-Apr", "16-Apr", "17-Apr", "18-Apr"],
datasets: [{
label: "High",
backgroundColor: "#3e95cd",
borderColor: "#3e95cd",
pointBorderWidth: 10,
pointHoverRadius: 10,
data: [10943, 29649, 6444, 2330, 36694, 10943, 29649],
fill: false,
borderWidth: 3
}, {
label: "Low",
backgroundColor: "#ff3300",
borderColor: "#ff3300",
pointBorderWidth: 10,
pointHoverRadius: 10,
data: [9283, 1251, 6416, 2374, 9182, 9283, 1251],
fill: false,
borderWidth: 3
}]
},
options: {
scales: {
yAxes: [
{
ticks: {
beginAtZero: true
}
}
]
},
},
});
},1500);
}
}
To always show the tooltips, you must follow an approach similar to the one described here: Chart.js v2: How to make tooltips always appear on pie chart?
You have to define for your chart the showAllTooltips option as below:
let barChart = new Chart(this.barCanvas.nativeElement, {
type: "line",
//...
//...
},
options: {
showAllTooltips: true,
//...
}
});
And than you must call the code that defines the showAllTooltips behavior.
Here is a stackblitz of the working solution.
The method configureTooltipBehavior() is the one responsible for the magic.
https://stackblitz.com/edit/angular-ivy-fzpyva
I created a simple responsive HTML + JS chart with chart.js which worked well. I decided to do it within Vue CLI and so have tried to switch it to vue-chartjs but the same chart always renders about 33% taller than my window and so presents vertical scrollbars (the width is fine). I recreated the problem with a sample trivial graph which I render with:
import {Line} from 'vue-chartjs'
export default {
extends: Line,
mounted () {
this.renderChart(data, options)
}
}
Note the data is trivial and the options are {}.
If I use chart.js in my Vue component, instead of vue-chartjs then it works fine. I.e. I do nothing more than delete the above code from my component and change it to the following then it renders fine, just like my sample HTML + chart.js version.
import Chart from 'chart.js'
function mount(el) {
return new Chart(
document.getElementById(el).getContext('2d'), {
type: 'line',
data: data,
options: options,
})
}
export default {
template: '<canvas id="chart"></canvas>',
mounted () {
self.chart = mount('chart')
}
}
I am using the default responsive: true and maintainAspectRatio: false of course, and have no explicit CSS or size settings anywhere.
Why can I not get the chart to render the height correctly when I use vue-chartjs? I am using vue-chartjs version 3.4.2 but have also tried a few versions back. I have looked all over the github bug tracker but seen nothing related.
UPDATE:
You should pass the options as prop or locally. But it's needed to add:
responsive: true
maintainAspectRatio: false
the desired height as well as the options as you said. Here's how it worked for me:
options:
options: {
scales: {
yAxes: [
{
ticks: {
beginAtZero: true
}
}]
},
responsive: true,
maintainAspectRatio: false
}
In template:
<bin-graph-weight :chart-data="datacollection" :styles="myStyles" :options="datacollection.options"/>
graph-component.js:
import { Line, mixins } from 'vue-chartjs'
const { reactiveProp } = mixins
export default {
extends: Line,
mixins: [reactiveProp],
props: ['options'],
mounted () {
// this.chartData is created in the mixin.
this.renderChart(this.chartData, this.options)
},
// If you want to pass options please create a local options object
watch: {
chartData () {
this.$data._chart.update()
}
}
}
Also had problem with height overflow and responsiveness, fixed by introducing flex parent container that takes up 100% of the space. After setting responsive and ratio options (check out related chartjs doc):
options: {
// ..
responsive: true,
maintainAspectRatio: true
}
I used following css to fill 100% of the parent (where TheChart is vue-chartjs component. Basically need to make sure the chart's parent is always filling 100% of it's own parent):
vue template
<v-container class="chart-container">
<TheChart :chartdata="chartData" :options="chartOptions" />
</v-container>
scss:
.chart-container {
flex-grow: 1;
min-height: 0;
> div {
position: relative;
height: 100%;
}
}
With responsiveness the chart rerenders with promises and actually sets two times.
With a watcher in Vue.js you can rerender every time with changes in the chartData.
Chart component:
<script>
import { Bar, mixins } from 'vue-chartjs';
const { reactiveProp } = mixins;
export default {
extends: Bar,
mixins: [reactiveProp],
props: ['chartOptions'],
mounted() {
this.renderChart(this.chartData, this.chartOptions);
},
watch: {
chartData() {
this.renderChart(this.chartData, this.chartOptions);
},
},
};
</script>
Use together with dynamic styles.
Chart properties:
<template>
<div style="height:300px;">
<bar-chart :styles="myStyles" :chart-data="dataCollection"
:chart-options="chartOptions"></bar-chart>
</div>
</template>
<script>
import BarChart from './ChartBar.vue';
export default {
components: {
BarChart,
},
props: ['dataCollection'],
data() {
return {
myStyles: {
height: '300px',
width: '100%',
position: 'relative',
},
chartOptions: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
},
gridLines: {
display: true,
},
}],
xAxes: [{
ticks: {
beginAtZero: true,
},
gridLines: {
display: false,
},
}],
},
legend: {
display: true,
},
tooltips: {
enabled: true,
mode: 'single',
callbacks: {
label(tooltipItems, data) {
const { datasetIndex, index } = tooltipItems;
const value = data.datasets[datasetIndex].data[index];
if (parseInt(value, 10) > 999) {
return ` ${value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')}`;
}
return ` ${value}`;
},
},
},
responsive: true,
maintainAspectRatio: false,
height: 300,
},
};
},
};
</script>
<style lang="scss" scoped>
</style>
When I try to create a test using Cucumber and Gherkin I get a strange error. I show you the error first, then the files login.step.ts and login.po.ts:
// Error:
Undefined. Implement with the following snippet:
When('Enter the card number in the box', function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
// login.step.ts
import { expect } from 'chai';
const { Given, When, Then, Before } = require('cucumber');
import { browser, by, element } from 'protractor';
import { LoginPage } from './login.po';
let login: LoginPage;
Before(() => {
login = new LoginPage();
});
Given(/^Entering in Login$/, { timeout: 10 * 5000 }, async () => {
await browser.get('http://localhost:49152/login');
});
When(/^Enter the card number in the box$/, () => {
// login.setCardNumber('1234').then((txt) => {
// return 'ready!?';
// })
login.setCardNumber('1234');
});
// login.po.ts
import { browser, by, element, until } from 'protractor';
export class LoginPage {
navigateTo() {
return browser.get('/login');
}
setCardNumber(cardNumber) {
const input = element(by.css('#box'));
return input.sendKeys(cardNumber);
}
}
The first test passes successfully, but in the second test, the process ends in error.
[EDIT]
Adding the protractor.conf.js file:
exports.config = {
allScriptsTimeout: 11000,
specs: [
// './e2e/**/*.e2e-spec.ts',
'./e2e/features/*.feature'
],
capabilities: {
'browserName': 'chrome',
chromeOptions: {
args: ['disable-infobars']
},
metadata: {
browser: {
name: 'chrome',
version: '58'
},
device: 'Xubuntu Linux',
platform: {
name: 'Linux',
version: '16.04'
}
}
},
directConnect: true,
frameworkPath: require.resolve('protractor-cucumber-framework'),
plugins: [{
package: 'protractor-multiple-cucumber-html-reporter-plugin',
options: {
automaticallyGenerateReport: true,
removeExistingJsonReportFile: true
}
}],
cucumberOpts: {
require: ['./e2e/steps/**/*.ts', './e2e/support/*.ts'],
tags: [],
dryRun: false,
compiler: [],
format: 'json:reports/results.json',
strict: true
},
baseUrl: 'http://localhost:4200/',
SELENIUM_PROMISE_MANAGER: false,
framework: 'custom',
onPrepare() {
require('ts-node').register({
project: 'e2e/tsconfig.e2e.json'
});
}
};
remove the double quote " behind box in /^Enter the card number in the box"$/
I found the answer to my problem. I only had to include the following configuration line to my Before() step in my test:
browser.ignoreSynchronization = true;
According to what I read in different places, it seems that Chrome is looking to perform some task with sockets, and with this line we are disabling this task.
I have two collections Colors and Cars.
In the car possibly to choose the color.
How to save/update object in collection so that embed Color object in Car object?
Cars = new Mongo.Collection('cars');
Cars.attachSchema(new SimpleSchema({
colorId: {
label: 'Color',
type: String,
autoform: {
options: function () {
return Colors.find().map(function (p) {
return {label: p.colorName, value: p._id};
});
},
label: false
}
},
color: {
type: Object,
},
'color._id': {
type: String,
autoform: {
omit: true,
},
},
'color.colorName': {
type: String,
autoform: {
omit: true,
},
},
'color.colorCode': {
type: String,
autoform: {
omit: true,
},
},
}));
Colors = new Mongo.Collection('colors');
Colors.attachSchema(new SimpleSchema({
colorName: {
type: String,
label: "Color Name",
max: 20,
},
colorCode: {
type: String,
optional: true,
label: "Color Code",
autoform: {
afFieldInput: {
type: "color"
}
}
},
}));
I try use
AutoForm.hooks({ insertCarForm: {before: {
but it did not work
There are several ways that you can achieve this and the solution largly depends on any relevant packages that you might be using. It's hard to give a working example without seeing your existing code that creates new 'cards'. Nevertheless, here is an example using the core Meteor API.
Assuming you have some form Template defined (which I have called 'manageCar'), you would do something like this.
Define a Meteor Method to handle inserting/updating the Car.
Meteor.methods({
updateCar: function(carDoc) {
check(carDoc, { /* carDoc schema */ });
const color = Colors.findOne(carDoc.colorId);
carDoc.color = color;
if (carDoc._id) {
Cars.update(carDoc._id, {
$set: {
colorId: carDoc.colorId,
color: carDoc.color,
}
})
} else {
Cars.insert(carDoc);
}
},
});
Add an event handler for the form submission that calls the defined Method.
Template.manageCar.events({
'click .js-save-car'(event, instance) {
const data = {
_id: event.target._id.value,
colorId: event.target.colorId.value
};
Meteor.call('updateCar', data, function(error, result) {
if (!error) {
alert('Car updated successfully');
}
});
}
});
Long story short, you just need to make sure you have access to the Color id that you are saving for the Car and then make sure you perform a find on the Color collection to retrieve the necessary Color document, then use that for your Car insert or update.
Let me know if you have any questions or need further explanation.