Ng2-Charts Unexpected change chart's color when data is changed - angular7

In my project I use ng2-charts. All works fine and chart is shown as expected (data, labels, chart's colors), but when data is changed then color of chart become grey by default. May someone help to correct problem with chart's color?
Here is my code:
import { ChartDataSets } from 'chart.js';
import { Color, Label } from 'ng2-charts';
...
export class JuridicalBidPrimaryComponent extends BidComponent {
lineChartData: ChartDataSets[];
lineChartLabels: Label[];
lineChartLegend = true;
lineChartType = 'line';
lineChartColors: Color[] = [
{
backgroundColor: 'rgba(148,159,177,0.2)',
borderColor: 'rgba(148,159,177,1)'
},
{
backgroundColor: 'rgba(77,83,96,0.2)',
borderColor: 'rgba(77,83,96,1)'
}];
options: any = {
legend: { position: 'bottom' }
}
constructor(
...//inject services
) {
super();
this.initData();
};
initData(): void {
this.lineChartData = [];
this.lineChartLabels = [];
if (this.cabinetId)
this.getData(this.year);
}
getData(year: number) {
this.isLoading = true;
var limitPromise = this.juridicalLimitService.getPrimary(this.cabinetId, year).catch(error => {
this.notificationService.error(error);
return Observable.throw(error);
});
var analyticsPromise = this.juridicalAnalyticsService.getUsedEnergy(this.cabinetId, year).catch(error => {
this.notificationService.error(error);
return Observable.throw(error);
});
forkJoin([limitPromise, analyticsPromise]).subscribe(data => {
this.limits = data[0];
this.lineChartLabels = data[1].map(e => e.Period);
this.lineChartData.push(
{
data: data[1].map(e => e.Limit),
label: 'Bid'
},
{
data: data[1].map(e => e.Used),
label: 'Used'
}
);
this.isLoading = false;
}, error => {
this.isLoading = false;
});
}
}
export abstract class BidComponent {
cabinetId: number;
isLoading: boolean = false;
#Input("periods") periods: BaseDictionary[];
#Input("cabinetId") set CabinetId(cabinetId: number) {
this.cabinetId = cabinetId;
this.initData();
}
abstract initData(): void;
}
As you can see this component is partial and I use setter to listen of cabinetId changes.
Here is html part:
...
<canvas baseChart width="400" height="150"
[options]="options"
[datasets]="lineChartData"
[labels]="lineChartLabels"
[legend]="lineChartLegend"
[chartType]="lineChartType"
[colors]="lineChartColors"></canvas>
...
And I use this component as:
<app-juridical-bid-primary [cabinetId]="cabinetId"></app-juridical-bid-primary>
I find similar question similar question, but, unfortunately, don't understand answer

After some hours of code testing I find answer. It is needed to correct code from question:
...
import * as _ from 'lodash'; //-- useful library
export class JuridicalBidPrimaryComponent extends BidComponent {
lineChartData: ChartDataSets[] = [];
lineChartLabels: Label[] = [];
...
initData(): void {
/*this.lineChartData = [];
this.lineChartLabels = [];*/ //-- this lines is needed to remove
if (this.cabinetId)
this.getData(this.year);
}
getData(year: number) {
...
forkJoin([limitPromise, analyticsPromise]).subscribe(data => {
this.limits = data[0];
this.lineChartLabels.length = 0;
this.lineChartLabels.push(...data[1].map(e => e.Period));
if (_.isEmpty(this.lineChartData)) {
//-- If data array is empty, then we add data series
this.lineChartData.push(
{
data: data[1].map(e => e.Limit),
label: 'Замовлені величини'
},
{
data: data[1].map(e => e.Used),
label: 'Використано'
}
);
} else {
//-- If we have already added data series then we only change data in data series
this.lineChartData[0].data = data[1].map(e => e.Limit);
this.lineChartData[1].data = data[1].map(e => e.Used);
}
this.isLoading = false;
}, error => {
this.isLoading = false;
});
}
}
As I understand ng2-charts, if we clean dataset (lineChartData) and add new data then the library understand this as create new series and don't use primary settings for the ones. So we have to use previous created series.
I hope it will be useful for anyone who will have such problem as I have.

Related

Custom resolver React-hook-form

