Creating Angular directives: dynamically applying ng-class directive to template elements during compilation

In short, the aim is to streamline templating by avoiding manual addition of

ng-class={'has-error': 'formName.inputName.$invalid'}
for every individual form-group

The idea is to develop a directive that will automatically generate a string to append to a template element. This string will be an ng-class attribute with an expression.

Initially, I believed that creating a simple directive during the compile phase to insert the ng-class attribute would suffice, but it appears insufficient.

Definition Object for Directive

{
    restrict: 'C',
    compile: function(tElement, tAttrs) {
        var $elem = angular.element(tElement),
            formName = $elem.parents('[ng-form]').length ? $elem.parents('[ng-form]').attr('ng-form') : $elem.parents('form').attr('name'),
            controlName = $elem.find('.form-control').attr('name');

        $elem.attr('ng-class', '{"has-error": ' + formName + '.' + controlName + '.$invalid}');

        console.log('added ng-class attr', $elem.attr('ng-class'));
    }
}

Check out the Plunk here

Take a look at the console output

The first form-group has the dynamic addition of its ng-class attribute.
The associated expression is correct
However, the execution of the ng-class directive never occurs

The second form-group manually adds its ng-class attribute in the template and functions as expected.

--

What could be the missing piece here?

Answer №1

Successfully achieved the desired outcome without increasing additional watchers (although ng-class utilizes a watcher, the proposed solution led to a surge in watchers count during testing)

In order to avoid recursion when $compiling the new DOM element in postLink, I had to remove the directive itself. Consequently, this restriction prevents me from reusing a class-name like "form-group"

Below is the Directive that is currently functioning

function bsFormClass($compile) {
  return {
    restrict: 'A',

    compile: function (tElement) {

        var $elem = angular.element(tElement),
            controlName = $elem.find('.form-control').attr('name');

        if(!controlName) { return; }

        var formName = $elem.parents('[ng-form]').length ? $elem.parents('[ng-form]').attr('ng-form') : $elem.parents('form').attr('name');

        $elem.attr('ng-class', '{"has-error": ' + formName + '.' + controlName + '.$invalid}');
        $elem.removeAttr('bs-form-class');

        return {
            post: function(scope, elem, attrs) {
                $compile(elem)(scope);
            }
        }
    }
  }
}

Many thanks for your assistance

Answer №2

It is not possible to include directives in the current element!

To resolve this issue, simply monitor the relevant expressions and apply the class when needed:

function updateClassBasedOnFormValidity () {
    return {
        restrict: 'E',
        link: function(scope, elem, attrs) {
            var formName = elem.parents('[ng-form]').length ? elem.parents('[ng-form]').attr('ng-form') : elem.parents('form').attr('name'),
                controlName = elem.find('.form-control').attr('name');

            scope.$watch(formName + '.' + controlName + '.$invalid', function(newval) {
              elem.toggleClass('has-error', !!newval);
            });
        }
    };
}

The ng-class directive will handle the monitoring process. Check out the altered plunk here: http://plnkr.co/edit/aBc9YRTgP4C7LmNhOJKU?p=preview

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

Efficiently incorporating multiple properties into one in Angular

Within my Angular service, I have defined variables in the following manner: export class MyService { someVariableA = 1; someParams = { someVariableB, otherVariable: this.someVariableA }; } In a component, I update 'someVariableA&a ...

Having trouble toggling journal entries in an HTML journal? The Jquery function might not be working properly

I have been tasked with creating a civil war journal for my 8th grade Social Studies class and I decided to present it as an HTML file featuring the title and date of each journal entry. The goal is to allow users to click on each entry to open it while au ...

Combining Parallel Axios GET Requests Using the Promise.allSettled Method Along With Source Objects

My current challenge lies in successfully merging the keys of the source object containing the URL with the results of Axios GET requests, despite resolving parallel execution using the allSettled Promise method. Here is the array of objects I am working ...

What is the reason behind certain vanilla JavaScript libraries necessitating NPM?

I am a beginner in JavaScript and I want to learn more about it. Recently, I discovered a library called bounce.js which is an animation library. However, it requires NPM for installation, but I am hesitant to use NPM or any packet Manager because they hav ...

Guide to retrieving objects in React.js

