Exploring AngularJS: Leveraging ng-model within a custom directive featuring iterations and dynamically generated HTML elements

Trying to implement a directive for a grid, I encountered an issue where passing in a column definition that includes an HTML control with ng-model and ng-click directives resulted in an error: "Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!" And the ng-model was not binding as expected. To see this problem in action, check out a simple Plunker example here: https://plnkr.co/edit/ue1lQqvrUE0M5zZXsg8a?p=preview.

// Code snippet


                var myApp = angular.module('myApp', ['ngSanitize']);

                myApp.controller('MyCtrl', function($scope) {

                    $scope.myGridConfig = {};
                    $scope.myGridConfig.columnDefs = [{
                        field: 'facility',
                        headerName: 'Facility'
                    }, {
                        field: 'code',
                        headerName: 'Code'
                    }, {
                        field: 'cost',
                        headerName: 'Cost'
                    }, {
                        field: 'conditionRating',
                        headerName: 'Conditional Rating'
                    }, {
                        field: 'extent',
                        headerName: 'Extent'
                    }, {
                        field: 'planYear',
                        headerName: 'Plan Year',
                        cellRenderer: function(row, de) {
                            return '<input type="checkbox" ng-model="row.selected" />';
                        }
                    }];

                    $scope.myGridConfig.rowCollection = [{
                        facility: "Atlanta",
                        code: "C-RD34",
                        cost: 540000,
                        conditionRating: 52,
                        extent: 100,
                        planYear: 2014
                    }, {
                        facility: "Seattle",
                        code: "CRDm-4",
                        cost: 23000,
                        conditionRating: 40,
                        extent: 88,
                        planYear: 2014
                    }, {
                        facility: "Austin",
                        code: "GR-5",
                        cost: 1200000,
                        conditionRating: 92,
                        extent: 90,
                        planYear: 2014
                    }];
                });

                myApp.directive('cellRender', function($compile) {
                    var directive = {};
                    directive.restrict = 'A';
                    directive.scope = {
                        cellRender: "="
                    };
                    directive.replace = true;
                    //directive.template = "<div>{{renderThis(cellRender.row, cellRender.def)}}</div>";
                    directive.template = "<div>{{renderThis()}}</div>";
                    directive.link = function(scope, ele, attrs) {


                        scope.renderThis = function() {
                            if (scope.cellRender.def.cellRenderer) {
                                ele.html(scope.cellRender.def.cellRenderer(scope.cellRender.row, scope.cellRender.def));
                                $compile(ele.contents())(scope);
                            }
                            return scope.cellRender.row[scope.cellRender.def.field];
                        };

                        /*scope.renderThis = function(r, d) {
if (d.cellRenderer)
{
ele.html(d.cellRenderer(r,d));
$compile(ele.contents())(scope);
}
return r[d.field];
};*/

                    };

                    return directive;
                });

                myApp.directive('grid', function($compile) {
                    var directive = {};
                    directive.restrict = 'EA';
                    directive.scope = {
                        gridConfig: "="
                    };
                    directive.template = "<table style='border: 1px solid black;'>\n" +
                        "<thead>\n" +
                        "<tr>\n" +
                        "<th ng-repeat=\"def in gridConfig.columnDefs\" >{{def.headerName}}</th>\n" +
                        "</tr>\n" +
                        "</thead>\n" +
                        "<tbody>\n" +
                        "<tr ng-repeat=\"row in gridConfig.rowCollection\">\n" +
                        "<td ng-repeat=\"def in gridConfig.columnDefs\" ><div cell-render=\"{'row': row, 'def' :def}\"></div></td>\n" +
                        "</tr>\n" +
                        "</tbody>\n" +
                        "</table>";

                    directive.link = function(scope, ele, attrs) {

                        angular.forEach(scope.gridConfig.rowCollection, function(rr) {
                            rr.selected = true;
                        });

                    };
                    return directive;
                });
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <title>Test dynamic ngModel</title>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.29/angular.js"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.29/angular-sanitize.js"></script>
  <script type="text/javascript" src="script.js"></script>
</head>

<body ng-app="myApp">
  <div ng-controller="MyCtrl">
    <div grid="" grid-config="myGridConfig"></div>
    <div>Row 2 selected: {{myGridConfig.rowCollection[1].selected}}</div>
  </div>
</body>

</html>

Answer â„–1

