3

Directives in Angular2 do not have "scopes", while Components do. But in my case I need Directive to create a scope. Look at my App component - it has an HTML template, and ANYWHERE on any element the foo directive could appear. This should grab some date from service and assign it to the Element.

In Angular1 it was very easy... Directives could have its own scope. But in Angular 2 I cannot find any (even dirty) way to achieve that.

It looks like a simple task, doesn't it?

@Directive({
  selector: '[foo]'
})
class FooDirective {
  @Input()
  public id:string;

  public bar;

  constructor() {
     this.bar = 'This is the "bar" I actually need. It is taken from DB let's say..' + this.id;
  }
}




@Component({
  selector: 'app',
  template: `
     <div foo id="2">
       This is random content 1: {{bar}}
     </div>

     <div foo id="2">
       This is random content 2: {{bar}}
     </div>
  `,
  directives: [FooDirective]
})
class App {
  bar:string = 'This should be ignored, I need "bar" to be set from directive!';
}

bootstrap(App);
Community
  • 1
  • 1
Arūnas Smaliukas
  • 3,231
  • 6
  • 27
  • 46

2 Answers2

5

You could try something like that leveraging a local variable that would reference the applied directive:

@Component({
  selector: 'app'
  template: `
    <div foo id="2" #dir1="foo">
      This is random content 1: {{dir1.bar}}
    </div>

    <div foo id="2" #dir2="foo">
      This is random content 2: {{dir2.bar}}
    </div>
  `,
  directives: [FooDirective]
})
class App {
  bar:string = 'This should be ignored, I need "bar" to be set from directive!';
}

In your case bar is evaluated using properties of the current component, the App one.

Edit (following the @yurzui's comment)

You need to add an exportAs property in your directive:

@Directive({
  selector: '[foo]',
  exportAs: 'foo'
})
class FooDirective {
  (...)
}
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • Yeah, I know that. But let's say I'm creating "helper" directive for our designers, and I said them "add foo on any element, and it will grab some data from database".. And I don't need to add "extra work" for them.. (I already done this tool with angular 1, so they will not like my new tool with "more work for them" – Arūnas Smaliukas May 17 '16 at 16:01
  • 1
    Don't forget to add exportAs: 'foo' within metadata directive. – yurzui May 17 '16 at 16:01
  • Also I think there is limit in one template for those "local variables". But our templates are quite big – Arūnas Smaliukas May 17 '16 at 16:02
  • 1
    I understand well your approach! That said, I'm not sure that it's possible with Angular2 directive. I guess that you could do something with components (with an attribute selector) and a template element. Something like this: `
    `. This answer could give you ideas: http://stackoverflow.com/questions/36531503/custom-templatetransclusion-without-ng-content-for-list-component-in-angular2.
    – Thierry Templier May 17 '16 at 16:10
  • `ng-content` is also supported but variables are resolved in the component that provides this content... – Thierry Templier May 17 '16 at 16:14
  • 1
    @ThierryTemplier your hint with – Arūnas Smaliukas May 18 '16 at 07:51
  • @ArūnasSmaliukas ngFor leverages embedded views with the `ViewContainerRef` class... – Thierry Templier May 18 '16 at 07:56
0

What do mean when you say that components do have scope?

My understanding is that there is no shared object (or prototypal inheritance) between components. But I think that is what you are looking for -- you want FooDirective and App to share the same (scope) object, correct? If so, I don't think there is anything equivalent in Angular 2.


I doubt you'll like this, but the best I could come up with (that is different from @Thierry's approach) is to use the div as the "shared object" (rather than the directive). The directive uses HostBinding to save the value to a data attribute on the div, then the component retrieves that value in the template, using a local variable to get a reference to the div/shared object:

import {Component, Directive, Input, HostBinding} from '@angular/core';

@Directive({selector: '[foo]'})
class FooDirective {
  @Input() id:string;
  @HostBinding('attr.data-bar') bar;
  ngOnInit() {
     this.bar = 'This is "bar" I actually need. It is taken from DB lets say...' + this.id;
  }
}

@Component({
  selector: 'my-app',
  template: `{{title}}<p>
    <div #div1 foo id="2">
      This is random content 1: {{div1.getAttribute('data-bar')}}
    </div>`,
  directives: [FooDirective]
})
export class AppComponent {
  title = `Angular - RC.1`;
}

Plunker

I like @Thierry's approach better than what I show above, but I figured I would post what I was toying around with anyway.

Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • Thanks for the answer, but I still try to make "less work" for my company designers. I mean they already are using my directive from Angular 1 and I said them "Add 'foo' on any element and you will have some magick value in `{{bar}}`". I cannot say to them "I have made better tool with angular 2 but now you will have to add more work to make the same result..." And I still believe that it is possible to make it with Angular 2.. At the moment I'm trying with structural directives (*foo) – Arūnas Smaliukas May 18 '16 at 07:57