If I have a AngularDart Component:
#Component(selector: "my-selector",useShadowDom: false,
templateUrl: "packages/test/test.html")
class MyComponent {
MyComponent() {
...
}
...
}
How can I get the templateUrl programmatically?
I want to avoid a constructor with an Element injected. An injected Injector would be OK.
Found the answer:
#Component(selector: "my-selector", useShadowDom: false,
templateUrl: "packages/test/test.html")
class MyComponent {
Injector _injector;
MyComponent(this._injector) {
}
String get url {
DirectiveMap _directiveMap = _injector.get(DirectiveMap);
var tuples = _directiveMap['my-selector'];
//Validate.isTrue(tuples[0].directive is Component);
Component annotation = tuples[0].directive;
//_logger.info("TemplateUrl: ${annotation.templateUrl}");
return annotation.templateUrl;
}
}
Related
Is there a way to access a component used in a different component's dart file?
Component1.dart
#Component(
selector: 'comp1',
templateUrl: 'packages/comp1_package/Component1.html',
cssUrl: 'packages/comp1_package/Component1.css',
useShadowDom: false)
class Component1 {
Component1 get comp1ctrl => this;
Component1() {
}
void testing() {
// WANT TO CALL COMP2 printTest HERE
}
}
Component1.html
<div id="mainTab" class="maincontainer">
<comp2></comp2>
</div>
Component2.dart
#Component(
selector: 'comp2',
templateUrl: 'packages/comp2_package/Component2.html',
cssUrl: 'packages/comp2_package/Component2.css',
useShadowDom: false)
class Component2 {
Component2 get comp2ctrl => this;
Component2() {
}
void printTest() {
print("PRINTING FROM IN COMP2");
}
}
Component2.html
<h3>TEST HEADER</h3>
How can I get access to the Component2 object in Component1.dart and be able to call a method in it? Any help would be appreciated!
My AppComponent sample:
///other imports here
import { ApplyColor } from './../../shared/directives/applycolor';
import { SomeComponent} from './../../components/somecomponent';
#Component({
selector: 'my-app',
directives: [ApplyColor, ROUTER_DIRECTIVES],
providers: [ROUTER_PROVIDERS],
templateUrl: 'myurl.html'
})
#RouteConfig([
//routes here
{ path: '/main', name: 'Main', component: SomeComponent, useAsDefault: true },
])
export class AppComponent {
}
In order to instantiate ApplyColor in SomeComponent
Import ApplyColor
Add to directives: [ApplyColor]
Instantiate with a new keyword
Which is:
import {Component, AfterViewInit, ViewChild} from 'angular2/core';
import { ApplyColor } from './../../shared/directives/applycolor';
#Component({
selector: 'my-selector',
directives: [ApplyColor],
templateUrl: 'app/components/mycomponenturl.html'
})
export class MyComponent implements AfterViewInit {
constructor() { }
ngAfterViewInit() {
var color = new ApplyColor();
color.apply(2);
}
}
How can I instantiate/inject ApplyColor without there 3 steps above?
Directive instances are managed by Angular2. This means that you only need to specify it into the directives attribute. So if ApplyColor is a directive just add it into the directives attribute.
If ApplyColor isn't a directive, you can explicitly instantiate into the provide to the child component using #Input.
In your case, it's a bit particular since you leverage routing. In this case, you need to rely on a shared service. Such service needs to be defined when bootstrapping the application to be able to share a single instance for all components. You can set your instance of ApplyColor into a field of this service. So both component (AppComponent and SomeComponent) can access it.
Define the service
export class SharedService {
color:ApplyColor;
}
Bootstrapping the service
bootstrap(AppComponent, [ SharedService ]);
Set color from AppComponent
#Component({
(...)
})
export class AppComponent {
constructor(private service:SharedService) {
var color = new ApplyColor();
this.service.color = color;
}
}
Get color from SomeComponent
#Component({
(...)
})
export class AppComponent {
constructor(private service:SharedService) {
this.service.color.apply(2);
}
}
I have just started working angular2 but as I can understand:
import ApplyColor => you can't remove that, it required by the compiler to know which class you are referenced to
directives : [ApplyColor] => that means you will use the selector (the one you have defined in applycolor.ts) in the template (app/components/mycomponenturl.html). it is only to know where the component will be in the view.
new ApplyColor => you are creating the object yourself, it is not injected.
To inject your component,
export class MyComponent implements AfterViewInit {
constructor(private color:ApplyColor) { }
ngAfterViewInit() {
this.color.apply(2);
}
}
I hope it helped you ?
I have the following angular2 app with a simple dependency injected and it doesn't work. What am I missing?
Here's the error:
EXCEPTION: Cannot resolve all parameters for 'AppComponent'(?). Make sure that all the parameters are decorated with Inject or have valid type annotations and that 'AppComponent' is decorated with Injectable.
and the code:
import {Component} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
class DataService {
items: Array<any>;
constructor() {
this.items = [
{ name: 'Christoph Burgdorf' },
{ name: 'Pascal Precht' },
{ name: 'thoughtram' }
];
}
getItems() {
return this.items;
}
}
#Component({
selector: 'app',
providers: [DataService],
template: `
<ul>
<li *ngFor="#item of items">{{item.name}}</li>
</ul>
`
})
class AppComponent {
items: Array<any>;
constructor(dataService: DataService) {
this.items = dataService.getItems();
}
}
bootstrap(AppComponent, []);
Can't reproduce. I added your code to a Plunker
https://plnkr.co/edit/0DTjG5?p=preview
and it seems to work fine with or without #Injectable().
It is suggested to always add #Injectable() to services but it is only required when the service has constructor parameters.
.
I'm not using typescript but ES6 and angular2 alpha39 to load a component dynamically. The following code is similar to what I have in my app. What I have noticed is angular2 does not create an instance of DynamicComponentLoader nor ElementRef and inject into the constructor. They are undefined.
How can I do the injection of DynamicComponentLoader using ES6 and angular2 alpha39?
import {Component, View, Inject, DynamicComponentLoader, ElementRef } from 'angular2/angular2'
#Component({
selector: 'dc',
bindings: [ DynamicComponentLoader ]
})
#View({
template: '<b>Some template</b>'
})
class DynamicComponent {}
#Component({
selector: 'my-app'
})
#View({
template: '<div #container></div>'
})
#Inject(DynamicComponentLoader)
#Inject(ElementRef)
export class App {
constructor(
dynamicComponentLoader,
elementRef
) {
dynamicComponentLoader.loadIntoLocation(DynamicComponent, elementRef, 'container');
}
}
If you want to write code in ES7, I think the most concise approach to specify injections at this time is to use static getter for parameters:
import {Component, View, DynamicComponentLoader, ElementRef } from 'angular2/angular2'
#Component({
selector: 'my-app'
})
#View({
template: '<div #container></b>'
})
export class App {
static get parameters() {
return [[DynamicComponentLoader], [ElementRef]];
}
constructor(dynamicComponentLoader, elementRef) {
dynamicComponentLoader.loadIntoLocation(DynamicComponent, elementRef, 'container');
}
}
See this plunker
If you want to write code in ES6, which doesn't support decorators, you must also use static getter for annotations property. In this case you must import ComponentMetadata and ViewMetadata instead of Component and View For example:
import {ComponentMetadata, ViewMetadata, DynamicComponentLoader, ElementRef } from 'angular2/angular2';
export class App {
static get annotations() {
return [
new ComponentMetadata({
selector: 'app'
}),
new ViewMetadata({
template: '<div #container></b>'
})
];
}
static get parameters() {
return [[DynamicComponentLoader],[ElementRef]];
}
constructor(dynamicComponentLoader, elementRef) {
dynamicComponentLoader.loadIntoLocation(DynamicComponent, elementRef, 'container');
}
}
See this plunker
I would like to dynamically build a component tree basing on some information received from AJAX calls.
How to programmatically add a component to the DOM from inside of other component? I have <outer-comp> and I would like, basing on some logic, insert an <inner-comp>. The following code just inserts the elements <inner-comp></inner-comp> to the DOM, and not actual <inner-comp> representation.
#NgComponent(
selector: 'outer-comp',
templateUrl: 'view/outer_component.html',
cssUrl: 'view/outer_component.css',
publishAs: 'outer'
)
class AppComponent extends NgShadowRootAware {
void onShadowRoot(ShadowRoot shadowRoot) {
DivElement inner = shadowRoot.querySelector("#inner");
inner.appendHtml("<inner-comp></inner-comp>");
}
}
Update:
I managed to render the inner component correctly in the following way, but I'm still not sure if this is the proper way:
class AppComponent extends NgShadowRootAware {
Compiler compiler;
Injector injector;
AppComponent(this.compiler, this.injector);
void onShadowRoot(ShadowRoot shadowRoot) {
DivElement inner = shadowRoot.querySelector("#inner");
inner.appendHtml("<inner-comp></inner-comp>");
BlockFactory template = compiler(inner.nodes);
var block = template(injector);
inner.replaceWith(block.elements[0]);
}
}
The API has changed in AngularDart 0.9.9:
BlockFactory now is ViewFactory
scope.$new now seems to be scope.createChild(scope.context)
injector.createChild(modules) now requires a list of modules (instead of a single one)
AngularDart 0.10.0 introduces these changes:
NgShadowRootAware not is ShadowRootAware
..value() now is ..bind(., toValue: .)
So the code of pavelgj now looks like so:
class AppComponent extends ShadowRootAware {
Compiler compiler;
Injector injector;
Scope scope;
DirectiveMap directives;
AppComponent(this.compiler, this.injector, this.scope, this.directives);
void onShadowRoot(ShadowRoot shadowRoot) {
DivElement inner = shadowRoot.querySelector("#inner");
inner.appendHtml("<inner-comp></inner-comp>");
ViewFactory template = compiler([inner], directives);
Scope childScope = scope.createChild(scope.context);
Injector childInjector =
injector.createChild([new Module()..bind(Scope, toValue: childScope)]);
template(childInjector, [inner]);
}
}
This would be a proper use of the block API.
class AppComponent extends NgShadowRootAware {
Compiler compiler;
Injector injector;
Scope scope;
DirectiveMap directives;
AppComponent(this.compiler, this.injector, this.scope, this.directives);
void onShadowRoot(ShadowRoot shadowRoot) {
DivElement inner = shadowRoot.querySelector("#inner");
inner.appendHtml("<inner-comp></inner-comp>");
BlockFactory template = compiler([inner], directives);
Scope childScope = scope.$new();
Injector childInjector =
injector.createChild(new Module()..value(Scope, childScope));
template(childInjector, [inner]);
}
}
Also, if you ever need to recompile the inner template make sure you do childScope.$destroy() on the previous childScope.
The above code samples on longer work because of changes in the Angular Dart library. Specifically ViewFactory.call which no longer takes an injector but takes a Scope and a DirectiveInjector. I've tried adapting what's above and I get very close. The component shows up but none of the bindings are replaced (I see {{cmp.value}} for example.
Here's the code I'm using. I think the issue here is that DirectiveInjector is coming in as null.
void main() {
IBMModule module = new IBMModule();
AngularModule angularModule = new AngularModule();
Injector injector = applicationFactory()
.addModule(module)
.run();
AppComponent appComponent = injector.get(AppComponent);
appComponent.addElement("<brazos-input-string label='test'/>");
}
#Injectable()
class AppComponent {
NodeValidator validator;
Compiler _compiler;
DirectiveInjector _injector;
DirectiveMap _directiveMap;
NodeTreeSanitizer _nodeTreeSanitizer;
Scope _scope;
AppComponent(this._injector, this._compiler, this._directiveMap, this._scope, this._nodeTreeSanitizer) {
validator = new NodeValidatorBuilder.common()
..allowCustomElement("BRAZOS-INPUT-STRING")
..allowHtml5()
..allowTemplating();
}
void addElement(String elementHTML) {
DivElement container = querySelector("#container");
DivElement inner = new DivElement();
inner.setInnerHtml(elementHTML, validator: validator);
ViewFactory viewFactory = _compiler.call([inner], _directiveMap);
Scope childScope = _scope.createChild(new PrototypeMap(_scope.context));
if (_injector == null) {
print("injector is null");
}
View newView = viewFactory.call(childScope, _injector);
container.append(inner);
newView.nodes.forEach((node) => inner.append(node));
}
}
class IBMModule extends Module {
IBMModule() {
bind(BrazosInputStringComponent);
bind(BrazosTextAreaComponent);
bind(BrazosButtonComponent);
bind(ProcessDataProvider, toImplementation: ActivitiDataProvider);
bind(AppComponent);
}
}
I did finally get this to work but was not happy with having to add a timer:
#Injectable()
class AppComponent{
NodeValidator validator;
Compiler _compiler;
DirectiveInjector _directiveInjector;
DirectiveMap _directiveMap;
NodeTreeSanitizer _nodeTreeSanitizer;
Injector _appInjector;
Scope _scope;
AppComponent(this._directiveInjector, this._compiler, this._directiveMap, this._nodeTreeSanitizer, this._appInjector, this._scope) {
validator = new MyValidator();
}
void addElement(String id, String elementHTML) {
DivElement container = querySelector(id);
DivElement inner = new DivElement();
container.append(inner);
Element element = new Element.html(elementHTML, validator: validator);
ViewFactory viewFactory = _compiler.call([element], _directiveMap);
if (_scope != null) {
Scope childScope = _scope.createProtoChild();
View newView = viewFactory.call(childScope, _directiveInjector);
newView.nodes.forEach((node) => inner.append(node));
Timer.run(() => childScope.apply());
} else {
print("scope is null");
}
}
}
EDIT
The package http://pub.dartlang.org/packages/bwu_angular contains this decorator/directive as bwu-safe-html
------
I use a custom directive for that
#NgDirective(
selector: '[my-bind-html]'
)
class MyBindHtmlDirective {
static dom.NodeValidator validator;
dom.Element _element;
Compiler _compiler;
Injector _injector;
DirectiveMap _directiveMap;
MyBindHtmlDirective(this._element, this._injector, this._compiler, this._directiveMap) {
validator = new dom.NodeValidatorBuilder.common()
..allowHtml5()
..allowImages();
}
#NgOneWay('my-bind-html')
set value(value) {
if(value == null) {
_element.nodes.clear();
return;
}
_element.setInnerHtml((value == null ? '' : value.toString()),
validator: validator);
if(value != null) {
_compiler(_element.childNodes, _directiveMap)(_injector, _element.childNodes);
}
}
}
It can be used like
my-bind-html='ctrl.somehtml'
Angular issue
I created an issue to include this functionality into Angulars ng-bind-html https://github.com/angular/angular.dart/issues/742 (declined)