Reputation: 300
I am new to AngularJS.
to achieve a data-grid with a filter form I have created two directives in my main view.
myFilterForm - is a filter form for the data in the grid with the functions:
in order to communicate the filter changes to the grid (and to other directives that were removed for simplification) these methods broadcast a 'filterChanged' event with the filter object:
myApp.directive('myFilterForm', [function () {
return {
restrict: 'A',
templateUrl: 'partials/filter-form.html',
scope: true, // use own scope
link: function (scope, elem, attrs) {
// Define a filter object for URL parameters
scope.formObject = {};
// Clear the filter:
scope.resetFilter = function(){
scope.formObject = angular.copy(scope.defaultFilter);
scope.$parent.filterOn = false;
scope.$parent.$broadcast('filterChanged', scope.formObject);
scope.filterReset = true;
}
// Filter the data:
scope.filter = function(){
if(scope.filterForm.$dirty){
scope.$parent.filterOn = true;
}
scope.filterForm.$setPristine(); // disable the filter button until some value changes (prevent multiple clicks)
scope.$parent.$broadcast('filterChanged', scope.formObject);
scope.filterReset = angular.equals(scope.formObject, scope.defaultFilter);
console.log("filterReset="+scope.filterReset);
}
},
controller: function($scope, $timeout){
//TODO: HACK!!!
var timer = $timeout(
function() {
console.log( "Timeout executed", Date.now() );
$scope.formObject = angular.copy($scope.defaultFilter, $scope.formObject);
$scope.filterReset = true;
console.log(">>>>>>>LOADING>>>>>>"+angular.toJson($scope.formObject));
$scope.$parent.$broadcast('filterChanged', $scope.formObject);
},
100
);
}
}}]);
the (simplified) filter-form.html template:
<div class="well">
<form novalidate name="filterForm" novalidate class="form-horizontal" style="margin-bottom: 0px">
<input type="text" ng-model="formObject.firstName" class="form-control input-sm">
</form>
<button ng-click="resetFilter()" class="btn btn-danger"><i class="fa fa-times fa-fw"></i> Reset</button>
<button ng-click="filter()" class="btn btn-success"><i class="fa fa-check fa-fw"></i> Filter</button>
</div>
myDataGrid - is a wrapper around ng-grid that uses server-side sorting and paging
in myDataGrid I handle the 'filterChanged' event by calling the $http to reload the grid's (filtered) data.
myApp.directive('myDataGrid', ['serverProxy', '$log', function (serverProxy, $log ) {
return {
restrict: 'A',
replace: false,
template: '<div cg-busy="\'gridTracker\'" class="gridStyle" ng-grid="gridOptions"></div>',
scope: true, //ng-grid wrapper should use it own (child) scope (see: https://github.com/angular-ui/ng-grid/issues/206)
controller: 'gridCtrl',
link: function (scope, elem, attrs) {
if(attrs.apiUrl){
scope.apiUrl = attrs.apiUrl;
}
// holds the item count in the DB with the current filter applied
scope.totalServerItems = 0;
// holds the filter form model
scope.formObject = {};
// holds the sorting and paging settings
scope.paramObject = { limit: scope.pagingOptions.pageSize, page: scope.pagingOptions.currentPage };
// Watch for changes in ng-grid's currentPage
scope.$watch('pagingOptions.currentPage', function (newVal, oldVal) {
if (newVal !== oldVal) {
scope.paramObject.page = newVal;
scope.loadData();
}
}, true);
// Watch for changes in ng-grid's pageSize
scope.$watch('pagingOptions.pageSize', function (newVal, oldVal) {
if (newVal !== oldVal) {
scope.pagingOptions.currentPage = 1;// if pageSize changes - start from first page
scope.paramObject.limit = newVal;
scope.loadData();
}
}, true);
scope.$watch('sortOptions', function (newVal, oldVal) {
if (newVal !== oldVal) {
$log.debug("sortOptions="+angular.toJson(scope.sortOptions.fields));
scope.paramObject.sort = newVal.fields[0];
scope.paramObject.direction = newVal.directions[0];
scope.loadData();
}
}, true);
scope.$on('filterChanged', function(event, filterObj) {
$log.debug('myDataGrid: caught filterChanged event! - new val='+angular.toJson(filterObj));
scope.pagingOptions.currentPage = 1;// if filter changes - start from first page
scope.loadData(filterObj);
});
// Update the paging data for the grid:
scope.setPagingData = function(data, page, pageSize){
scope.myData = data.content;
scope.totalServerItems = data.totalElements;
if (!scope.$$phase) {
scope.$apply();
}
};
scope.loadData = function (formObject) {
if(formObject) { scope.formObject = formObject; }
serverProxy.getData(scope.apiUrl, scope.paramObject, scope.formObject, 'gridTracker', function(data){
scope.setPagingData(data, scope.pagingOptions.currentPage, scope.pagingOptions.pageSizes);
});
};
}
}}]);
the controller for the grid has some ng-grid default configuration:
myApp.controller('gridCtrl', ['$scope', function($scope) {
$scope.pagingOptions = {
pageSizes: [10, 25, 50, 198],
pageSize: 10,
currentPage: 1
};
$scope.sortOptions = {
columns: [],
fields: [],
directions: []
};
$scope.gridOptions = {
data: 'myData',
columnDefs: $scope.gridColumnDefs, // should be defined in the view's controller
enablePaging: true,
showFooter: true,
totalServerItems: 'totalServerItems',
pagingOptions: $scope.pagingOptions,
sortInfo: $scope.sortOptions,//,
useExternalSorting: true,
i18n: "en"
};
}]);
and finally, the main view had both directives:
<div my-filter-form></div>
<div my-data-grid></div>
and is using a controller where I set the specific configuration for the page:
the default filter
myApp.controller('ViewCtrl', ['$scope', function($scope) {
// server URL to get the grid data:
$scope.apiUrl = '/api/users';
$scope.gridColumnDefs = [
{field: 'firstName', displayName:'First Name', resizable: true, sortable: true},
{field: 'lastName', displayName:'Last Name', resizable: true, sortable: true},
{field: 'roles', displayName:'Roles', resizable: true, sortable: false, cellFilter: 'listProperty:\'name\''},
{field: 'active', displayName:'Active', resizable: true, sortable: false, cellFilter: 'yesNoBoolean'},
{field: 'updateDate', displayName:'Update Date', resizable: true, sortable: true, cellFilter: 'date:\'dd MMM yyyy - hh:mm:ss a\''}
];
// default filter values
$scope.defaultFilter = {
"firstName": "jimmy"
};
}]);
I would like the initial load of the page to load the grid and the form with default filter values applied.
my current solution is to set a short $timeout in the controller of myFilterForm directive which sets the default filter and fires the 'filterChanged' event. this works but it feels like a hack.
could someone suggest the proper way to achieve this.
Thanks.
Upvotes: 1
Views: 989
Reputation: 300
found a better solution that works and does not involve $timeout. (thanks m59)
in the 'myFilterForm' I removed the controller function and added the following watch in the link function:
scope.$watch('defaultFilter', function (newVal, oldVal) {
scope.resetFilter();
}, true);
the defaultFilter does not actually change - it is used to reset the filter back to the defaults. but angular calls the watch on the initial load which is what I need to reset the form and load the data
Upvotes: 1