Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Controls in Angular break when configuration options come from service

I have a service that will return my some config options for an ng-grid. The getGridOptions function takes the name of the controller it's used on and returns the correct set of options (only one shown here for brevity).

service for ng-grid options:

angular.module('services').service('GridOptionsService',function(){

    var documents = {
        data: 'myData',
        enablePaging: true,
        showFooter:true,
        totalServerItems: 'totalServerItems',
        pagingOptions: {
            pageSizes: [50,100,200],
            pageSize: 50,
            currentPage: 1
        },
        filterOptions:  {
            filterText: '',
            useExternalFilter: false
        },
        enableCellEdit: false,
        enableColumnReordering: true,
        enablePinning: false,
        showGroupPanel: false,
        groupsCollapsedByDefault: true,
        enableColumnResize: true,
        showSelectionCheckbox: true,
        selectWithCheckboxOnly: true,
        columnDefs: [
            {field:'docId', displayName:'Document ID', cellTemplate: NgGridDomUtil.toLink('#/documents/{{row.getProperty(col.field)}}')},
            {field:'docTags', displayName:'Tags'},
            {field:'lastSaveDate', displayName:'Last saved'},
            {field:'documentVersion', displayName:'Version', width: 120},
            {field:'busDocId', displayName:'Customer Doc ID'},
            {field:'markedForDelete', displayName:'Deleted', width: 120, cellTemplate: NgGridDomUtil.toCheckbox('{{row.getProperty(col.field)}}')}]
    };

    var gridOptionManager = {
        documents: documents

    }
    return {
        getGridOptions: function(controllerName){
            return gridOptionManager[controllerName];
        }

    }
})

The NgGridDomUtil class just makes it easier to style things on the grid:

var NgGridDomUtil = (function(){
    var toLink = function(href){
    var html = '<div class="ngCellText" ng-class="col.colIndex()"><a ng-href= "'+href+'" class="ngCellLink"><span ng-cell-text>{{row.getProperty(col.field)}}</span></a></div>'
    return html;
}
var toCheckbox = function(_selected){
    var html = '<div class="ngCellText" ng-class="col.colIndex()"><input type="checkbox" ng-change="console.log('+"TEST"+')" ng-model="COL_FIELD"  ng-input="COL_FIELD"' + (_selected ? 'selected' : '') + ' /></div>'
    return html
}
return {
    toLink: toLink,
    toCheckbox: toCheckbox
}
})();

My problem is what when I use the GridOptionsService to retrieve the data, the data is still presented to the grid correctly, but the text filtering no longer works and the paging is broken. However, the selectedFilterOption still works.

controller:

angular.module('controllers').controller('Repository', ['$scope', 'DataContext','GridOptionsService','$http', function($scope, DataContext,GridOptionsService,$http) {
    $scope.filterOptions = {
        filterText: '',
        useExternalFilter: false
    };
    $scope.totalServerItems =0;
    $scope.pagingOptions ={
        pageSizes: [5,10,100],
        pageSize: 5,
        currentPage: 1
    }
    //filter!
    $scope.dropdownOptions = [{
        name: 'Show all'

    },{
        name: 'Show active'
    },{
        name: 'Show trash'
    }];
    //default choice for filtering is 'show active'
    $scope.selectedFilterOption = $scope.dropdownOptions[1];


    //three stage bool filter
    $scope.customFilter = function(data){
        var tempData = [];
        angular.forEach(data,function(item){
            if($scope.selectedFilterOption.name === 'Show all'){
                tempData.push(item);
            }
            else if($scope.selectedFilterOption.name ==='Show active' && !item.markedForDelete){
                tempData.push(item);
            }
            else if($scope.selectedFilterOption.name ==='Show trash' && item.markedForDelete){
                tempData.push(item);
            }
        });
        return tempData;
    }



    //grabbing data
    $scope.getPagedDataAsync = function(pageSize, page,  searchText){
        var data;
        if(searchText){
            var ft = searchText.toLowerCase();
            DataContext.getDocuments().success(function(largeLoad){
                //filter the data when searching
                data = $scope.customFilter(largeLoad).filter(function(item){
                    return JSON.stringify(item).toLowerCase().indexOf(ft) != -1;
                })
                $scope.setPagingData($scope.customFilter(data),page,pageSize);
            })
        }
        else{
            DataContext.getDocuments().success(function(largeLoad){
                var testLargeLoad = $scope.customFilter(largeLoad);
                //filter the data on initial page load when no search text has been entered
                $scope.setPagingData(testLargeLoad,page,pageSize);
            })
        }
    };
    //paging
    $scope.setPagingData = function(data, page, pageSize){
        var pagedData = data.slice((page -1) * pageSize, page * pageSize);
        //filter the data for paging
        $scope.myData = $scope.customFilter(pagedData);
        $scope.myData = pagedData;
        $scope.totalServerItems = data.length;
//        if(!$scope.$$phase){
//            $scope.$apply();
//        }
    }

    //watch for filter option change, set the data property of gridOptions to the newly filtered data
    $scope.$watch('selectedFilterOption',function(){
        var data = $scope.customFilter($scope.myData);
        $scope.myData = data;
        $scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage);
        $scope.setPagingData($scope.myData,$scope.pagingOptions.currentPage,$scope.pagingOptions.pageSize);
    })
    $scope.$watch('pagingOptions',function(newVal, oldVal){
        $scope.getPagedDataAsync($scope.pagingOptions.pageSize,$scope.pagingOptions.currentPage,$scope.filterOptions.filterText);
        $scope.setPagingData($scope.myData,$scope.pagingOptions.currentPage,$scope.pagingOptions.pageSize);
    },true)


    $scope.message ="This is a message";
    $scope.gridOptions = {
        data: 'myData',
        enablePaging: true,
        showFooter:true,
        totalServerItems: 'totalServerItems',
        pagingOptions: $scope.pagingOptions,
        filterOptions: $scope.filterOptions,
        enableCellEdit: true,
        enableColumnReordering: true,
        enablePinning: true,
        showGroupPanel: true,
        groupsCollapsedByDefault: true,
        enableColumnResize: true
    }
    $scope.gridOptions = GridOptionsService.getGridOptions('documents');
    //get the data on page load
    $scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage);
}]);