I have found a solution and created a plunker for it. Check it out here: http://plnkr.co/edit/6boouR50HiCh2BPuHEp1?p=preview

If you have any suggestions on how to improve or make this code cleaner, feel free to share.

var myApp = angular.module('myApp',['ngSanitize']);

myApp.controller('MyCtrl', function($scope) {
        $scope.myGridConfig = {};
        $scope.myGridConfig.columnDefs = [{field: 'facility', headerName: 'Facility'},
        {field: 'code', headerName: 'Code'},
        {field: 'cost', headerName: 'Cost'},
        {field: 'conditionRating', headerName: 'Conditional Rating'},
        {field: 'extent', headerName: 'Extent'},
        {field: 'planYear', headerName: 'Plan Year', cellTemplate: '<input type=\"checkbox\" ng-model=\"row.selected\" ng-click=\"clickMe()\"></input>'}];

        $scope.myGridConfig.rowCollection = [{
          facility: "Atlanta",
          code: "C-RD34",
          cost: 540000,
          conditionRating: 52,
          extent: 100,
          planYear: 2014
        }, {
          facility: "Seattle",
          code: "CRDm-4",
          cost: 23000,
          conditionRating: 40,
          extent: 88,
          planYear: 2014
        }, {
          facility: "Austin",
          code: "GR-5",
          cost: 1200000,
          conditionRating: 92,
          extent: 90,
          planYear: 2014
        }];

angular.forEach($scope.myGridConfig.rowCollection, function(rr) {
                        rr.selected = true;
                });
});

myApp.directive('customCellTemplate', function($compile, $sce, $parse) {
        var directive = {};
        directive.restrict = 'A';
        directive.scope = {
                def : "=",
row: "="
        };
        directive.replace = true;
        directive.link = function(scope, ele, attrs) {
ele.html(scope.def.cellTemplate);
$compile(ele.contents())(scope);


scope.clickMe = function() {
alert(scope.row.cost);
};
        };

        return directive;
});

myApp.directive('grid', function($compile, $sce, $parse) {
        var directive = {};
        directive.restrict = 'EA';
directive.replace=true;
        directive.scope = {
                gridConfig : "="
        }
        directive.template = "<table style='border: 2px solid black;'>\n" +
                "<thead>\n" +
                        "<tr>\n" +
                                "<th ng-repeat=\"def in gridConfig.columnDefs\" >{{def.headerName}}</th>\n" +
                        "</tr>\n" +
                "</thead>\n" +
                "<tbody>\n" +
                        "<tr ng-repeat=\"row in gridConfig.rowCollection\">\n" +
                                "<td ng-repeat=\"def in gridConfig.columnDefs\" >\n" +
"<div custom-cell-template=\"\" ng-if=\"def['cellTemplate']\" row=\"row\" def=\"def\"></div><div ng-if=\"!def['cellTemplate']\">{{row[def.field]}}</div>\n" +
"</div></td>\n" +
                        "</tr>\n" +
                "</tbody>\n" +
        "</table>";
        return directive;
});
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Angular-scrollable-table directive demo</title>

    <link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css"
        rel="stylesheet" type="text/css">

    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.29/angular.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.29/angular-sanitize.js"></script>
    <script type="text/javascript" src="script.js"></script>
  </head>
  <body ng-app="myApp">
    <div ng-controller="MyCtrl">
      <div grid="" grid-config="myGridConfig"></div>
          2{{myGridConfig.rowCollection[1].selected}}
    </div>
  </body>
</html>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Prevent the page from refreshing when a value is entered

I currently have a table embedded within an HTML form that serves multiple purposes. The first column in the table displays data retrieved from a web server, while the second column allows for modifying the values before submitting them back to the server. ...

When adding an object to an array in AngularJS, it seems to trigger updates for

Currently, I am fetching data from a WebAPI and then storing it in an array called products, which is scoped. $scope.products In addition to the products array, I also have another scoped array named selectedFish. $scope.selectedFish = []; My objective ...

transfer jquery element using jquery ajax

