I used to build applicaitons with angular1, there was possible to have directives on the allready loaded DOM elements, it was like you have the main component (app), wich is build from loaded html and then inside you can load directives from ether loaded html or load it from URL.
Howewer in angular2 it seems that to bootsrap application I have to use component which requires me to have template/templateURL, which I think is not nessesery since I don't want to load seperatly menues and other common stuff, I would rather do that on server level then laoding it seperatly. Does anyone knows how could I achive this in angular2?
In Angular2 you need to bootstrap a component and a component needs to have a view. Directives can't be bootstrapped. Directives can't be added or removed dynamically, they are only applied where static HTML in a components view matches their selector.
To me it sounds that for your use case Angular1 is the better fit.
You can have directives but as #Günter Zöchbauer mentioned before you will need to bootstrap a component..
Change detector are created when a component is first instantiated. Here is an exaple for ng2 Directive from Angular documentation :
class Greeter {
greet(name:string) {
return 'Hello ' + name + '!';
}
}
#Directive({
selector: 'needs-greeter'
})
class NeedsGreeter {
greeter:Greeter;
constructor(greeter:Greeter) {
this.greeter = greeter;
}
}
#Component({
selector: 'greet',
viewProviders: [
Greeter
],
template: `<needs-greeter></needs-greeter>`,
directives: [NeedsGreeter]
})
class HelloWorld {
}
See for more details: https://angular.io/docs/ts/latest/api/core/index/ComponentMetadata-class.html#!#constructor
But keep in mind that:
Each Angular component requires a single #Component annotation. The
#Component annotation specifies when a component is instantiated, and
which properties and hostListeners it binds to.
When a component is instantiated, Angular
creates a shadow DOM for the component.
loads the selected template into the shadow DOM.
creates all the injectable objects configured with providers and viewProviders.
Related
In Angular JS I would want to get a component controller instance manually, that ideally is achieved through the require binding in component. I have a requirement where in I am outside the Angular JS component environment and I cannot use require.
// Sample require binding
module.component('superHeros', {
template: `
<h2>
Super Heros Component
</h2>
`,
require: {
parentCtrl: '^heroList'
},
controller: superHerosControler
});
Though I thought this could be achieved with $injector of angular but does not seem to work (throws Unknown provider).
So I want to do this
require: {
parentCtrl: '^heroList'
}
like this this.requiredController = $injector.get(<componentName>);
I understand that this works perfectly for Services or factories. Is there a way to achieve this or a workaround that can essentially get me an component controller instance ?
Plunker here
Any help would be appreciated. Thanks
I am running UI Router with angular 4.x. Below code is not rendering anything and I did not get any error message too. But when I changed to $default as a view name, then I am getting the page.Please suggest me.
<app-root>
<ui-view name='main'></ui-view>
</app-root>
Below is the angular State Definition,
export const appState = {
name: 'app',
views : {
main : {
component: AppComponent
}
}
};
When Angular application bootstrap's, it wipes out the inner content of app-root(root) component. What ever you put inside root component will appear until Angular application bootstrap. Generally this place has been used to add Splash screen, loader to show initial loading.
To see your ui-view to replace via ui-router configuration, you should add ui-view inside app-root component HTML.
#Component({
selector: 'app-root',
template: `<ui-view name='main'></ui-view>`
})
export AppRootComponent {
}
In Angular 1.5, the .component() was introduced and seems to be considered good practice. I am currently migrating to ui-router 1.0 which allows (and recommends) routing to components.
So I refactored a controller/template route to a component router which works great: The component "example" gets injected to the ui-view container in its own DOM node:
However that breaks my layout (using angular material). The workaround i used is simply copying and using css classes angular material uses for layout on the inserted node. However, i consider this a "hacky" solution: What if angular material changes it's layout module for example?
A better solution would be to add layout attributes or even a css class to the "example" element. But how can i do that? Is that even possible?
I think this question is related to the following, where i provided a workaround for the specific problem. But I am interested in a more generic solution: Using angular component breaks material layout
I faced the same problem in my Angular 1.5 project where I also use angular-ui-router and Angular Material components. One possible solution is simply using string-based binding where you bind two attributes into your component: layout and flex. You can give the values for these two bound attributes (usually 'row' and '100' respecively) as you specify your ui-routing state in the following way:
var exampleState = {
name: 'examplestate',
url: '/', // change this to your desired path
component: 'example',
resolve: {
layout: function () {
return 'row'; // you can change this value depending on your UI requirements
},
flex: function () {
return '100'; // you can change this value depending on your UI requirements
}
}
};
In the config method of your module, you define your exampleState in the standard way:
$stateProvider.state(exampleState);
Finally, in the component definition, as you define your bindings, you add these two attributes to the list of bound attributes.
Example in Typescript:
export class ExampleComponent implements ng.IComponentOptions {
public bindings: any;
public controller: any;
public template: string;
constructor() {
this.bindings = {
layout: '#',
flex: '#'
};
this.controller = ExampleComponentController;
this.template = ExampleViewTemplate;
}
}
You can add this component to your angular module in the usual way:
.component('example', new ExampleComponent());
This will result in the following HTML element in the DOM (created by angular-ui-router automatically):
<example layout="row" flex="100" class="ng-scope ng-isolate-scope layout-row flex-100">
So in this way you don't need to insert any manual CSS styling in your code. You are using the standard Angular Material attributes on your component-element.
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?
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
I started to migrate one of my Angular 1 directives to the Angular 2 component.
The directive I am currently on has the controllerAs: 'ctrl' property and the directive's template uses 'ctrl.' prefix when it access properties.
Looking at the official ComponentMetadata doc I do not see any properties that can be used instead of this one.
There is no equivalent to controllerAs in Angular 2. For example, given this controller class and template:
#Component({
selector: 'component-a',
template: `<div class="component-a">
<div class="counter" (click)="increment()">Component A: {{counter}}</div>
</div>`
})
export class ComponentA {
counter = 0;
increment() {
this.counter += 1;
}
}
In the method increment(), this is bounded to the controller instance of that particular component itself. In the template, the counter can be accessed via {{counter}}.
As we can see there is no mechanism to name the controller because we can already access it using the default functionality.
You can either think that the controllerAs mechanism has been integrated in the default component functionality of Angular 2, or that functionality has been removed as its no longer needed, depending on how you look at it.