Accessing AngularJS variable in slickgrid - angularjs

I have integrated SlickGrid with my Angular JS application. Earlier I was populating the Grid Data with plain hardcoded javascript code[see below]. But now I need to get the grid data from REST service which is invoked in my angular Js Controller and saved in an object array. How do I access a variable[array] defined in angular js inside my SlickGrid javascript code.
I tried replacing 'data' below with the array defined in angular js code but it's not working. Can anyone please help me here.
$(function () {
/* need to comment out this code once I can use the array defined in angular Js */
var data = [];
for (var i = 0; i < 500; i++) {
data[i] = {
title: "Task " + i,
duration: "5 days",
percentComplete: Math.round(Math.random() * 100),
start: "01/01/2009",
finish: "01/05/2009",
effortDriven: (i % 5 == 0)
};
}
grid = new Slick.Grid("#myGrid", data, columns, options);
})
Update: This is my slickgrid code. I am invoking a REST service to get the data in $scope.data1 and then using it to populate the slickgrid but I am getting an empty slickgrid. But if I hardcode the data in $scope.data1 , it works. What I am missing? Can anyone please help me. I have spent an entire day on this issue.
$scope.populateGridData = function()
FetchPopulation.get({id:1} , function(response ) {
$scope.data1 = [];
for (var i = 0; i < response.PopulationList.population.length; i++)
$scope.data1[i] = {
firstName: response.PopulationList.population[i].firstName,
lastName: response.PopulationList.population[i].lastName,
designation: response.PopulationList.population[i].designation,
department: response.PopulationList.population[i].department,
salary: response.PopulationList.population[i].salary,
rating: response.PopulationList.population[i].rating,
joiningDate: response.PopulationList.population[i].joiningDate,
employeeId: response.PopulationList.population[i].employeeId,
employeeType: response.PopulationList.population[i].employeeType,
manager: response.PopulationList.population[i].manager,
permanent: (i % 5 == 0),
percentComplete: Math.round(Math.random() * 100)
};
/*
$scope.data1= [{employeeId:"12345", firstName: "aaa", lastName: "bbb" , designation:"Business Analyst" , department:"FSI" ,
salary:"120000",rating:"1" , joiningDate:"12/8/2013" , employeeType:"permanent" , manager:"aaaa" }];
*/
var grid = new Slick.Grid("#myGrid", $scope.data1, $scope.columns, $scope.options);
$scope.grid.setSelectionModel(new Slick.CellSelectionModel());
});
};

