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

Tips for identifying emails from protected platforms such as ProtonMail or Hushmail

A question for the community: So, I have a website that's totally above board and legitimate. Lately, I've noticed some shady characters trying to register from email domains like Proton and Hush - yikes! Before I dive into PhoneText validation, ...

Using npm to install packages with multiple package.json files

My current project includes a submodule with another submodule, each having their own package.json file. This is how I set up my project: |root ----|node_modules ----|package.json ----|someFolder ----|submodule_1 -------- |package.json -------- |someFold ...

In search of a comprehensive AJAX-enabled content management system

Is there a Content Management System (CMS) available that can create a fully ajax-driven website, allowing for a persistent Flash component without the need to reload it with each page navigation? ...

Steps to remove OTP from dynamodb after a specified time period (2 minutes)

Recently, I have been utilizing the setImmediate Timeout function to call the deleteOTP function with details such as the userId of the OTP to be deleted. However, I am encountering challenges when passing the argument (userId) to the deleteOTP function ...

Tips for implementing a ternary operator within a component using the v-for directive

Here is the updated code with a conditional check for item.image: <template lang="pug"> b-carousel.d-none.d-sm-block( id='categoryRoulette' controls no-animation :interval='0' ) b-carousel-slide( v-for=&quo ...

Setting the initial date value for an Angular Material md-datepicker to a specific date

When using Angular Material md-datepicker, by default the system's current date is set as today's date and highlighted in the calendar. However, I am looking for a way to manually set any given date as today's date instead. Please see this i ...

Incorporate highcharts data into your Laravel project using the power of AJAX

Encountering an issue with loading data for a Highcharts chart. The response from my controller provides the following data: [['Doctorado', 91.86],['Maestría', 6.98],['Licenciatura', 1.16]] Although the AJAX call is succes ...

Cycle through the list and populate the table with the data

My attempt to clarify this explanation is my best, as articulating exactly what I am trying to achieve is quite challenging: Initially, I have a list of names: { "Items": [ { "Id": 0, "Name": "Robinson" }, ...

When trying to insert JavaScript into the console, a section of the code fails to execute

In this code snippet, I am fetching the most common English words from Wikipedia, extracting all the words from the page, and then filtering out the commonly used words: // get table data from most common words var arr = []; $.ajax({ url: 'https:/ ...

Populate a select list in real time with dynamically generated options

I'm facing a challenge at the moment; I am using JavaScript to dynamically generate select boxes, however, I require Ajax to populate them with options. At the moment, my code is returning undefined and I am unsure of how to proceed. While my PHP succ ...

Calls to debounced functions are postponed, with all of them running once the waiting timer is complete

Utilizing the debounce function to create a real-time search feature. Researching more on debouncing from https://css-tricks.com/debouncing-throttling-explained-examples/, it seems like the function should control the number of calls made. In my scenario ...

What steps can I take to make sure a particular node is successfully loaded and ready for use using JavaScript/jQuery?

When making a reservation at a hotel using the CJS Chrome extension, I am attempting to extract information about available rooms and rates both when the page loads and when the user changes dates. My current method involves injecting JavaScript into the p ...

Position divs to be perfectly aligned and centered

I'm in the process of creating a webpage and facing challenges with aligning my divs properly, specifically the animated company logos. I want them to be positioned side by side rather than on top of each other, with space in between to fit the layou ...

Utilize Angular to transform a standard text string within a JSON object into a properly formatted URL

Welcome to my first Stack Overflow question! I usually find the answers I need on my own, but this time I could use some guidance as I delve into Angular. I have a directive that pulls the name and URL from a JSON object and generates HTML with the name a ...

several guidelines assigned to a single element

Exploring two different directives in Angular: 1. Angular UI select - utilizes isolate scope. 2. Custom directive myDirective - also uses isolate scope to access ngModel value. Encountering error due to multiple directive usage with isolate scope. Isola ...

Utilize JQuery to identify and select every parent element, then retrieve the height of the first child element and adjust the height of the

Recently, I developed a PHP script that pulls news and case posts from a database. The HTML structure used for displaying these posts is as follows: <a href='/post/placeholder'> <div class='col nopadding col12-12 counter'> ...

What is the reason behind the failure of next/script with Google reCAPTCHA?

Currently, I am in the process of upgrading from next js version 8 to version 11. I wanted to take advantage of the amazing next js feature for "next/script". However, when I tried to implement it for Google reCAPTCHA using "react-recaptcha": "^2.3.10", th ...

Creating a dynamic nested list in HTML using JavaScript with data retrieved from a JSON source

I'm trying to generate a dynamic nested ul\li list from a JSON array. IMPORTANT! While I can use jQuery for this transformation, it's in a node.js environment where DOM access is restricted and I must work with a string. The depth of the a ...

Stop the spread - Touch to choose - React with Material Design

I am currently utilizing ReactJS along with the library @material-ui/core. In my code, I am seeking to halt event propagation on Click. Here is the snippet: <NumPad.Number onChange={val => { this.setPrice(val) } }> <TextField ...

What significance does it hold when an unhandled rejection event lacks a reason field?

Our app tracks client-side errors using Rollbar, but we keep encountering a not very helpful error message from Safari and Chrome: [unhandledrejection] error getting `reason` from event Upon investigation, I found that this message is generated by Rollbar ...