Can't get errors from resolver using react-hook-form custom resolvers. Trying to validate some fields dynamically. But if I submit form I can get errors fields
export const customResolver = (parameters) => {
return (values, _context, { fields, names }) => {
const transformedParameters = transformParameters(parameters);
const paramsIds = getParamsIds(transformedParameters);
const { name, value } = Object.values(fields)[0];
let errors = {};
if (names && names[0].length <= 3) {
// Validate single field
const textRequired = transformedParameters
.find(({ id }) => id === name)
.values.find(({ name: valueName }) => value === valueName).textRequired;
if (textRequired && !values[`${names} commentary`]) {
errors = {
[`${names} commentary`]: {
type: "required",
message: "Поле необходимо заполнить!",
},
};
}
} else {
// Validate onSubmit method
errors = Object.entries(values).reduce((acc, [id, fieldValue]) => {
if (paramsIds.includes(id) && !fieldValue) {
return {
...acc,
[id]: { type: "required", message: "Поле необходимо заполнить!" },
};
}
return acc;
}, {});
}
return { values, errors };
};
};
What did I do wrong?

Select2 doesn't work change value from jQuery (dataAdapter)

I have a huge json data source (over 50,000 + lines) loaded in memory from a static file.
Note: It's not important why I have it in a static file.
I use select2 (v 4.0.5) that initializes as:
function initSelect2(selectName, dataSelect) {
var pageSize = 20;
$.fn.select2.amd.require(["select2/data/array", "select2/utils"],
function (ArrayData, Utils) {
function CustomData($element, options) {
CustomData.__super__.constructor.call(this, $element, options);
}
Utils.Extend(CustomData, ArrayData);
CustomData.prototype.query = function (params, callback) {
if (!("page" in params)) {
params.page = 1;
}
var data = {};
data.results = dataSelect.slice((params.page - 1) * pageSize, params.page * pageSize);
data.pagination = {};
data.pagination.more = params.page * pageSize < dataSelect.length;
callback(data);
};
$('#mySelect3').select2({
ajax: {},
dataAdapter: CustomData,
width: '100%'
});
});
}
I have one big problem. I can not set the value to select from jQuery. If I try like this:
$ ("#mySelect3").val(17003).trigger("change");
nothing will happen. But I think I'm doing it badly. If I understand the documentation I think I should implement function:
CustomData.prototype.current = function (callback) {}
that should create the data, and then function:
CustomData.prototype.query = function (params, callback) {}
should only filter them.
Can you please help me, how do I implement select2 initialization, that can work with many options and can by set from jQuery?
With custom data adapter you don't need pagination :
// create huge array
function mockData() {
var hugeArray = [];
for (let i = 0; i < 50000; i++) {
el = {
id: i,
text: 'My mock data ' + i,
};
hugeArray.push(el);
}
return hugeArray;
};
// define custom dataAdapter
$.fn.select2.amd.define("myCustomDataAdapter",
['select2/data/array','select2/utils'],
function (ArrayData, Utils) {
function CustomData ($element, options) {
CustomData.__super__.constructor.call(this, $element, options);
};
Utils.Extend(CustomData, ArrayData);
CustomData.prototype.query = function (params, callback) {
var data = {
// here replace mockData() by your array
results: mockData()
};
callback(data);
};
return CustomData;
}
);
//
$('#mySelect3').select2({
allowClear: true,
// use dataAdapter here
dataAdapter:$.fn.select2.amd.require("myCustomDataAdapter"),
});
And with search you can do like this :
// create huge array
function mockData() {
var hugeArray = [];
for (let i = 0; i < 50000; i++) {
el = {
id: i,
text: 'My mock data ' + i,
};
hugeArray.push(el);
}
return hugeArray;
};
// define custom dataAdapter
$.fn.select2.amd.define("myCustomDataAdapter",
['select2/data/array','select2/utils'],
function (ArrayData, Utils) {
function CustomData ($element, options) {
CustomData.__super__.constructor.call(this, $element, options);
};
Utils.Extend(CustomData, ArrayData);
CustomData.prototype.query = function (params, callback) {
var data = {
// here replace mockData() by your array
results: mockData()
};
if ($.trim(params.term) === '') {
callback(data);
} else {
if (typeof data.results === 'undefined') {
return null;
}
var filteredResults = [];
data.results.forEach(function (el) {
if (el.text.toUpperCase().indexOf(params.term.toUpperCase()) == 0) {
filteredResults.push(el);
}
});
if (filteredResults.length) {
var modifiedData = $.extend({}, data, true);
modifiedData.results = filteredResults;
callback(modifiedData);
}
return null;
}
};
return CustomData;
}
);
//
$('#mySelect3').select2({
minimumInputLength: 2,
tags: false,
allowClear: true,
// use dataAdapter here
dataAdapter:$.fn.select2.amd.require("myCustomDataAdapter"),
});
I had the same issue and this is how I solved it.
Note: We are using dataAdapter because we dealing with large that, so I am assuming your drop-down will contain large amount of data.
After implementing your DataAdapter with a query use this example to initialize your select2.
if(selectedValue !== null )
{
$("#item_value").select2({
placeholder: 'Select an option',
allowClear: true,
disabled: false,
formatLoadMore: 'Loading more...',
ajax: {},
data: [{ id: selectedValue, text: selectedValue }],
dataAdapter: customData
});
$("#item_value").val(selectedValue).trigger('change');
}else{
$("#item_value").select2({
placeholder: 'Select an option',
allowClear: true,
disabled: false,
formatLoadMore: 'Loading more...',
ajax: {},
dataAdapter: customData
});
}
To set selected value in select2 you need to pass some data into data option, but as we are dealing with large amount of data. You can't pass the complete array of large data you have as it's going to cause your browser window to freeze and lead to a bad user performance.
But instead set the data option only with the selected value you got from db or variable.
I hope this helps.