The grid options that I have hard coded into the controller are the same as the ones returned from the service. What I don't understand is why the grid renders, the dropdown filter works, but the paging is broken, only when the options for the grid come from a service? But it works as expected if it's hard coded into the controller.

EDIT: If someone can more eloquently state my problem, feel free to edit the title.

like image 641
wootscootinboogie Avatar asked Jun 09 '14 19:06

wootscootinboogie


People also ask

How does Angular determine change in form control?

In Angular we have observables which subscribe to form value changes. what you have to do is, subscribe to the form instance and detect any changes to the form. Suppose you don't want to include save button on your form. whenever user change any thing in the form save data to the server.

What is setValue and patchValue in Angular?

By Arvind Rai, January 17, 2021. This page will walk through Angular setValue and patchValue methods of FormGroup . The setValue method sets a new value to the form controls of this FormGroup . The patchValue patches the new value of this FormGroup in best possible way.

What is the purpose of FormBuilder?

The FormBuilder provides syntactic sugar that shortens creating instances of a FormControl , FormGroup , or FormArray . It reduces the amount of boilerplate needed to build complex forms.

What are form controls Angular?

What are form controls in Angular? In Angular, form controls are classes that can hold both the data values and the validation information of any form element. Every form input you have in a reactive form should be bound by a form control. These are the basic units that make up reactive forms.


3 Answers

I don't really know how the ngGrid is implemented, but a common pitfall I do know that exist in many directives, is they expect their configurations to be ready as soon as they're initialized. Meaning that instead of watching the configuration object, they assume it exists and use it directly in the link\controller functions which runs as soon as they're created.

If this is indeed the case, a quick workaround to the problem is initializing the directive only when you have the configuration object. Let's say you pass on the configuration object through the variable 'options' on your scope, you'll then write something like:

<!-- If options exists on your scope, it means you fetched it from the server -->
<div ng-if="options">
    <div ng-grid ng-grid-options="options"></div>
</div>

Again, I'm not familiar with ngGrid or its usage, this is just an educated guess, take the conclusions and apply them on the correct API.

like image 121
haimlit Avatar answered Sep 22 '22 09:09

haimlit


I haven't tested this, but one possible issue is that you are overwriting an object that is on the $scope. This can break the two way binding. For a quick test try

    $scope.grid = {
        Options: {
            data: 'myData',
            enablePaging: true,
            showFooter:true,
            totalServerItems: 'totalServerItems',
            pagingOptions: $scope.pagingOptions,
            filterOptions: $scope.filterOptions,
            enableCellEdit: true,
            enableColumnReordering: true,
            enablePinning: true,
            showGroupPanel: true,
            groupsCollapsedByDefault: true,
            enableColumnResize: true
        }
   }
   $scope.grid.Options = GridOptionsService.getGridOptions('documents');

You would need to update the grid options in the directives attribute as well of course.

like image 36
Ben Felda Avatar answered Sep 25 '22 09:09

Ben Felda


The problem is that the controller functions for filtering and paging use the options defined on the controller $scope, but the ng-grid UI is not bound to those objects.

The controller methods for filtering and paging use $scope.pagingOptions as the data source. However, the ng-grid UI is bound to $scope.gridOptions.pagingOptions. When you create the $scope.gridOptions explicitly in the controller, $scope.gridOptions.pagingOptions refers to the same object as $scope.gridOptions, so making changes in the UI will change the value used in the controller functions.

However, when $scope.gridOptions is assigned using the service, the service is creating new pagingOptions, so there is no connection between $scope.gridOptions.pagingOptions and $scope.pagingOptions. Making changes in the UI will not change the values used in the controller functions.

The same is true for filterOptions.

One way to resolve the issue is

$scope.gridOptions = GridOptionsService.getGridOptions('documents');
$scope.pagingOptions = $scope.gridOptions.pagingOptions
$scope.filterOptions = $scope.gridOptions.filterOptions
like image 41
starchild Avatar answered Sep 24 '22 09:09

starchild