4

I'm using Angular 1.5+ with Typescript, preparing my code to be compatible with Angular 2. I've got a situation where many of my components need to use an application-wide repository location for the views that are mapped to their templateUrl properties, but sometimes a view needs a specific, local implementation.

So, normally the views are hosted on a fast-served CDN, they're re-used between multiple sites that all belong to the same general code base, api, etc. This is done to prevent duplicating them and to centralize what doesn't need to be repeated.

Rarely, I'll need to override this behavior and use a more specific, fine-tuned view. So my approach to this was to add a binding to the components called viewLocation, with the intent to use it like this;

<component-name></component-name> is the default. In this situation, the default CDN path is used. <component-name view-location="local"></component-name> is a unique situation. If this happens, the templateUrl should be able to respond to that and switch to a path relative to the specific application and not from the CDN.

I thought it would be pretty simple until I realized that I wouldn't have access to the binding properties within the actual constructor or templateUrl function, as is demonstrated here;

export class SidebarComponent extends Component {
   constructor() {
      super();
      this.bindings = { viewLocation: '=' };
      // other properties
      this.templateUrl = ($someService: IServiceInterface): string => {
         // help! I don't have access to the viewLocation property!
      }
   }
}

So is there anything else I can do to get access to that property?

Ciel
  • 4,290
  • 8
  • 51
  • 110
  • 1
    I think it's not possible in way you want to do it, but possible in other way http://stackoverflow.com/questions/23065165/angularjs-directive-dynamic-templates – BotanMan Jul 03 '16 at 20:27
  • Thanks. I will check that out. I'm a bit flabberghasted that you can't possibly access the bindings at this stage. It seems like a pretty critical flaw of the entire component system. – Ciel Jul 03 '16 at 20:28
  • Unfortunately, I don't think the link you gave me will work. There's no access to the HTML element for the component at this stage in the process, either. – Ciel Jul 03 '16 at 20:29
  • Basically you need to set template on `link` stage, when you can be sure that scopes are `connected` and value that you pass to the directive scope on the place – BotanMan Jul 03 '16 at 20:33
  • components don't have a `link` phase, I don't think. The official documentation even mentions this; https://docs.angularjs.org/guide/component – Ciel Jul 03 '16 at 20:36
  • Yeah, I just looked it up. They don't have a `link` stage. – Ciel Jul 03 '16 at 20:37
  • You are right, but why can't we try to do it on controller?:) like that http://stackoverflow.com/a/37782407/2500040 or just by setting $element.html(...) depending on your `viewLocation` value – BotanMan Jul 03 '16 at 20:41
  • Because again, this is a component - not a directive. They don't work like that. – Ciel Jul 03 '16 at 20:44
  • Trying to set the template from the controller yields all manner of strange behavior. It's not a directive. – Ciel Jul 03 '16 at 20:45
  • It does look like there is an exposed `$element` service in angular that might work, as it returns the DOM element of the injected component. I've got to find out if that stays working with Angular 2 though. But perhaps this method might be the right path. – Ciel Jul 03 '16 at 20:50
  • Check also this thread if you didn't yet http://stackoverflow.com/questions/33749994/dynamic-template-in-templaturl-in-angular2 – BotanMan Jul 03 '16 at 20:51
  • Yeah, now I see your idea, but you need to make it only for some components? I think maybe then it could be configured based on some service, so you will set value not for a component but for a service that would be injected into the templateUrl function – BotanMan Jul 03 '16 at 20:59

1 Answers1

7

This is not done in TS, but the AngularJS 1.5 component generally provides the $element and $attrs to the $injector when you are using an injectable template.

This is an example in AngularJS using Javascript where the template URL is picked based on an attribute set on the component:

angular.module('example', []);

angular.module('example').component('myComponent', {
  templateUrl: function($element, $attrs, $log) {

    $log.info('determining template to be used');

    if($attrs.useTemplate) {
      return $attrs.useTemplate;
    }

    return 'default.html';
  }
});

Template snippet:

<my-component use-template="hui.html"></my-component>
<my-component use-template="bu.html"></my-component>
<p></p>
<my-component></my-component>

Working example: template injection in an angular 1.5 component

simsibimsiwimsi
  • 249
  • 1
  • 7
  • Aha! So `$attrs` is an injectable service? – Ciel Jul 07 '16 at 18:11
  • 2
    In this case it is.. :-) it's provided by Angular as one of the "locals" - dependencies that are made available by Angular for acertain context which in this case is template injection – simsibimsiwimsi Jul 07 '16 at 18:30
  • So is this possible to do in actual Angular 2 code? Or does it only work in Angular 1? From what I can find, the `templateUrl` property only accepts a `string` in angular 2. – Ciel Jul 13 '16 at 14:59