Relay uses initial variable during setVariables transition, not "last" variable

I have a page where a bunch of file ids get loaded from localStorage, then when the component mounts / receives new props, it calls setVariables. While this works and the new variables are set, the results from the initial variables is used during the transition, which causes an odd flickering result.
Why would Relay give me something different during the transition at all? My expectation would be that this.props.viewer.files.hits would be the same as the previous call while setVariables is doing its thing, not the result from using the initial variables.
const enhance = compose(
lifecycle({
componentDidMount() {
const { files, relay } = this.props
if (files.length) {
relay.setVariables(getCartFilterVariables(files))
}
},
}),
shouldUpdate((props, nextProps) => {
if (props.files.length !== nextProps.files.length && nextProps.files.length) {
props.relay.setVariables(getCartFilterVariables(nextProps.files))
}
return true
})
)
export { CartPage }
export default Relay.createContainer(
connect(state => state.cart)(enhance(CartPage)), {
initialVariables: {
first: 20,
offset: 0,
filters: {},
getFiles: false,
sort: '',
},
fragments: {
viewer: () => Relay.QL`
fragment on Root {
summary {
aggregations(filters: $filters) {
project__project_id {
buckets {
case_count
doc_count
file_size
key
}
}
fs { value }
}
}
files {
hits(first: $first, offset: $offset, filters: $filters, sort: $sort) {
${FileTable.getFragment('hits')}
}
}
}
`,
},
}
)
Ah I finally figured this out. prepareParams was changing the value
export const prepareViewerParams = (params, { location: { query } }) => ({
offset: parseIntParam(query.offset, 0),
first: parseIntParam(query.first, 20),
filters: parseJsonParam(query.filters, null), <-- setting filters variable
sort: query.sort || '',
})
const CartRoute = h(Route, {
path: '/cart',
component: CartPage,
prepareParams: prepareViewerParams, <--updating variable
queries: viewerQuery,
})

NativeScript with Angular2 data binding not working with geolocation

