AngularJS will not compile elements containing ng-repeat

I developed a specialized directive that swaps out the content within an element with a loading icon until the value of a specific attribute is no longer undefined. Here is the code for the directive:

.directive('ngLoading', function (Session, $compile) {

      var loadingSpinner = '<div class="spinner">' + 
        '<div class="rect1"></div>' + 
        '<div class="rect2"></div>' +
        '<div class="rect3"></div>' +
        '<div class="rect4"></div>' +
        '<div class="rect5"></div></div>';

      return {
      restrict: 'A',
      link: function (scope, element, attrs) {
        var originalContent = element.html();
        element.html(loadingSpinner);
        scope.$watch(attrs.ngLoading, function (val) {
            if(val) {
                element.html(originalContent);
                    $compile(element.contents())(scope);
            } else {
                element.html(loadingSpinner);
            }
        });
      }
    };
  });

I utilize this directive in my view like this:

<div ng-loading="user">
    {{user.name}}
</div>

The content inside this div is substituted by a loading icon until the user variable in the scope holds some data. At that point, the initial content is restored and compiled using $compile.

Although this setup works well in most instances, it fails when there is an ng-repeat directive present in the original content. For example, the following scenario does not work:

<div ng-loading="users">
    <table>
        <thead>
            <tr>
                <th>Name</th>
                <th>Age</th>
            </tr>
        </thead>
        <tbody>
            <tr ng-repeat="user in users">
                <td>{{user.name}}</td>
                <td>{{user.age}}</td>
            </tr>
        </tbody>
    </table>
</div>

In this case, the table is rendered but none of the rows inside tbody are displayed as if the users variable was null. Even though I can see that the users variable indeed contains an array of users right before the $compile call. I attempted wrapping the $watch code within my directive with an $apply call, leading to an error stating "$apply already in progress."

It's important to note that the controller overseeing the div has a property named users.

What could be causing this issue?

UPDATE

I made changes to my HTML like so:

<div ng-loading="users">
    {{users[0]}}
    <table>
        <thead>
            <tr>
                <th>Name</th>
                <th>Age</th>
            </tr>
        </thead>
        <tbody>
            <tr ng-repeat="user in users">
                <td>{{user.name}}</td>
                <td>{{user.age}}</td>
            </tr>
        </tbody>
    </table>
</div>

Upon completion of the loading process, the div's content is replaced by the toString representation of the first user in the array, containing all the correct details, followed by an empty table. This appears to be a problem specifically related to ng-repeat...

Answer №1

Perhaps consider taking a different approach - in Angular, that type of DOM manipulation isn't typically recommended. With Angular's two-way data binding, could a conditional show/hide in the HTML achieve the desired outcome while allowing Angular to handle the details?

For example:

<div ng-show="users">
    <table>
        <thead>
            <tr>
                <th>Name</th>
                <th>Age</th>
            </tr>
        </thead>
        <tbody>
            <tr ng-repeat="user in users">
                <td>{{user.name}}</td>
                <td>{{user.age}}</td>
            </tr>
        </tbody>
    </table>
</div>
<div class="spinner" ng-hide="users">
    <div class="rect1"></div>
    <div class="rect2"></div>
    <div class="rect3"></div>
    <div class="rect4"></div>
    <div class="rect5"></div>
</div>

Answer №2

To streamline the process, rather than retrieving the HTML content and adding the element content separately, it's more efficient to directly insert the spinner into the content and apply CSS changes to display it at the top. Once the loading is finished, simply remove the spinner element as shown below:

app.directive('ngLoading', function ($compile) {

          var loadingSpinner = '<div class="spinner">' + 
            '<div class="rect1"></div>' + 
            '<div class="rect2"></div>' +
            '<div class="rect3"></div>' +
            '<div class="rect4"></div>' +
            '<div class="rect5"></div></div>';

          return {
          restrict: 'A',
          link: function (scope, element, attrs) {  

            scope.$watch(attrs.ngLoading, function (val) {
                if (val) {
                    element.append(loadingSpinner);
                } else {
                    element.find('div.spinner').detach();
                }
            });
          }
        };
      });

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

In order to trigger the mousedown event in Three.js, I have found that I need to click and gently move my cursor

There seems to be an issue with the code below, as the "intersects" variable does not populate with items in the onDocumentMouseDown event handler when I simply click an object. It only detects the object clicked when I click and slightly drag the mouse be ...

Exploring the functions of the elasticsearch javascript library: Understanding the search_type feature