How can I send a jQuery object to a PHP function using POST? When I stringify the object, I get the following output: [ { "id": "701", "user_id": "2", "playlist": "ukg%20garage", "tracks": "5", "thumbnail": "Coldplay.jpeg", "crea ...

Unable to refresh scope variable within the scope function as expected

Updating my chart data using chartJS and its angular-chart wrapper has been quite the challenge for me. When I click a button, new data is added to the chart without any issues. However, I encountered a problem when trying to update the chart after a scro ...

HTML with an intricate and dynamic table layout

I am attempting to design a dynamic table that looks like the one shown in this image: https://i.stack.imgur.com/0Bmur.png The challenges I face in creating this HTML table are as follows: It needs to be dynamic as it will display data from a database T ...

How can you animate the background of a website using AngularJS - CSS or JavaScript?

My aim is to create a dynamic animation for the background image when the view changes. The current background image is set through a function defined within MainController: // app/js/controllers.js $scope.getBg = function() { return $route.current.sco ...

Exploring deep within JSON data using jQuery or Javascript

I have a substantial JSON file with nested data that I utilize to populate a treeview. My goal is to search through this treeview's data using text input and retrieve all matching nodes along with their parent nodes to maintain the structure of the tr ...

What could be the reason for the emptiness of my AngularJS scope object?

The code snippet in my controller looks like this: app.controller("weeklyLogViewer", function ($scope, $http){ $scope.allLogs = {}; $http({ method: 'POST', url: '../Utilities/WeeklyLog.php', data: $scope.dateSelected, ...

Determining the Location of a Drag and Drop Item

I have been utilizing the code found at for implementing Drag & Drop functionality. My inquiry is: How can I retrieve the exact position (x,y) of a group once it has been dragged and dropped? ...

Is it possible to await the response of a request in Socket.io with socket.on('answerToRequest')?

Can the following scenario work out? async function checkSocketStatus(){ socket.emit('checkOtherSocketStatus', otherSocketId); await socket.on('responseCheckSocketStatus', (status)=>{ console.log('status'); } ...

Clicking does not trigger scrollIntoView to work properly

I'm facing an issue with a button that is supposed to scroll the page down to a specific div when clicked. I implemented a scrollIntoView function in JavaScript and attached it to the button using onClick. Although the onClick event is functioning as ...

What is the process of adding an array into a JSON object using the set() function in Firebase?

I am trying to add a new item to my firebase database with a specific JSON object structure: var newItem = { 'address': "Кабанбай батыр, 53", 'cityId': 1, 'courierName': "МаР...

Omit node_modules from typescript compilation using gulp-typescript

Having trouble running a gulp task to compile my typescript due to dependency-related errors. /content/node_modules/@angular/core/src/facade/lang.d.ts(12,17): error TS2304: Cannot find name 'Map'. /content/node_modules/@angular/core/src/facade/l ...

Error: The object 'exports' is not defined in geotiff.js at line 3

Looking to integrate the geotiff library with Angular 6.1.0 and TypeScript 2.9.2. Installed it using npm i geotiff Encountering the following error in the browser console: Uncaught ReferenceError: exports is not defined at geotiff.js:3 After r ...

"Getting Started with Respond.js: A Step-by-Step

I've been struggling to find clear instructions on how to properly set up respond.js. Should I just unzip it into the htdocs folder, or do I only need respond.min.js in there? Then, do I simply reference the file like this... <script src="respon ...

Troubleshooting ng-click not functioning within ng-repeat with database integration in MEAN Stack

//app.js var blogApp = angular.module('BlogApp', []); blogApp.controller('BlogController', function($scope, $http){ $scope.createPost = createPost; $scope.deletePost = deletePost; function init(){ getAllPosts(); } init(); ...

Encountering a problem involving the apostrophe character "'" when trying to save content into a MySQL database

I have been developing an application that allows users to create HTML templates and save them. Users can utilize different components such as text, images, etc. to build HTML pages. Challenge: The issue I'm encountering is when a user inputs text wi ...

Guide to implementing a delay in JavaScript/jQuery code right after invoking an asynchronous function

Is there a way to execute an asynchronous function and then introduce a delay in the subsequent code execution to ensure that the asynchronous operation has completed? I want to avoid wrapping the following code in a function and delaying it, I simply ne ...

Is there a reason why angularJS doesn't provide the exact error location directly, opting instead to just offer a link to their website that provides a generic explanation?

Why does AngularJS not provide the specific error location directly, such as which file the error is in, instead of just giving a link to their website with a generic explanation? This makes debugging very challenging! Whenever there is an error, it becom ...

What are some methods for singling out a specific table row?

When working on my app, I faced the task of importing a JSON file and displaying its contents in a table with 3 columns. However, there are two main issues that arose: Utilizing array index for the row key is not recommended due to the table also having ...