59

I've been reading this nice recent article about new .component() helper in Angular 1.5, which is supposed to help everyone to migrate to Angular 2 eventually. Everything looks nice and simple, but I couldn't find any information about DOM manipulation inside components.

There is a template property though, which can be a function and accept $element and $attrs arguments. Still it's not clear to me if that's the replacement for a link function. It doesn't seem so.

troorl
  • 1,579
  • 1
  • 15
  • 20

6 Answers6

74

EDIT 2/2/16: The 1.5 documentation now covers components: https://docs.angularjs.org/guide/component


Some thoughts based on some reading (links below):

  • Components aren't replacements for directives. A component is a special type of directive that organizes a controller with a template.

  • Components do not have a link function and controllers still are not where you'd handle DOM manipulation.

  • If you need DOM manipulation, your component can use other directives that include that DOM manipulation in a link function.

It took me a while to figure this out, but once I did it made some sense: components are directives but not all directives are--or need to be--components.

The question about link functions is a natural one, or was to me, when I thought components were replacing directives. Why? Because we've been taught to put DOM manipulation inside a directive's link function: "Directives that want to modify the DOM typically use the link option to register DOM listeners as well as update the DOM." https://docs.angularjs.org/guide/directive.

If you're running with that assumption (components replace directives), then you'll find that the Angular docs don't answer the question because, well, it's not the right question once you know the purpose of a component. (Components are described in the $compileProvider documentation not the directive documentation.)

Background reading

What I say above is really a rephrasing of what Todd Motto has said in what's probably the best discussion (so far) on components and directives:

https://www.reddit.com/r/angularjs/comments/3taxjq/angular_15_is_set_to_introduce_the_component/

It could be useful to have those comments pulled out into a more general article.

Most articles on components don't mention a link function (this doesn't mean these aren't excellent articles):

https://toddmotto.com/exploring-the-angular-1-5-component-method/

https://medium.com/@tomastrajan/component-paradigm-cf32e94ba78b#.vrbo1xso0

https://www.airpair.com/angularjs/posts/component-based-angularjs-directives

Or when the link function is mentioned it is in parentheses:

http://teropa.info/blog/2015/10/18/refactoring-angular-apps-to-components.html

One article says that components, "use controllers instead of link functions." But it's not an "instead" situation: controllers aren't stand-ins for link functions.

BlackICE
  • 8,816
  • 3
  • 53
  • 91
jody tate
  • 1,406
  • 1
  • 13
  • 25
  • 1
    Well said. I use components for "pages", with children directives where DOM manipulation is needed. It was strange at first, but it's working very nicely especially with 1.5 multi transclude. – Phix Feb 03 '16 at 05:32
  • 3
    I'm not sure why you say you shouldn't do any dom manipulation in components when the angular documentation explicitly says: "$postLink() - Called after this controller's element and its children have been linked. Similar to the post-link function this hook can be used to set up DOM event handlers and do direct DOM manipulation." Can you explain why this isn't appealing to you? – pasquers Sep 08 '16 at 16:58
  • I don't use the words "shouldn't" or "any" in the answer--apologies if that's implied. As for what `$postLink` is for, this is helpful reading: https://toddmotto.com/angular-1-5-lifecycle-hooks#what-postlink-is-not. – jody tate Sep 09 '16 at 17:28
  • am I missing something or is the basic consensus that components are just watered down directives? A component could be re-created as a directive, but a directive could not always be re-created as a component. Components just aren't as powerful as directives because they lack the ability to perform DOM manipulation. – jtate Mar 29 '17 at 20:27
  • this is a great answer. `component.$postLink` should be used to set logic after the DOM is set. `directive.link` is still the go to spot for DOM manipulation. You know it's time to create a directive when you're injecting $element into your component controller. – user2954463 Aug 09 '17 at 16:20
7

This makes it easier to write an app in a way that's similar to using Web Components or using Angular 2's style of application architecture.

Advantages of Components:

simpler configuration than plain directives promote sane defaults and best practices optimized for component-based architecture writing component directives will make it easier to upgrade to Angular 2

When not to use Components:

for directives that rely on DOM manipulation, adding event listeners etc, because the compile and link functions are unavailable when you need advanced directive definition options like priority, terminal, multi-element when you want a directive that is triggered by an attribute or CSS class, rather than an element

C.Champagne
  • 5,381
  • 2
  • 23
  • 35
surekha shelake
  • 246
  • 3
  • 7
5

Update (from august 22, 2017): $inject is recommended way for doing this in AngularJS. Read Styleguide: Styleguide link and AngularJS docs: AngularJS docs

For using DOM bindings in components instead of creating directive with link function you can inject '$element' or other service you need in your controller function, e.g.

app.component('pickerField', {
    controller: PickerField,
    template: '<span>Your template goes here</span>'
  });

  PickerField.$inject = ['$element'];

  function PickerField(element) {
    var self = this;
    self.model = self.node.model;
    self.open = function() {
      console.log('smth happens here');
    };
    element.bind('click', function(e) {
      console.log('clicked from component', e);
      self.open();
    });
  }
Ekaterina Tokareva
  • 813
  • 11
  • 11
  • 1
    Injecting $element is bad practice. It makes the $componentController difficult to test. – user2954463 Aug 09 '17 at 16:06
  • 1
    @jdrury Actually, this is angularJS recommended way to inject dependencies. You can read the docs: https://docs.angularjs.org/guide/di#-inject-property-annotation and the styleguide https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#manual-annotating-for-dependency-injection Also I'll update my answer with this links – Ekaterina Tokareva Aug 22 '17 at 15:28
  • 2
    I agree - if need to access $element into a component, your way is correct. But my argument is that if you need to access $element, you should use a directive instead of a component. My reason is this: If you inject $element into a component, you will have to create a fake $element via $compile when you unit test with $componentController. – user2954463 Aug 22 '17 at 15:35
2

Ok, so it appears that controller is the right place for it now, since it is the only possible one. Also we can't use a replace option in the a component helper.

troorl
  • 1,579
  • 1
  • 15
  • 20
0

You can use $postLink() function which is in newest angular.

https://docs.angularjs.org/guide/component

Similar to the post-link function this hook can be used to set up DOM event handlers and do direct DOM manipulation.

Szymon
  • 1,281
  • 1
  • 20
  • 36
-1

According to current Angular2 documentation (see https://github.com/angular/angular/blob/master/modules/angular2/docs/core/02_directives.md) there will still be directives in Angular2. So basically you will be able to use both @Directive or @Component, where:

  • Directives are useful for encapsulating behavior.
  • Component is a directive which uses shadow DOM to create encapsulate visual behavior. Components are typically used to create UI widgets or to break up the application into smaller components.

So According to this, if you need DOM manipulation you will need to use @Directive, therefore Angular.directive in Angular 1.x. Event bindings can be done using the host properties. Concerning DOM manipulation per se there is still missing documentation (e.g. https://github.com/angular/angular/blob/master/modules/angular2/docs/core/09_compilation.md or https://github.com/angular/angular/blob/master/modules/angular2/docs/core/08_lifecycle.md) but you can look for Lifecycle as suggested here https://stackoverflow.com/a/32062065.

As a short answer, with Angular 1.5+, continue using angular.directive if you have DOM access, otherwise encapsulate into angular.component. Also try to reduce as much as possible the use of $scope for non-dom event and prefer RxJS for that see https://medium.com/front-end-developers/managing-state-in-angular-2-using-rxjs-b849d6bbd5a5#.obgb6dl6n,

Community
  • 1
  • 1
barraq
  • 326
  • 2
  • 7