Struggling to extract a country from an online JSON file that I am currently fetching. I am attempting to streamline the process by creating a function to retrieve the country from the dataset and avoid repeating code. However, I am encountering difficulti ...

Effortless Like/Unlike feature with a text button option enhanced with ajax functionality and

Struggling to create a simple Like/Unlike button in PHP without refreshing the page. Despite an abundance of tutorials on AJAX and jQuery, implementation remains elusive due to lack of experience. Uncertain where each part of the code goes within which fil ...

What is the best way to use multiple encoding types when sending data via a POST method in Node.js?

My current challenge involves sending a combination of name and description text data alongside a video file. Unfortunately, I've encountered an issue where I can only send either the video or the text, but not both simultaneously. Below is the code ...

Updating a <div> using Ajax while also transmitting a counter variable

In my possession are two .php files. The initial file, named ajax_testing.php, is structured as follows: <!DOCTYPE html> <html> <?php $index_local=1; $_SESSION['global_index'] = $index_local; ?> <head> <script ...

Adjust the width of columns in a C# GridView

I need to set a fixed width for the "Address" column in a GridView during the page load event. The GridView is bound from C# and uses a data source from C# as well. The reason I need to set the width for the "Address" column is because it contains long d ...

Getting started with Tween JS on a three-dimensional cube in Three JS

I'm venturing into the world of Tween JS and attempting to create a basic animation that moves objects to the right using Tween. Here is the code snippet from my init function (utilizing Three JS): var geometry = new THREE.CylinderGeometry(200, 200, ...

Convert checkbox choices to strings stored in an array within an object

I have a intricate object structure JSON{ alpha{ array1[ obj1{}, obj2{} ] } } In addition to array1, I need to include another array: array2 that will only consist of strin ...

Experiencing difficulties integrating relational data with Angular and MongoDB

I have a view where I display 'Transporters'. Each Transporter has multiple 'Deliveries', so I want to associate Deliveries with the corresponding Transporters. My tech stack includes express, mongoose, and angular.js. Here are my mode ...

Unfortunately, the isoWeek function in moment.js is not functioning correctly on our Heroku instance

When my app relies on moment().startOf('isoWeek') to find the current start of the week, it functions perfectly on my local machine, showing Mon Oct 31 2016 00:00:00 GMT-0400 (EDT) as expected. However, on my Heroku server, this code fails and re ...

Loading page with asynchronous JavaScript requests

As I send my AJAX request just before the page loads and then call it on the same page afterwards, here is what it looks like: <input type="text" class="hidden" id="storeID" value="<?php echo $_GET['store']; ?>"> $(document).ready(fu ...

Executing two SQL queries simultaneously in NodeJS can be achieved by using a single statement

app.get("/total", function(req,res){ var q = "SELECT COUNT(*) AS new FROM voters_detail WHERE parties LIKE '%BJP%'"; connection.query(q, function(err, results){ if(err) throw err; var hello = results[0].new; res.send("BJP Was Voted By ...

Creating a Next.js dynamic route that takes in a user-submitted URL for customization

Currently, I have implemented the Next.js Router to facilitate the display of different dashboards based on the URL slug. While this functionality works seamlessly when a button with the corresponding link is clicked (as the information is passed to the Ne ...

Understanding the "this" keyword in React.js is crucial for

Looking for a solution: updateSongIndex: function(index) { this.setState({currentSongIndex: index }); } I want to trigger the updateSongIndex function by clicking on a div element and passing an index as an argument var trackList = d ...

There seems to be an issue with the Axios Post not consistently finishing each time

I'm a newcomer to React and running into some issues. Here is the API call I am currently using. Axios.post(Addr_currentRound_addOne, { gameId: gameId }).then(history.push("/leiter_tunierplan/"+gameId)); And here is the corresponding API c ...

How can I add scrolling functionality to the active list item with React?

I created a music player that allows users to search for songs by artist. Check out the CODE SANDBOX here! Take a look at how the SongsList component is structured in my project: const SongsList = (props) => { const { loading, errorMess ...

ERROR: mammoth has not been declared

I've been attempting to integrate the mammoth npm library with my Meteor project. When using the import command, import { mammoth } from "mammoth";, I encountered Uncaught TypeError: Cannot read property 'bind' of undefined After trying ...