Did you type by hand the commented line there? Because it's not valid JSON, everything has to be escape [{employeeId:"12345" should be [{"employeeId":"12345",... and even if it's not that, your dataset seems wrong. I am not using SlickGrid without the DataView as you are doing but if you take the basic example and copy this piece of code (pulled from SlickGrid example2:
$(function () {
for (var i = 0; i < 5; i++) {
var d = (data[i] = {});
d["title"] = "<a href='#' tabindex='0'>Task</a> " + i;
d["duration"] = "5 days";
d["percentComplete"] = Math.min(100, Math.round(Math.random() * 110));
d["start"] = "01/01/2009";
d["finish"] = "01/05/2009";
d["effortDriven"] = (i % 5 == 0);
}
grid = new Slick.Grid("#myGrid", data, columns, options);
})
it will most probably work... Try this piece out before going back to your code, but I strongly suspect your JSON result might not be valid as you think it is... now after you tried the basic sample and you go back to your code, you could try to validate your JSON output by going here: JSONLint just copy+paste your JSON in there and click validate.

Related

Issues with AnyGantt

Im using AnyGantt, but Im having problems setting it up correctly.
Here is the full code:
var endpoint = '/api/chart/data/'
var label = []
var start = []
var end = []
var werk = []
$.ajax({
method: 'GET',
url: endpoint,
success: function(data){
labels = data.label
start = data.start
end = data.end
uplant = data.werk
var obj = {}
var finalArray = []
for (var i = 1; i <= start.length; i++) {
var first = { id: i, name: uplant[i] }
obj = { ...obj, ...first }
var periods = { id: labels[i], start: start[i - 1], end: end[i - 1] }
if (obj.periods) {
obj.periods.push(periods)
} else {
obj.periods = [periods]
}
finalArray.push(obj)
}
anychart.onDocumentReady(function () {
var data = finalArray;
var treeData = anychart.data.tree(data, "asTable");
chart = anychart.ganttResource();
chart.data(treeData);
chart.getTimeline().scale().minimum("2018-01-01");
chart.getTimeline().scale().maximum("2020-01-01");
var dataGrid = chart.dataGrid();
dataGrid.column(0)
.title('#')
.width(30)
.cellTextSettings({hAlign: 'center'});
dataGrid.column(1)
.title('Werk')
.width(60)
.cellTextSettings({hAlign: 'left'})
.format(function () {
return this.name;
});
chart.getTimeline().horizontalScrollBar().enabled(true);
/* chart.getTimeline().periods().edit(true); */
chart.getTimeline().edit(true);
chart.getTimeline().tooltip(false);
chart.getTimeline().elements().labels(false);
chart.container("containerx");
chart.draw();
chart.fitAll();
});
},
error:function(error_data){
console.log("error")
console.log(error_data)
}});
I can't click and move the periods (tasks) and I cant scroll.
Thank you very much for any suggestions
Please find below a screenshot of the chart:
Please find below a screenshot of the chart:
Scale range
You are applying the scale min/max correctly:
chart.getTimeline().scale().minimum("2018-01-01");
chart.getTimeline().scale().maximum("2020-01-01");
But then you override the default scale and it drops the min/max settings:
var dateTimeScale = anychart.scales.dateTime();
var dateTimeTicks = dateTimeScale.ticks();
chart.xScale(dateTimeScale);
There's no need to do this! The default Gantt Chart is already a dateTime type. Solution - simply remove those three lines above from your code.
LiveEdit
Your line chart.getTimeline().periods().edit(true); is correct, but due to a little bug in the current version 8.7.0 (Aug 2019) this setter doesn't work for types of elements. To avoid it replace this line with the following line chart.getTimeline().edit(true);.
Scrollers
The Gantt chart doesn't support x/yScrollers, only simple scroll bars.
The following methods the Gantt chart doesn't support:
chart.xScroller(true);
chart.yScroller(true);
For zooming, you can use one of the API functions. This subject is described in detail in the Gantt zooming article.

ng-google-chart how to expose draw() method

I've created a sample of my chart below using
nicholas bering, API Promise. I faked the $http data callback in my demo below.
My question is how to correctly access the draw() method of the chart after it is already displayed in the browser?
In my demo below, I create a google.visualization.DataView() so I can access the hideRows() method. Once that occurs, the documentation says I need to call on the draw() method to repaint the chart with the newly altered row information.
In this case I'm trying to let the user hide rows of items where the quantity being displayed is zero (row 2 "Olives" in my data). Once I get this working, I will let the user toggle other things but for now I'm trying to keep my question simple.
But this is where I get lost... the draw() method as I understand it should already exist on the original chart I created. How does one expose the draw() method of the original chart without having to poke at the DOM with a document.getElementById('myBarChart'). This seems so unlike everything Angular.
Here is my code:
<div ng-controller="ChartsController as ChartsController"
ng-init="ChartsController.init()">
<button ng-click="ChartsController.ToggleZeroDistributionOff()">No Zeros</button><br>
<div google-chart chart="chartMe" id="myBarChart" />
</div>
now my controller:
'use strict';
app.controller('ChartsController', ['$scope', '$http', '$q', 'googleChartApiPromise', function ($scope, $http, $q, googleChartApiPromise) {
this.name = "ChartsController";
this.$inject = ['$scope', '$q', '$http', 'googleChartApiPromise'];
$scope.chartMe = {};
this.init = function () {
// simulated $http callback data returned in promise
var dataPromise = {
"data": [
{"itemname": "Mushrooms", "qty": 13 },
{"itemname":"Onions", "qty": 11},
{"itemname":"Olives", "qty": 0},
{"itemname":"Zucchini", "qty": 1},
{"itemname": "Pepperoni", "qty": 27 }
]
}
// bind data and chart loading before building the my chart
$q.all({ data: dataPromise, api: googleChartApiPromise })
.then(apiLoadSuccess);
};
function apiLoadSuccess(result) {
$scope.chartMe.type = 'BarChart';
//create a new DataTable loaded with data from the HTTP response
$scope.chartMe.data = new google.visualization.DataTable();
$scope.chartMe.data.addColumn('string', 'Item Name/Units');
$scope.chartMe.data.addColumn('number', 'Qty');
// create an array to hold index of items
var aNoQty = [];
var aQty = [];
var aRows = [];
for (var i = 0; i < result.data.data.length; i++) {
var oData = [];
aRows.push(i);
oData[0] = result.data.data[i].itemname;
oData[1] = result.data.data[i].qty;
// which items quanity exist
if (result.data.data[i].qty > 0) {
aQty.push(i);
} else {
aNoQty.push(i);
};
// now add the row
$scope.chartMe.data.addRow(oData);
};
$scope.aNoQty = aNoQty;
$scope.aQty = aQty;
$scope.chartMe.options = {
title: "Item(s) Distributed",
isStacked: false,
displayExactValues: true,
};
};
this.ToggleZeroDistributionOff = function () {
$scope.chartMe.view = new google.visualization.DataView($scope.chartMe.data);
$scope.chartMe.view.hideRows($scope.aNoQty)
// this seems like the wrong way to attach to existing chart...
// i'm referring to using document.getElementById() - not very Angular !
// but how else to expose the draw() method ??
var myChart = new google.visualization.BarChart(document.getElementById('myBarChart'));
// now draw() method is expoised
myChart.draw($scope.chartMe.view.toDataTable(), $scope.chartMe.options)
}
}]);
Thanks in advance for any suggestions.
Thank you WhiteHat for your suggestion. It worked both ways but in my case found your first answer to be easier to work with.
I posted a complete solution below:
'use strict';
app.controller('ChartsController', ['$scope', '$http', '$q',
'googleChartApiPromise', function ($scope, $http, $q, googleChartApiPromise) {
this.name = "ChartsController";
this.$inject = ['$scope', '$q', '$http', 'googleChartApiPromise'];
$scope.chartMe = {};
this.init = function () {
// simulated $http callback data returned in promise
var dataPromise = {
"data": [
{"itemname": "Mushrooms", "qty": 13 },
{"itemname":"Onions", "qty": 11},
{"itemname":"Olives", "qty": 0},
{"itemname":"Zucchini", "qty": 1},
{"itemname": "Pepperoni", "qty": 27 }
]
}
// bind data and chart loading before building the my chart
$q.all({ data: dataPromise, api: googleChartApiPromise })
.then(apiLoadSuccess);
};
function apiLoadSuccess(result) {
$scope.chartMe.type = 'BarChart';
//create a new DataTable loaded with data from the HTTP response
$scope.chartMe.data = new google.visualization.DataTable();
$scope.chartMe.data.addColumn('string', 'Item Name/Units');
$scope.chartMe.data.addColumn('number', 'Qty');
// create an array to hold index of items
var aNoQty = [];
var aQty = [];
var aRows = [];
for (var i = 0; i < result.data.data.length; i++) {
var oData = [];
aRows.push(i);
oData[0] = result.data.data[i].itemname;
oData[1] = result.data.data[i].qty;
// which items quanity exist
if (result.data.data[i].qty > 0) {
aQty.push(i);
} else {
aNoQty.push(i);
};
// now add the row
$scope.chartMe.data.addRow(oData);
};
$scope.aNoQty = aNoQty;
$scope.aQty = aQty;
$scope.chartMe.options = {
title: "Item(s) Distributed",
isStacked: false,
displayExactValues: true,
};
// chart view used later
$scope.chartMe.view = new google.visualization.DataView($scope.chartMe.data);
// grab a reference to the chart
$scope.chartMe.myChart = new google.visualization.BarChart(document.getElementById('myBarChart'));
};
this.ToggleZeroDistributionOff = function () {
// $scope.chartMe.view = new google.visualization.DataView($scope.chartMe.data);
$scope.chartMe.view.hideRows($scope.aNoQty)
// draw() method exists so refresh with new view
$scope.chartMe.myChart.draw($scope.chartMe.view.toDataTable(), $scope.chartMe.options)
}
}]);
This worked for me and opened lots of new options.
Thanks! Another way to hide rows as a response to an input in the view, assuming that you don't have that much data, is to use ng-change and set
the value of the cells in your row(s)/column(s) to = null. You'll have to find all the cells that you want to set to null because you can't simply set the
whole row or column to null. One by one. The advantage is that you can stick to the simple way of using ng-google-charts. Again this might make things easier only for small charts. You can also write a function which does a push() to the table an put in the ng-change of your input to do insert data from the view. If you ng-change affects directly the DataTable, make a variable that stores the original values, that way you can hide and then restore columns or rows.
https://www.w3schools.com/angular/ng_ng-change.asp

Saving data in LocalStorage in Ionic Application

I have an ionic app in which I have 7 input fields. what I am doing is I am saving those values from input fields into localstorage. below is my code in angularjs controller :
window.localStorage.setItem('Frames', JSON.stringify(PositionFrames));
$scope.Save = function () {
var FrameValues = {
"PositionName": $scope.data.PositionName,
"box1": 'j0' + $scope.data.box1,
"box2": 'j1' + $scope.data.box2,
"box3": 'j2' + $scope.data.box3,
"box4": 'j3' + $scope.data.box4,
"box5": 'j4' + $scope.data.box5,
"box6": 'gr' + $scope.data.box6
};
var temp = [];
tmp = JSON.parse(window.localStorage.getItem('Frames'));
temp.push(tmp);
temp.push(FrameValues);
window.localStorage.setItem('Frames', JSON.stringify(temp));
// window.localStorage['Frames'] = JSON.stringify(temp)
console.log(JSON.stringify(temp));
};
Whenever I hit button to save data and print data in console.log I can see following results :
[[[{"PositionName":"Home","box1":90,"box2":90,"box3":90,"box4":90,"box5":90,"box6":90}],{"PositionName":"Amrit","box1":"j058","box2":"j161","box3":"j2143","box4":"j3152","box5":"j4157","box6":"gr159"}],{"PositionName":"Amrit","box1":"j058","box2":"j161","box3":"j2143","box4":"j3152","box5":"j4157","box6":"gr159"}]
However if you see data in not in order, I dont know why it is not in format like this [{},{},{}]. I cannot figure out where these additional [[[ are coming from.
How do I solve it ?
Thanks Fellas :)
Thanks
following solved this problem
for (i = 0; i < tmp.length; i++) { temp.push(tmp[i]) }
hope this will help someone

How to merge REST call results in Angular app more efficiently

I have an Angular SPA running on a SharePoint 2013 page. In the code, I'm using $q to pull data from 10 different SharePoint lists using REST and then merging them into one JSON object for use in a grid. The code runs and outputs the intended merged data but it's leaky and crashes the browser after a while.
Here's the code in the service:
factory.getGridInfo = function() {
var deferred = $q.defer();
var list_1a = CRUDFactory.getListItems("ListA", "column1,column2,column3");
var list_1b = CRUDFactory.getListItems("ListB", "column1,column2,column3");
var list_2a = CRUDFactory.getListItems("ListC", "column4");
var list_2b = CRUDFactory.getListItems("ListD", "column4");
var list_3a = CRUDFactory.getListItems("ListE", "column5");
var list_3b = CRUDFactory.getListItems("ListF", "column5");
var list_4a = CRUDFactory.getListItems("ListG", "column6");
var list_4b = CRUDFactory.getListItems("ListH", "column6");
var list_5a = CRUDFactory.getListItems("ListI", "column7");
var list_5b = CRUDFactory.getListItems("ListJ", "column7");
$q.all([list_1a, list_1b, list_2a, list_2b, list_3a, list_3b, list_4a, list_4b, list_5a, list_5b])
.then(function(results){
var results_1a = results[0].data.d.results;
var results_1b = results[1].data.d.results;
var results_2a = results[2].data.d.results;
var results_2b = results[3].data.d.results;
var results_3a = results[4].data.d.results;
var results_3b = results[5].data.d.results;
var results_4a = results[6].data.d.results;
var results_4b = results[7].data.d.results;
var results_5a = results[8].data.d.results;
var results_5b = results[9].data.d.results;
var combined_1 = results_1a.concat(results_1b);
var combined_2 = results_2a.concat(results_2b);
var combined_3 = results_3a.concat(results_3b);
var combined_4 = results_4a.concat(results_4b);
var combined_5 = results_5a.concat(results_5b);
for(var i = 0; i < combined_1.length; i++){
var currObj = combined_1[i];
currObj["column4"] = combined_2[i].column4;
currObj["column5"] = combined_3[i].column5;
currObj["column6"] = combined_4[i].column6;
currObj["column7"] = combined_5[i].column7;
factory.newObjectArray[i] = currObj;
}
deferred.resolve(factory.newObjectArray);
},
function (error) {
deferred.reject(error);
});
return deferred.promise;
};
Here's the REST call in CRUDFactory:
factory.getListItems = function (listName, columns){
var webUrl = _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getByTitle('"+listName+"')/items?$select="+columns+"&$top=5000";
var options = {
headers: { "Accept": "application/json; odata=verbose" },
method: 'GET',
url: webUrl
};
return $http(options);
};
And then here's the controller bit:
$scope.refreshGridData = function(){
$scope.hideLoadingGif = false;
$scope.GridData = "";
GlobalFactory.getGridInfo()
.then(function(){
$scope.GridData = GlobalFactory.newObjectArray;
$scope.hideLoadingGif = true;
});
};
UPDATE 1: Per request, Here's the HTML (just a simple div that we're using angular-ui-grid on)
<div ui-grid="GridOptions" class="grid" ui-grid-selection ui-grid-exporter ui-grid-save-state></div>
This code starts by declaring some get calls and then uses $q.all to iterate over the calls and get the data. It then stores the results and merges them down to 5 total arrays. Then, because my list structure is proper and static, I'm able to iterate over one of the merged arrays and pull data from the other arrays into one master array that I'm assigning to factory.newObjectArray, which I'm declaring as a global in my service and using as my grid data source.
The code runs and doesn't kick any errors up but the issue is with (I believe) the "getGridInfo" function. If I don't comment out any of the REST calls, the browser uses 45 MB of data that doesn't get picked up by GC which is then compounded for each click until the session is ended or crashes. If I comment out all the calls but one, my page only uses 18.4 MB of memory, which is high but I can live with it.
So what's the deal? Do I need to destroy something somewhere? If so, what and how? Or does this relate back to the REST function I'm using?
UPDATE 2: The return result that the grid is using (the factory.newObjectArray) contains a total of 5,450 items and each item has about 80 properties after the merge. The code above is simplified and shows the pulling of a couple columns per list, but in actuality, I'm pulling 5-10 columns per list.
At the end of the day you are dealing with a lot of data, so memory problems are potentially always going to be an issue and you should probably consider whether you need to have all the data in memory.
The main goal you should probably be trying to achieve is limiting duplication of arrays, and trying to keep the memory footprint as low as possible, and freeing memory as fast as possible when you're done processing.
Please consider the following. You mention the actual number of columns being returned are more than your example so I have taken that into account.
factory.getGridInfo = function () {
var deferred = $q.defer(),
// list definitions
lists = [
{ name: 'ListA', columns: ['column1', 'column2', 'column3'] },
{ name: 'ListB', columns: ['column1', 'column2', 'column3'], combineWith: 'ListA' },
{ name: 'ListC', columns: ['column4'] },
{ name: 'ListD', columns: ['column4'], combineWith: 'ListC' },
{ name: 'ListE', columns: ['column5'] },
{ name: 'ListF', columns: ['column5'], combineWith: 'ListE' },
{ name: 'ListG', columns: ['column6'] },
{ name: 'ListH', columns: ['column6'], combineWith: 'ListG' },
{ name: 'ListI', columns: ['column7'] },
{ name: 'ListJ', columns: ['column7'], combineWith: 'ListI' },
],
// Combines two arrays without creating a new array, mindful of lenth limitations
combineArrays = function (a, b) {
var len = b.length;
for (var i = 0; i < len; i = i + 5000) {
a.unshift.apply(a, b.slice(i, i + 5000));
}
};
$q.all(lists.map(function (list) { return CRUDFactory.getListItems(list.name, list.columns.join()); }))
.then(function (results) {
var listResultMap = {}, var baseList = 'ListA';
// map our results to our list names
for(var i = 0; i < results.length; i++) {
listResultMap[lists[i].name] = results[i].data.d.results;
}
// loop around our lists
for(var i = 0; i < lists.length; i++) {
var listName = lists[i].name, combineWith = lists[i].combineWith;
if(combineWith) {
combineArrays(listResultMap[combineWith], listResultMap[listName]);
delete listResultMap[listName];
}
}
// build result
factory.newObjectArray = listResultMap[baseList].map(function(item) {
for(var i = 0; i < lists.length; i++) {
if(list.name !== baseList) {
for(var c = 0; c < lists[i].columns.length; c++) {
var columnName = lists[i].columns[c];
item[columnName] = listResultMap[list.name][columnName];
}
}
}
return item;
});
// clean up our remaining results
for (var i = 0; i < results.length; i++) {
delete results[i].data.d.results;
delete results[i];
}
deferred.resolve(factory.newObjectArray);
},
function (error) {
deferred.reject(error);
});
return deferred.promise;
};
I would suggest to add some sort of paging option... It's perhaps not a great idea to add all results to one big list.
Next i would suggest against ng-repeat or add a "track by" to the repeat function.
Check out: http://www.alexkras.com/11-tips-to-improve-angularjs-performance/
Fiddler your queries, the issue is probably rendering all the elements in the dom... Which could be kinda slow ( investigate)

Converting string to array using filter and using it in ng-repeat

I have a string which is in "1200:2,1300:3,1400:2" format. I need this to be printed like
<p>1200</p><p>2</p>
<p>1300</p><p>3</p>
<p>1400</p><p>2</p>
I tried using filter,
return function (input) {
//Validate the input
if (!input) {
return '';
}
var hoaArray = [];
var inputArray = input.split(',');
for (var i = 0; i < inputArray.length; i++) {
var adminTimeArray = inputArray[i].split(':');
hoaArray.push({ 'adminTime': adminTimeArray[0], 'dose': adminTimeArray[1]?adminTimeArray[1]:'' });
}
return hoaArray;
};
and inside html like
<p ng-repeat="timing in timing_list | formatter">{{timing.}}</p>{{timing .adminTime}}</div>
I am getting the following error,
Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [[{"msg":"fn: regularInterceptedExpression","newVal":36,"oldVal":34}],[{"msg":"fn: regularInterceptedExpression","newVal":38,"oldVal":36}],[{"msg":"fn: regularInterceptedExpression","newVal":40,"oldVal":38}],[{"msg":"fn: regularInterceptedExpression","newVal":42,"oldVal":40}],[{"msg":"fn: regularInterceptedExpression","newVal":44,"oldVal":42}]]
Could anyone please help me understand what am I doing wrong?
Regards,
Raaj
In the IndexController.js file:
var inputString = "1200:2,1330:3,1400:4,1500:3";
var formatInputString = function (input) {
//Validate the input
if (!input) {
return '';
}
var hoaArray = [];
var inputArray = input.split(',');
for (var i = 0; i < inputArray.length; i++) {
var adminTimeArray = inputArray[i].split(':');
hoaArray.push({ 'adminTime': adminTimeArray[0], 'dose': adminTimeArray[1] ? adminTimeArray[1] : '' });
}
return hoaArray;
};
$scope.inputString = inputString;
$scope.formattedString = formatInputString(inputString);
In the HTML file:
<div ng-repeat="timing in formattedString" >
{{timing.adminTime}}
{{timing.dose}}
</div>
The issue here - possibly a limitation or a bug in Angular - is that your filter creates new array objects every time it runs. ng-repeat uses under the covers $scope.$watchCollection to watch for the expression "timing_list | formatter" - this watcher always trips up because, in trying to detect a change in a values in the collection, it compares objects with a simple "!==" - and the objects are always new and different objects.
In short, this is another way to reproduce:
$scope.items = [1, 2, 3];
$scope.$watchCollection("items | foo", function(){
});
where foo is a filter that operates on each element in the array creating a new object:
.filter("foo", function(){
return function(inputArray){
return inputArray.map(function(item){
return {a: item};
});
};
});
So, to answer your question - you cannot with Angular v1.3.15 use a filter that returns an array with objects (without some funky object caching) with $watchCollection, and by extension, with ng-repeat.
The best way is to create the array first (with ng-init or in the controller), and then use it in the ng-repeat.

Resources