I have a chart component, and my job is to make a button to change it's type (eg. columns to pie), but i don't know how to change it on a button click event. Here's the structure of the component (the idea is to change the :series-defaults-type when the button with ChangeType id is pressed)
<template>
<div style="width: 100%;overflow: overlay;border-radius: 20px;">
<button id="changeType" #click="changeType()">Change</button>
<chart-more-option :kpiName="'EquipmentRetirementForecast'" v-if="showMoreOptions"/>
<chart :title-text="'Equipment Retirement Forecast'"
:title-color="'#FFF'"
:title-font="'openSans'"
:chart-area-background="'#1B1534'"
:legend-visible="false"
:series-defaults-type= "'column'"
:series="series"
:category-axis="categoryAxis"
:axis-defaults-color="'#FFF'"
:axis-defaults-labels-rotation-angle="'30'"
:value-axis="valueAxis"
:tooltip="tooltip"
:theme="'sass'"
:zoomable-mousewheel="true">
</chart>
</div>
</template>
<script>
import { Chart } from '#progress/kendo-charts-vue-wrapper';
import ChartMoreOption from '../ChartMoreOption';
export default {
name: 'EquipmentRetirementForecast',
components: {
'chart': Chart,
ChartMoreOption
},
props: {
fetchData: {
type: Boolean,
default: false
},
showMoreOptions: {
type: Boolean,
default: true,
},
},
watch: {
labelAlign(){
var c = this.$refs.chart
c.updateWidget();
}
},
computed:{
requestBody(){
return this.$store.getters['usersession/getTopologyRequestBody']
},
series(){
return this.$store.getters['riskmanagement/getRetirementForecastSeries']
},
categoryAxis(){
return this.$store.getters['riskmanagement/getRetirementForecastCategoryAxis']
},
},
data: function() {
return {
valueAxis: [{
line: {
visible: false
},
minorGridLines: {
visible: true
},
labels: {
rotation: "auto"
}
}],
tooltip: {
visible: true,
template: "#= series.name #: #= value #",
},
}
},
mounted(){
if(this.fetchData){
this.$store.dispatch("riskmanagement/FetchRetirementForecastData",this.requestBody).then(()=>{
});
}
},
methods: {
changeType(){
//code goes here
}
}
}
</script>
<style src="../style-dashboard.scss" lang="scss" scoped />
This is the chart i need to change:
Changing the :series-defaults-type to pie by hand, it works, but i need to make that change in a button click, as follows:
Add a data property and give it the default of 'column', name it for example chartType. Then inside the changeType() you add this.chartType = 'pie'. And change :series-defaults-type= "'column'" to :series-defaults-type= "chartType".
Also remember to NOT use : for attribute values that are hardcoded. So :chart-area-background="'#1B1534'" should be chart-area-background="#1B1534".
Related
In my code, I am using AG-Grid with the master detail property to display some data. The code doesn't get any errors and the master row has records, but when I expand the detail, not 1 row is present even though in the network, params.data has values. My code is below. What am I doing wrong, and how should I fix it?
constructor(
private _reportService: ReportService,
) {
this._reportService
.getAllSupplierProductList()
.subscribe((response: any) => {
this.rowData = response;
});
}
public detailCellRendererParams: any = {
detailGridOptions: {
columnDefs: [
{ headerName: 'Ürün Kodu', field: 'StockIntegrationCode' },
{ headerName: 'Ürün Adı', field: 'ProductName' },
{ headerName: 'Ürün Kategorisi', field: 'CategoryName' },
],
defaultColDef: {
flex: 1,
filter: 'agTextColumnFilter',
resizable: true,
sortable: true,
floatingFilter: true,
},
},
getDetailRowData: (params) => {
params.successCallback(params.data.StockIntegrationCode && params.data.ProductName && params.data.CategoryName);
},
} as IDetailCellRendererParams;
rowData: Observable<IReportRow[]>;
onFirstDataRendered(params: FirstDataRenderedEvent) {
// arbitrarily expand a row for presentational purposes
setTimeout(function () {
params.api.getDisplayedRowAtIndex(1)!.setExpanded(true);
}, 0);
}
onGridReady(params: GridReadyEvent) {
}
In the getDetailRowData function, you're given params.data, which is the data of the master row. You should obtain details and pass it to the params.successCallback function like this:
getDetailRowData: (params) => {
this._reportService
.getProductList(params.data.SupplierId)
.subscribe(products => {
params.successCallback(products);
});
}
I have a component which I wish I could create rows and columns with data and columns object from the parent.
There is a situation where I need to render an html template to create a clickable link, I make it want to display the link in the Actions column. vue version: ^2.5.17
below is the code from parent.vue:
// parent.vue
<table-component :data="category" :column="column" class="bg-red-200 py-4 mt-8"/>
// parent.vue
data(){
return {
modalMode: '',
isShowModalEdit: false,
category: [
{ id: 1, name: 'Jasa Pre-Order', color: '#eb4034' },
{ id: 2, name: 'Jualan', color: '#fcba03' },
{ id: 3, name: 'Jasa Design', color: '#9f34eb' },
],
}
}
// parent.vue
methods: {
toggleModalEdit(){
this.isShowModalEdit = !this.isShowModalEdit
this.modalMode = 'EDIT'
}
}
// parent.vue
computed: {
column() {
return [
{
dataField: 'name',
text: 'Name',
},
{
dataField: 'color',
text: 'Category Color',
formatter: (cell,row) => {
return `
<div style="background-color: ${cell};" class="rounded-full h-8 w-8 flex items-center justify-center mr-2"></div>
<div class="font-bold text-gray-500">${cell}</div>
`
},
classes: (cell, row, rowIndex, colIndex) => {
return 'flex';
}
},
{
dataField: 'actions',
text: 'Actions',
formatter: (cell,row) => {
return `
Edit
`
},
},
]
},
}
and this is the sample code from component.vue:
// component.vue
<tbody class="bg-white divide-y divide-gray-200">
<tr v-for="(row, rowIndex) in data" :key="rowIndex">
<td v-for="(col, colIndex) in column" :key="col.dataField" :class=" col.classes ? col.classes(row[col.dataField],row,rowIndex,colIndex) : '' " v-html=" col.formatter ? col.formatter(row[col.dataField],row) : row[col.dataField] " class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"></td>
<tr>
</tbody>
// component.vue
props: {
data: {
type: Array,
required: true
},
column: {
type: Array,
required: true
},
}
the result is like this:
but the link in the Actions column does not work as it should, I hope that when clicking this link will run the method of the perent namely toggleModalEdit. and this is what the link looks like when i inspect it:
i am still new to vue, i am not sure what i did best or not, i hope you guys can help.
Your issue is, that the HTML inside the v-html directive is not processed by Vue's template compiler at all.
Because the compiler doesn't compile this HTML, the #click is not interpreted (but simply rendered without triggering any action).
For this to work, you'd need to iterate over all columns and initialize a new component that handles what's inside the cell yourself directly in HTML (and not in some string that's gonna be rendered later on).
I guess that this is enough - if you still need to interpret what's in the string, you may use Vue.compile to interpret the content. But be careful as it's not safe in case there's some malicious code in it - but since the directive by default has no sanitizing at all, I guess that's just the way Vue.js works.
Thanks to #SimplyComple0x78 for the answer, I marked your suggestions:
For this to work, you'd need to iterate over all columns and initialize a new component that handles what's inside the cell yourself directly in HTML (and not in some string that's gonna be rendered later on).
so I try to create and initialize a new component, I call it element-generator. reference from here. here's the code:
// element-generator.vue
<script>
export default {
render: function (createElement) {
const generatedChildren = (child) => {
if(!child) return // when child of undefined
if(typeof child === 'string') return child // when children is String
return child.map((e,i,a)=>{
if(typeof child[i] == 'string'){
return child[i]
}else{
return createElement(
child[i].tag,
child[i].attributes,
generatedChildren(child[i].children) // javascript recursive
)
}
})
}
return createElement(
this.formatter.html.tag,
this.formatter.html.attributes,
generatedChildren(this.formatter.html.children)
)
},
props: {
formatter: {
type: Object,
required: true
},
},
}
</script>
and I no longer use v-html in component.vue instead I just do a check inside <td> and call element-generator to handle what's inside the cell:
// component.vue
<tbody class="bg-white divide-y divide-gray-200">
<tr v-for="(row, rowIndex) in data" :key="rowIndex">
<td v-for="(col, colIndex) in column" :key="col.dataField"
:class=" col.classes ? col.classes(row[col.dataField],row,rowIndex,colIndex) : '' "
class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"
>
<element-generator v-if="col.formatter" :formatter="col.formatter(row[col.dataField],row)"></element-generator>
<div v-else>{{row[col.dataField]}}</div>
</td>
</tr>
</tbody>
and in parent.vue I replaced the String with the Object that will be passed to the element-generator later, it looks like this:
// parent.vue
computed: {
column() {
return [
{
dataField: 'name',
text: 'Name',
},
{
dataField: 'color',
text: 'Category Color',
formatter: (cell,row) => {
return {
html: {
tag: 'div',
attributes: {
class: 'flex'
},
children:[
{
tag: 'div',
attributes: {
style: `background-color: ${cell};`,
class: 'rounded-full h-8 w-8 flex items-center justify-center mr-2',
},
},
{
tag: 'div',
attributes: {
class: 'font-bold text-gray-500',
},
children: cell
},
]
}
}
},
},
{
dataField: 'actions',
text: 'Actions',
formatter: (cell,row) => {
return {
html: {
tag: 'a',
attributes: {
class: 'text-indigo-600 hover:text-indigo-900',
on: {
click: this.toggleModalEdit
},
attrs: {
href: "#"
},
},
children: 'Edit'
},
}
},
},
]
},
},
then when I inspect it in the browser, the result is like this(this is different from the previous one):
and finally what I want to display when Edit is clicked is now displayed:
Thank you very much everyone.
I'm using the material-table component, which I fill with dynamic data coming from Redux (an array of objects), but then I do other things with that data inside my component's state. To create column dropdown filters, there's an element inside each column's array of options, lookup, that receives an object and creates the dropdown based on it's values.
I am extracting some items from my data and putting them inside an element in my component's state. This is an object, the same kind that lookup receives. The thing is that the component shows an empty dropdown, as if the object was empty, but it's not. I'm logging it in into the console and the object is filled with the data I need.
I initially thought it was a render problem, that the object is empty at the beggining, and then it's filled with data, but the component renders every time.(Yeah, React is reactive).
This is only the code needed to help me solve this problem:
Table component
import React, { Component } from "react";
import MaterialTable from "material-table";
class CustomTable extends Component {
state = {
column1: "",
column2: "",
column3: "",
column1FilterList: {}
columns: [
{
title: "Column1",
field: "column1",
editable: "onAdd",
filtering: true,
lookup: { ...this.column1FilterList }
},
{
title: "Column2",
field: "column2",
editable: "onAdd",
filtering: true,
},
{
title: "Column3",
field: "column3",
editable: "onAdd",
filtering: true
}
]
};
componentDidMount() {
this.props.fetchValues()
this.props.fetchApplications()
this.filterColumn1ExistingKeys()
}
filterColumn1ExistingKeys = () => {
return this.props.elements.map(element => {
return this.setState(prevState => ({
column1FilterList: {
...prevState.column1FilterList,
[element.name]: element.name
}
}))
})
}
render() {
return (
<div>
<MaterialTable
options={{
search: false,
actionsColumnIndex: 4,
filtering: true
}}
title="Search by"
columns={this.state.columns}
data={this.state.data}
/>
</div>
);
}
}
export default CustomTable;
The problem is how you save that data. You create a new object in the constructor with { ...this.column1FilterList }. This will create a new object which will act as the lookup object, which is filled with the initial data of column1FilterList (empty). Updating the column1FilterList later does not change that lookup object, because it is disconnected (new object). You have to update the lookup within the columns as well like this:
const filterColumn1ExistingKeys = () => {
const column1FilterList = this.state.column1FilterList;
this.props.elements.forEach(element => column1FilterList[element.name] = element.name)
this.setState({
column1FilterList,
columns: [{
title: "Column1",
field: "column1",
editable: "onAdd",
filtering: true,
lookup: { ...column1FilterList }
},
{
title: "Column2",
field: "column2",
editable: "onAdd",
filtering: true,
},
{
title: "Column3",
field: "column3",
editable: "onAdd",
filtering: true
}
]
})
}
Hope this helps. Let me know, if that works for you. If you have any questions, let me know. Happy coding.
I'm using Quasar Framework with VueJS. I have a Data Table component that allows single item selection. The Data Table data is automatically updated every 5 seconds through websockets events. If I have an item selected, every time the Data Table data is updated, that item gets deselected, and I would like to persist the item selection after the data is updated.
Is there a way to accomplish this? I was thinking in two different approachs, but I have no idea how to do it, at least not from the documentation:
access directly the Data Table selected item
handle an event fired when an item is selected
In both cases the main idea would be to save the selection in a store, an restore the selection when the Data Table items update is done.
<template>
<q-layout>
<!-- Main block start-->
<div class="scroll" style="width: 100%">
<div class="layout-padding">
<div class="row" style="margin-top: 30px;">
<q-data-table :data="dataTableData" :config="config" :columns="columns" #refresh="refresh">
</q-data-table>
</div>
</div>
</div>
<!-- Man block end-->
</q-layout>
</template>
<script>
export default {
sockets:{
connect: function(){
console.log('socket connected')
},
info: function(data){
console.log('sockets: info',data)
//This is where I should get the current selected item if exists
this.dataTableData = data;
//This is where I should restore the selected item
}
},
methods: {
getInfo: function(val){
this.$socket.emit('getInfo', val);
},
refresh (done) {
this.$socket.emit('getInfo');
done();
}
},
created: function(){
this.getInfo()
},
data: function () {
return {
dataTableData: [],
config: {
title: 'My Data Table',
refresh: true,
columnPicker: false,
leftStickyColumns: 0,
rightStickyColumns: 0,
rowHeight: '50px',
responsive: true,
selection: 'single',
messages: {
noData: '<i>warning</i> No data available to show.'
}
},
columns: [
{ label: 'Name', field: 'name', width: '200px', sort: true },
{ label: 'Size', field: 'size', width: '50px', sort: true },
{ label: 'Status', field: 'status', width: '50px', sort: true },
{ label: 'Progress', field: 'progress', width: '150px', sort: false },
{ label: 'Speed', field: 'speed', width: '50px', sort: false },
{ label: 'ETA', field: 'eta', width: '50px', sort: false }
],
pagination: false,
rowHeight: 50,
bodyHeightProp: 'auto',
bodyHeight: 'auto'
}
},
watch: {
}
}
</script>
UPDATE
As far as I can see, they're already working in this enenhancement for the Quasar-Framework v0.14 =)
I'm currently trying to get a kendo ColorPalette to work with inline editing on my grid. I've pretty much got it all figured out except that I'm having some difficulties posting the selected color value to my controller.
Kendo Grid:
$("#ContactTagsGrid").kendoGrid({
dataSource: new kendo.data.DataSource({
transport: {
read: {
url: "/Admin/Tag/GetTagsByOrg",
dataType: "json",
data: {
orgId: OrganizationId
}
},
create: {
url: "/Admin/Tag/Create",
dataType: "json",
type: "POST",
data: function () {
return kendo.antiForgeryTokens();
}
}
},
schema: {
model: {
id: "Id",
fields: {
Id: { type: "number", nullable: true },
OrgId: { type: "number" },
Name: { type: "string" },
Color: { type: "string", defaultValue: "#f20000", validation: { required: true } }
}
}
},
pageSize: 20
}),
pageable: true,
sortable: true,
filterable: {
extra: false
},
scrollable: false,
columns: [
{
field: "Id",
hidden: true
},
{
field: "Name"
},
{
field: "Color",
editor: colorEditor,
template: function(dataItem) {
return "<div style='width: 25px; background-color: " + dataItem.Color + ";'> </div>";
},
width: "500px"
},
{
command: [
{
name: "Edit",
template:
"<a href='\\#' class='small btn btn-link k-grid-edit edit-text'><span class='fa fa-pencil'></span>Edit</a>",
text: "",
className: "fa fa-pencil"
},
{
template:
"<a href='\\#' class='small btn btn-link danger delete-text k-grid-delete'><span class='fa fa-trash-o'></span>Delete</a>",
name: "Delete",
text: " Delete",
className: "fa fa-trash-o"
}
],
width: "170px"
}
],
editable: {
mode: "inline",
destroy: false // don't use the kendo destroy method since we're using bootbox
},
// Custom save/cancel buttons
edit: function (e) {
var command = e.container.find("td:last");
command.html("<a href='\\#' class='small btn btn-primary k-grid-update'>Save</a><a href='\\#' class='small btn btn-default k-grid-cancel' style='margin-left: 5px'>Cancel</a");
}
});
Javascript to replace the default grid inline-editor with the Kendo ColorPalette:
function colorEditor(container, options) {
// create an input element
var div = $("<div></div>");
var input = $("<input />");
input.attr("name", "Color");
// append it to the container
div.appendTo(container);
input.appendTo(div);
// initialize a Kendo UI ColorPicker
div.kendoColorPalette({
palette: [
"#f20000", "#c60000", "#337a00"
],
columns: 3,
change: function () {
var color = this.value();
$("input[name=Color]").val(color);
}
});
}
When I click on the Save button, the only values that are posted to my controller are the Name and the OrgId.
If I set a defaultValue in the schema of my model like I did in the code above, then the default value for Color is always posted no matter if I select a different color or not.
If I do not set a defaultValue in the schema, then the value that is posted for Color is null.
So basically, I just need help updating my model so that it is correct when posting to the controller. I can see that the value of my input <input name="Color" /> is being updated correctly every time that I select a new color but again, it's not actually posting the value that it contains.
Not sure if this is needed, but here is what my model looks like:
public class TagCreateViewModel
{
public int OrgId { get; set; }
public string Name { get; set; }
public string Color { get; set; }
}
I ended up stumbling across this question on SO that helped me solve my problem. I just adapted it to use the Kendo Color Palette instead and it works perfectly now.
function colorEditor(container, options) {
$("<div type='color' data-bind='value:" + options.field + "'/>")
.appendTo(container)
.kendoColorPalette({
palette: [
"#f20000", "#c60000", "#337a00", "#54b010", "#9adc0d", "#28cb9b", "#0eac98", "#0ed6e8", "#14a7d1",
"#bc0aef", "#560ea7", "#2713bc", "#1457d1"
],
columns: 7
});
}