I use AngularJS 1.7 and ag-grid 18.0.1.
When I set angularCompileRows to true, I have this error in the console:
Uncaught TypeError: Cannot read property '$apply' of null
at eval (rowRenderer.js:586)
This is the code corresponding (this is agGrid code):
RowRenderer.prototype.checkAngularCompile = function () {
var _this = this;
// if we are doing angular compiling, then do digest the scope here
if (this.gridOptionsWrapper.isAngularCompileRows()) {
// we do it in a timeout, in case we are already in an apply
setTimeout(function () {
_this.$scope.$apply();
}, 0);
}
};
Here my options:
const gridOptions = {
rowData: null,
columnDefs: [...],
enableColResize: true,
onColumnResized: (params) => {
angularCompileRows: true,
suppressCellSelection: true,
enableSorting: true,
enableServerSideSorting: true,
rowModelType: 'infinite',
pagination: true,
paginationPageSize: 10,
unSortIcon: true,
suppressPaginationPanel: true,
suppressHorizontalScroll: true,
onGridReady: (params) => {
const dataSource = {
rowCount: null, // behave as infinite scroll
getRows: (rowsParams) => {
// call my API then rowsParams.successCallback(data, isLastPage);
},
};
gridOptions.api.setDatasource(dataSource);
},
};
The call stack
And the output of the debug option
The grid is rendering: headers are okay, but the content is empty.
Plunker https://plnkr.co/edit/j5ubZWit4CO5zmldgqnl?p=preview based on this exemple https://www.ag-grid.com/javascript-grid-infinite-scrolling/#example-2-equal-pagination-page-size-and-large-infinite-block-si and add angularCompileRows: true to options
EDIT: Here a plunker with an angular app https://plnkr.co/edit/7eOKSXbcCzLdYWtPygrS?p=preview
The error is not fired but the angularCompileRows do not seem to be called
If you're going to use angular with the grid, why not load angular first :). You're not loading angular and you are not using any angular code in the grid, how is it suppose to call $scope.$apply() without that?
Make angularCompileRows = false and your grid works. If you're going to use angular, start with an angular example.
Related
I am looping through each node of ag-grid data and have to display column values.Attached is the plunkr:
https://plnkr.co/edit/cFBLm7DkAZL5oWbqdyub?p=preview.
I am not able to display the ag-grid data in the console.
var gridOptions = {
defaultColDef: {
sortable: true
},
columnDefs: columnDefs,
animateRows: true,
enableRangeSelection: true,
rowData: rowData,
checkbox : true,
onSelectionChanged : getData
};
new agGrid.Grid(gridDiv, gridOptions);
});
function getData(){
gridOptions.api.forEachNode( function (node) {
console.log("node vaalues are:"+node.data);
});
There are a couple things wrong with your code.
Inorder for onSelectChanged to be called you need to specify how you want row selection to work, try: rowSelection: 'single'
In your plunkr (not your snippet above), your getData function is in the wrong scope. By this I mean all of your code is inside the document.addEventListener, but the getData is not. Because of this, getData doesn't know what gridOptions is.
Updated plunkr: https://next.plnkr.co/edit/cXKVZrT9siHoodVa
I need dynamically load options of a field. Before the options loaded, if user click the select dropdown, they will see a empty list. To prevent that, I want to disable the select when page load, and then enable it after the options load.
The options loads fine, but the reset the disabled doesn't work
Code like
{
type: 'select', key: 'ref_code', defaultValue: '', templateOptions: {
'required': false, 'label': 'Supporter',
'placeholder': 'loading...', 'options': [], 'disabled': true
},
controller: function ($scope) {
$scope.to.loading = parentScope.ensure_option('resellers').then(function (items) {
$scope.to.options = options.get('resellers');
$scope.to.disabled = false;
return $scope.to.options;
});
}
},
Any idea?
You are loading it asynchronously but not waiting for the response to come back.
$scope.to.options = return options.get('resellers').then(function(response) {
$scope.to.disabled = false;
return response;
});
I have the following code Angular JS:
appService.get({id : id}).then(function (response) {
$scope.vm.events.push({title: 'New event', type: 'important', draggable: true, resizable: true});
})
This code returns response from AJAX service and puts object to array $scope.vm.events.
So, in template I dont see this added element as: {{vm.events}}
There is also one function in the same controller:
$scope.add = function (){
$scope.vm.events.push({title: 'New event', type: 'important', draggable: true, resizable: true});
}
When I call it I see new element in template: {{vm.events}}.
Why does not work code in the first case?
This is because the callback function in your service is outside the angularjs digest cycle.
For tackling this, there are two ways:
Method 1:
The first way would be to use $scope.$apply just after your callback in the service has finished as follows:
appService.get({id : id}).then(function (response) {
$scope.vm.events.push({title: 'New event', type: 'important', draggable: true, resizable: true});
$scope.$apply(); //this for updating your scope outside the digest cycle
})
Method 2:
Wrapping your service code inside a function inside the controller's scope as follows:
$scope.fn = function() {
appService.get({id : id}).then(function (response) {
$scope.vm.events.push({title: 'New event', type: 'important', draggable: true, resizable: true});
})
}
So, in this method, whenever you want to call the service just call this function. This is also the reason why your 'add' function updates the template because it is in the 'scope' of the controller and in the digest cycle.
If you are not seeing it, it is because the promise is not being resolved.
Do try putting something in the rejection handling part of the promise (on a second function within the 'then()' block):
appService.get({id : id}).then(function (response) {
$scope.vm.events.push({title: 'New event', type: 'important', draggable: true, resizable: true});
},
function(){
$scope.vm.events.push({title: 'ERROR_EVENT', type: 'ERROR_CALLBACK', draggable: true, resizable: true});
});
Like others suggested, if this is outside of angular, please call for $scope.$apply() or $scope.$digest() to trigger a digest cycle so that your data can be updated.
By adding this second function you should be able to see if the promise was being resolved or not, and if the problem lies elsewhere.
Based upon a prior SO article on injecting toastr into your app/controller. I have setup my app.js as follows:
(function () {
app = angular.module("app", ['breeze.angular']).value('ngToastr', toastr);
//added toaster as factory so it can be injected into any controller
angular.module('app').factory('ngNotifier', function (ngToastr) {
return {
notify: function (msg) {
ngToastr.success(msg);
},
notifyError: function (msg) {
ngToastr.error(msg);
},
notifyInfo: function (msg) {
ngToastr.info(msg);
}
}
});
})();
as one of the answers stated I now have access to the toastr control from any controller.
app.controller('reportController', function ($scope, reportLibraryService, ngNotifier, $log) {
//report section
var rvm = this;
rvm.getReportList = GetReportList;
rvm.onError = OnError;
rvm.onReportComplete = OnReportComplete;
$scope.userId = 1;
GetReportList($scope.userId);
function OnReportComplete(response) {
$scope.reportList = response;
ngNotifier.notify("Reports Loaded");
};
function OnError(reason) {
$scope.error = "Could not fetch the data.";
$log.error(reason);
};
function GetReportList(userId) {
$log.info("Getting reports for userid " + userId)
reportLibraryService.getAllReports($scope.userId).then(rvm.onReportComplete, rvm.onError);
};
});
The question I have is how do I override the default options? I have tried two approaches so far. First adding an toastr div within the html with the options set, which did not work. And then I tried adding them within the factory but they were ignored there as well.
angular.module('app').factory('ngNotifier', function (ngToastr) {
return {
notify: function (msg) {
ngToastr.success(msg);
ngToastr.options = {
"closeButton": false,
"debug": false,
"progressBar": false,
"positionClass": "toast-bottom-right",
"onclick": null,
"showDuration": "300",
"hideDuration": "1000",
"timeOut": "5000",
"extendedTimeOut": "1000",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut"
}
}, ...
As a second part to this is toastr the correct tool to use or should I be using angular-toaster instead since this is an angular app? I currently do not have any jQuery dependencies anywhere else in my application.
thanks for any suggestions
For those trying to override a particular notification, rather than simply override the defaults globally, I was able to do so but with a catch. I wanted to make errors persist (set timeOut to 0) while success messages fade. So for a normal happy-path notification I just use:
toaster.success('Nothing to see here folks', 'Move along');
But for errors I want the message to persist so they can show their manager, write down the error message, whatever. This is easy with the original toastr project, you just pass a JSON object of override options as your last argument such as:
toastr.error('Original toastr example', 'Click to dismiss', {timeOut: 0});
Angularjs-toaster is different, you pass your params to the pop function.
But this did NOT work:
toaster.pop({
type: 'error',
title: 'Need More Information',
body: 'Error 42 occurred, run for the hills!',
timeOut: 0
});
I looked in the toaster.js code (I am using version 0.4.15) and it looks like you can pass ordered parameters to pop instead of a JSON object. This DID work:
toaster.pop(
'error',
'Need More Information',
'Error 42 occurred, run for the hills!',
0 );
Of course I'd prefer to pass an object with named params over a bunch of unlabeled params. I looked at their code closer and saw they changed the case sensitivity from timeOut to timeout!!! This works:
toaster.pop({
type: 'error',
title: 'Need More Information',
body: 'Error 42 occurred, run for the hills!',
timeout: 0
});
Just put toastr options in app.config
app.config(["toastrConfig",function(toastrConfig) {
var options = {
"closeButton": true,
"debug": false,
"newestOnTop": false,
"progressBar": true,
"positionClass": "toast-top-right",
"preventDuplicates": false,
"onclick": null,
"showDuration": "300",
"hideDuration": "1000",
"timeOut": "5000",
"extendedTimeOut": "1000",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut"
};
angular.extend(toastrConfig, options);
})]);
Since "AngularJS Toaster" is an AngularJS port of the "toastr" jQuery library, it is definitely better to use it in your AngularJS application.
I use it in similar manner, and didn't have problems setting options in HTML template like this:
<toaster-container toaster-options="{'position-class': 'toast-bottom-right', 'close-button': true, 'body-output-type': 'trustedHtml', 'showDuration': '400', 'hideDuration': '200',}"></toaster-container>
If you have a base angular controller, you can add your site-wide angular.options there. Then, if necessary, you can override the options you want in the toastung controller.
toastr.options = {
"closeButton": true,
"debug": false,
"newestOnTop": false,
"progressBar": true,
"positionClass": "toast-top-right",
"preventDuplicates": false,
"onclick": null,
"showDuration": "300",
"hideDuration": "1000",
"timeOut": "5000",
"extendedTimeOut": "1000",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut"
};
I have created MVC 4.0 app using Web API which returns data in JSON format (I am serializing object to json using NewtonSoft.Json) and trying to bind data in ng-Grid. I am receiving data in following format:
"[{\"Name\":\"FIRST_NAME\",\"Value\":\"FIRST_NAME\"},{\"Name\":\"CURRENT_DATE_TIME\",\"Value\":\"CURRENT_DATE_TIME\"},{\"Name\":\"CLIENTID\",\"Value\":\"CLIENTID\"},{\"Name\":\"CALLMODE\",\"Value\":\"CALLMODE\"}, {\"Name\":\"new 321\",\"Value\":null}]"
When i tried to assign same to data: of ng-Grid, each char populated on different row. following is the javascript i have written:
var guidesRespApp = angular.module('guidesRespApp', ['ngGrid']);
//Get Data from restful API.
guidesRespApp.controller('MyCtrl', function ($scope, $http) {
$http.get('/api/datadictionary').success(function (thisdata) {
$scope.myData = thisdata;
});
$scope.filterOptions = {
filterText: '',
useExternalFilter: true,
};
//Setting grid options
$scope.gridOptions = {
data: 'myData',
multiSelect: true,
filterOptions: { filterText: '', useExternalFilter: false },
enableRowReordering: false,
showGroupPanel: false,
maintainColumnRatios: false,
groups: [],
showSelectionCheckbox: true,
showFooter: true,
enableColumnResize: true,
enableColumnReordering: true
};
// $scope.totalFilteredItemsLength = function() {
// //return self.filteredRows.length;
// };
});
If manually assigned like below, data is getting displayed in grid:
$scope.myData = [{"Name":"FIRST_NAME","Value":"FIRST_NAME"},{"Name":"CURRENT_DATE_TIME","Value":"CURRENT_DATE_TIME"},{"Name":"CLIENTID","Value":"CLIENTID"},{"Name":"CALLMODE","Value":"CALLMODE"}];
can anyone please help me to understand how to fix it?
Also i wanted to display count of filtered items when i key in values in filtertext.
As mentioned on http://angular-ui.github.io/ng-grid/ , Data being displayed in the grid is of type array and each element in that array mapped to a row being displayed. So I modified my code like below and it worked for me:
$http.get('http://localhost:12143/api/datadictionary').success(function (thisdata) {
//Convert data to array.
var myData = $.parseJSON(JSON.parse(thisdata));
$scope.myData = myData;
});
even var myData = $.parseJSON(angular.fromJson(thisdata)); also working. Just we need first to parse the data (for this I used JSON.parse()) and then convert to array (for this i used $.parseJSON()).
Try changing the get callback to one of the following:
$http.get('/api/datadictionary').success(function (thisdata) {
$scope.myData = JSON.parse(thisdata);
// or
$scope.myData = angular.fromJson(thisdata);
});
Reference this with regards to how webapi is returning json. ASP.NET WebAPI: How to control string content returned to client?