I am developing a simple POC to display the current position.
The program receives the information from the geolocation.watchLocation however the location is not bind and displayed on the screen until I press the button STOP Monitoring. To be noted that the log is correctly showing the coordinates
JS: Start Monitoring ...
JS: Location: 49.6411839:6.0040451
JS: Stop Monitoring ...
import {Component} from "#angular/core";
import {Router} from "#angular/router";
import geolocation = require("nativescript-geolocation");
#Component({
selector: "TrackingPageSelector",
template:`
<StackLayout>
<Button [text]="watchId==0 ? 'Start Monitoring' : 'Stop Monitoring'" (tap)="buttonStartStopTap()"></Button>
<Label class="labelValue" [text]="latitude"> </Label>
<Label class="labelValue" [text]="longitude"> </Label>
</StackLayout>`
})
export class TrackingPageComponent {
latitude: number = 0;
longitude: number = 0;
watchId: number = 0;
constructor(private router: Router) {}
ngOnInit() {
if (!geolocation.isEnabled()) geolocation.enableLocationRequest();
}
public buttonStartStopTap() {
if (this.watchId != 0) {
console.log('Stop Monitoring ...');
geolocation.clearWatch(this.watchId);
this.watchId = 0;
} else {
console.log('Start Monitoring ...');
let _self = this;
this.watchId = geolocation.watchLocation(
function (loc) {
if (loc) {
_self.latitude = loc.latitude;
_self.longitude = loc.longitude;
console.log(`Location: ${_self.latitude}:${_self.longitude}`);
}
},
function(e){
this.errorMsg = e.message;
},
{desiredAccuracy: 3, updateDistance: 10, minimumUpdateTime: 1000 * 3}); // Should update every X seconds
}
}
}
If you use ()=> instead of function () then you don't need the _self hack.
It looks like you need to invoke change detection manually. NgZone seems not to cover geolocation.watchLocation()
constructor(private cdRef:ChangeDetectorRef) {}
...
this.watchId = geolocation.watchLocation(
(loc) => {
if (loc) {
this.latitude = loc.latitude;
this.longitude = loc.longitude;
this.cdRef.detectChanges();
console.log(`Location: ${this.latitude}:${this.longitude}`);
}
},
(e) => {
this.errorMsg = e.message;
},
or alternatively zone.run(() => ...) like
constructor(private zone:NgZone) {}
...
this.watchId = geolocation.watchLocation(
(loc) => {
if (loc) {
this.zone.run(() => {
this.latitude = loc.latitude;
this.longitude = loc.longitude;
console.log(`Location: ${this.latitude}:${this.longitude}`);
});
}
},
(e) => {
this.errorMsg = e.message;
},
If you only change local fields cdRef.detectChanges() is the better option, if you call methods that might modify some state outside of the current component, zone.run(...) is the better option.

OncellChange Event of Slickgrid

I have a slickgrid with a checkbox column.
I want to capture the row id when the checkbox in the selected row is checked or unchecked.
Attached is the script for slickgrid in my view
I want when user checks the checkbox, active column in database should be set to true and when it is unchecked, Active column should be set to false.
<script type="text/javascript">
//$('input[type="button"]').enabled(false);
var grid;
var columnFilters = {};
var jsonmodel = #Html.Raw(Json.Encode(Model));
//function LoadNewMessagesData(messages) {
var dataView;
var data = [];
////console.log('lena:' + subdata.length);
$.each(jsonmodel, function (idx, spotlight) {
var pd = moment(spotlight.PostedDate);
data.push({ id: spotlight.ID, Department: spotlight.SubSection,Title: spotlight.Title, PostedDate: pd.format('YYYY-MM-DD'), Active: spotlight.Active })
});
var columns = [
{ id: '0', name: "Department", field: "Department", sortable: true},
{ id: '1', name: "Title", field: "Title", sortable: true},
{ id: '2', name: "PostedDate", field: "PostedDate", sortable: true }
];
var checkboxSelector = new Slick.CheckboxSelectColumn({
cssClass: "slick-cell-checkboxsel"
})
columns.push(checkboxSelector.getColumnDefinition());
var options = {
enableCellNavigation: true,
explicitInitialization: true,
enableColumnReorder: false,
multiColumnSort: true,
autoHeight: true,
forceFitColumns: true
};
dataView = new Slick.Data.DataView();
grid = new Slick.Grid("#myGrid", dataView, columns, options);
grid.setSelectionModel(new Slick.RowSelectionModel());
var pager = new Slick.Controls.Pager(dataView, grid, $("#pager"));
grid.registerPlugin(checkboxSelector);
dataView.onRowCountChanged.subscribe(function (e, args) {
grid.updateRowCount();
grid.render();
});
dataView.onRowsChanged.subscribe(function (e, args) {
grid.invalidateRows(args.rows);
console.log('testing');
grid.render();
});
dataView.onPagingInfoChanged.subscribe(function (e, pagingInfo) {
var isLastPage = pagingInfo.pageNum == pagingInfo.totalPages - 1;
var enableAddRow = isLastPage || pagingInfo.pageSize == 0;
var options = grid.getOptions();
if (options.enableAddRow != enableAddRow) {
grid.setOptions({ enableAddRow: enableAddRow });
}
});
var rowIndex;
grid.onCellChange.subscribe(function (e, args) {
console.log('onCellChange');
rowIndex = args.row;
});
if (grid.getActiveCell()) {
}
//onSelectedRowsChanged event, if row number was changed call some function.
grid.onSelectedRowsChanged.subscribe(function (e, args) {
if (grid.getSelectedRows([0])[0] !== rowIndex) {
console.log('onSelectedRowsChanged');
}
});
grid.onSort.subscribe(function (e, args) {
var cols = args.sortCols;
var comparer = function (dataRow1, dataRow2) {
for (var i = 0, l = cols.length; i < l; i++) {
var field = cols[i].sortCol.field;
var sign = cols[i].sortAsc ? 1 : -1;
var value1 = dataRow1[field], value2 = dataRow2[field];
var result = (value1 == value2 ? 0 : (value1 > value2 ? 1 : -1)) * sign;
if (result != 0) {
return result;
}
}
return 0;
}
dataView.sort(comparer, args.sortAsc);
});
grid.init();
dataView.beginUpdate();
dataView.setItems(data);
// dataView.setFilter(filter);
dataView.endUpdate();
//}
function RefreshMessagesGrid() {
//console.log('RefreshMessagesGrid');
//grid.invalidate();
grid.invalidateRows();
// Call render to render them again
grid.render();
grid.resizeCanvas();
}
</script>`enter code here`
Thanks in Advance !!!
You have to bind click event on rows....and get the id of the row whose checkbox is checked/unchecked
function bindClickOnRow() {
if($(this).find('.checkboxClass').attr('checked') == 'checked'){
checked = true;
} else {
checked = false;
}
rowId = $(this).attr('id');
}

Resources