0

Directive allocation inside a parent template:

<div badge></div>

Directive template templates/badge.html:
Notice allocation of the dynamic id, using directive $id.

<div>
    <span id="id{{ ::$id }}_name">Nik Sumeiko, Frontend engineer</span>
</div>

Directive:

angular.module('app').directive('badge', () => ({
  restrict: "A",
  replace: true,
  templateUrl: "templates/badge.html",
  link: (scope, element, attributes) => {

    // Tries to query nested DOM element by a dynamic selector.
    const name = element.find(`#id${scope.$id}_name`);

    console.log(name.length, element.html());
  }
}));

Based on the console output it's clearly visible that directive template haven't compiled its dynamic values yet:

0 "
    <div>
      <span id="id{{ ::$id }}_name">Nik Sumeiko, Frontend engineer</span>
    </div>
"

How then it is possible to query nested elements by a dynamic selector? Is there any other directive methods that are dispatched after Angular rendered dynamic values of the template?

Please don't suggest to use $timeout injected function to delay template rendering inside link method, since I don't think it is the right way…

Nik Sumeiko
  • 8,263
  • 8
  • 50
  • 53

1 Answers1

2

Here the quote from angular documentation:

After linking the view is not updated until after a call to $digest which typically is done by Angular automatically.

That's why you can't find the elements containing expressions in their id - the view is not updated at that time.

Here the workarounds:

Create the element and attach it to the DOM manually

This way you have a reference to element, so you don't have to query for it:

link: (scope, element, attributes) => {
    let span = $compile('<span id="id{{ ::$id }}_name">Nik Sumeiko, Frontend engineer</span>')(scope);
    // do something to span ...
    element.append(span);
}

Use $timeout

You can use $timeout with a zero delay, it is not the wrong way. $timeout will execute your code right after the current $digest cycle, meaning after the view bindings are updated:

link: (scope, element, attributes) => {
    $timeout(function() {
        let span = element[0].querySelector(`#id${scope.$id}_name`);
    });
}
Alexander Kravets
  • 4,245
  • 1
  • 16
  • 15