Currently, I am attempting to run a search query using search_type of count with the elasticsearch.angular.js version sourced from the npm module. The query can be successfully executed as follows: POST /index1/type1/_search?search_type=count { " ...

Tips on creating a script for detecting changes in the table element's rows and columns with the specified data values

I have a div-based drop-down with its value stored in TD[data-value]. I am looking to write a script that will trigger when the TD data-value changes. Here is the format of the TD: <td data-value="some-id"> <div class="dropdown">some elements& ...

Creating types for React.ComponentType<P> in Material-UI using TypeScript

I am currently working with Typescript and incorporating Material-UI into my project. I am trying to define the component type for a variable as shown below: import MoreVert from '@material-ui/icons/MoreVert' import { SvgIconProps } from '@ ...

I am attempting to select the second checkbox within a nested ng-repeat in my Protractor Test

Here is the snippet of my code: <div class="sl" ng-repeat="qual in mQuals | orderBy : 'order'"> <div ng-repeat="cList in tList | orderBy:'Priority' " ng-if="c == qualifier.Id"> <p class="sl-text-body--small ...

Encountered an issue while attempting npm install, with the error message "Error: Undefined variable standalone_static_library in binding.gyp" and node-sass chokidar error

I've been working on updating a Ruby on Rails and React project from 3 years ago. However, I encountered an issue while trying to npm install. $ npm install gyp: Undefined variable standalone_static_library in binding.gyp while trying to load binding ...

Navigating CSS imports when implementing server-side rendering in React

This situation is somewhat similar to another question about Importing CSS files in Isomorphic React Components However, the solution proposed involves a conditional statement that checks if the import is being done from the server or the browser. The iss ...

Identify when JavaScript makes calls to C# methods

My team and I are currently working on a .NET MVC project in Visual Studio, where we frequently need to invoke controller or model functions from JavaScript files. An example of this is: $.ajax({ "dataType": 'json', ...

abandoning the upload of an item into a THREE.js environment

Currently, I am working on a THREE.js scene where I need to prevent uploading multiple files into the scene simultaneously. The setup involves using Angular to implement the three js scene and canvas as a factory to maintain only one instance of a canvas a ...

Managing Image Quality with Unsplash API

How can we ensure high quality images are embedded on the web using Unsplash API or other methods? The image displayed in the example below appears blurry and unclear compared to the original image. Original Image link: Example of embedding the image abo ...

I'm having trouble getting Grunt Source Maps to function properly within the foundation-press theme

I'm struggling to enable source maps for the npm package grunt-sass. Here's a snippet from my Gruntfile.js: The issue lies in this line: sourceMap: true, at line 13 module.exports = function(grunt) { var jsApp = [ 'js/app.js' ...

The parameter is causing the Aggregate Function to malfunction

When fetching MongoDB data, I encountered an issue. Using the JavaScript parameter to retrieve data from MongoDB resulted in a null return, but manually typing it brought all the data without any problems. That part is working fine. const data = await Numb ...

Building an Express API using ES6 script: A Step-by-Step Guide

After creating a no-view REST API using the express generator, I decided to convert everything to ES6 script and compile it with Babel. However, upon entering localhost after the changes, an error message appeared: No default engine was specified and no ...

Is there a way to ensure that a method is always called in JavaScript after JQuery has finished loading

We recently integrated jquery load into our website to dynamically load content into a div without refreshing the whole page. However, we've noticed that in the complete function, we have to constantly re-apply various bindings. Is there a way to set ...

"Within Angular 2+, the attribute referenced is not recognized as a valid property, even though it is explicitly defined within the @Input

The main goal here is to increase the vertical pixel count. <spacer [height]="200"></spacer> Initially facing an issue where an error message states that height is not a recognized attribute of spacer. To address this problem, I set up the fo ...

"Prevent users from taking screenshots by disabling the print screen key

Can someone help me figure out how to prevent users from taking a screenshot of my website by disabling the print screen key? Here's the code I've tried so far: <SCRIPT type="text/javascript"> focusInput = function() { document.focus() ...

What steps can I take to improve the efficiency of this filtering process in Vue.js?

I am seeking ways to enhance the method I utilize for determining which values should be displayed in a table. At present, my table displays various values and is accompanied by input fields. The values chosen in these input fields serve as filters for the ...

Respond to a recommendation with a response

I've been working on setting up a bot for my Discord server, and I recently added a "marry" command to it. Whenever a user makes an offer, a message announcing the proposal pops up with two reaction options. The first reaction signifies yes, while th ...

Convert a function into another function when the button is clicked

Hey there, I have an array containing years in my controller: public function getNextYear() { $years = $this->getYears(); foreach ($years as $key => $value) { $y[] = $value + 1; } return $y; } I am displaying this on my ind ...

What could be causing my externally hosted React component to malfunction when being imported via NPM?

After successfully creating a standalone component within my original project, I decided to explore the possibility of releasing it as an NPM module. To kick off this process, I attempted to load the library from another repository using NPM in a new proje ...