How to render angular component from node express - javascript

I am hitting the url with GET request: http://localhost:5029/article/16
and inside node I am checking for article/16 like this:
req.url.split("article/")[1];
and trying to render the component using handlebars like this:
res.render('header', {layout: 'header.component.html'});
I see the component rendered but css styles are not loaded.
my component.ts:
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit { }
component html:
<div class="header">
<li class="menu-item">
<a [routerLinkActive]="['active']" routerLink="/home">Home</a>
</li>
<li class="menu-item">
<div class="dropdown">
<button type="button" class="btn dropdown-toggle" data-toggle="dropdown" [routerLinkActive]="['active']">Tutorials</button>
</div>
</li>
</div>
Handlebars setup:
app.engine(
"hbs",
hbs({
extname: "html",
defaultView: "default",
layoutsDir: "../src/app/components/header/",
partialsDir: "../src/app/components/"
})
);
app.set("view engine", "hbs");
Similar requirement has been achieved here in RiotJS:
https://riot.js.org/api/#-riotrendertagname-opts
// render "my-tag" to html
var mytag = require('my-tag')
riot.render(mytag, { foo: 'bar' })

Not sure why you want to render a specific Angular component from node express, but I will try to explain what components are and why you can't directly load them and a better alternative on same.
As you might be already aware that, Decorators makes a specific block of code distinguish between service, component, directive etc.
Decorators are functions that are invoked with a prefixed # symbol and immediately followed by a class, parameter, method or property.
e.g
#Component({
})
export class TestComponent {}
#Injectable()
export class TestService {}
Notice the () on #Component or in #Injectable.
(These decorators need to register in some or other ngModule())
This means that the #Component is called once JavaScript encounters #Component(). In turn, this means that there must be a Component function somewhere that returns a function matching one of the decorator signatures outlined above. This is an example of the decorator factory pattern.
During Bootstrap of the application, all these functions are invoked, and compiler converts ts code in vanilla js, which is executed in the browser.
Coming back to the question what you are trying to do out there is trying to load the component without processing it with TS compiler, (Under the hood, angular is doing lots of work for us when we build or compile the application)
Possible Solution, if you don't want to serve all the components with help of angular, you can use Angular Element (Angular elements are Angular components packaged as custom elements, a web standard for defining new HTML elements in a framework-agnostic way), which can be rendered without the need of angular directly on any page.
To learn more about same visit https://angular.io/guide/elements
Hope this helps all the best!

Related

URL in string interpolation not clickable : Angular 8 [duplicate]

I am writing an Angular application and I have an HTML response I want to display.
How do I do that? If I simply use the binding syntax {{myVal}} it encodes all HTML characters (of course).
I need somehow to bind the innerHTML of a div to the variable value.
The correct syntax is the following:
<div [innerHTML]="theHtmlString"></div>
Documentation Reference
Angular 2.0.0 and Angular 4.0.0 final
For safe content just
<div [innerHTML]="myVal"></div>
DOMSanitizer
Potential unsafe HTML needs to be explicitly marked as trusted using Angulars DOM sanitizer so doesn't strip potentially unsafe parts of the content
<div [innerHTML]="myVal | safeHtml"></div>
with a pipe like
#Pipe({name: 'safeHtml'})
export class Safe {
constructor(private sanitizer:DomSanitizer){}
transform(style) {
return this.sanitizer.bypassSecurityTrustHtml(style);
//return this.sanitizer.bypassSecurityTrustStyle(style);
// return this.sanitizer.bypassSecurityTrustXxx(style); - see docs
}
}
See also In RC.1 some styles can't be added using binding syntax
And docs: https://angular.io/api/platform-browser/DomSanitizer
Security warning
Trusting user added HTML may pose a security risk. The before mentioned docs state:
Calling any of the bypassSecurityTrust... APIs disables Angular's built-in sanitization for the value passed in. Carefully check and audit all values and code paths going into this call. Make sure any user data is appropriately escaped for this security context. For more detail, see the Security Guide.
Angular markup
Something like
class FooComponent {
bar = 'bar';
foo = `<div>{{bar}}</div>
<my-comp></my-comp>
<input [(ngModel)]="bar">`;
with
<div [innerHTML]="foo"></div>
won't cause Angular to process anything Angular-specific in foo.
Angular replaces Angular specific markup at build time with generated code. Markup added at runtime won't be processed by Angular.
To add HTML that contains Angular-specific markup (property or value binding, components, directives, pipes, ...) it is required to add the dynamic module and compile components at runtime.
This answer provides more details How can I use/create dynamic template to compile dynamic Component with Angular 2.0?
[innerHtml] is great option in most cases, but it fails with really large strings or when you need hard-coded styling in html.
I would like to share other approach:
All you need to do, is to create a div in your html file and give it some id:
<div #dataContainer></div>
Then, in your Angular 2 component, create reference to this object (TypeScript here):
import { Component, ViewChild, ElementRef } from '#angular/core';
#Component({
templateUrl: "some html file"
})
export class MainPageComponent {
#ViewChild('dataContainer') dataContainer: ElementRef;
loadData(data) {
this.dataContainer.nativeElement.innerHTML = data;
}
}
Then simply use loadData function to append some text to html element.
It's just a way that you would do it using native javascript, but in Angular environment. I don't recommend it, because makes code more messy, but sometimes there is no other option.
See also Angular 2 - innerHTML styling
On angular2#2.0.0-alpha.44:
Html-Binding will not work when using an {{interpolation}}, use an "Expression" instead:
invalid
<p [innerHTML]="{{item.anleser}}"></p>
-> throws an error (Interpolation instead of expected Expression)
correct
<p [innerHTML]="item.anleser"></p>
-> this is the correct way.
you may add additional elements to the expression, like:
<p [innerHTML]="'<b>'+item.anleser+'</b>'"></p>
hint
HTML added using [innerHTML] (or added dynamically by other means like element.appenChild() or similar) won't be processed by Angular in any way except sanitization for security purposed.
Such things work only when the HTML is added statically to a components template. If you need this, you can create a component at runtime like explained in How can I use/create dynamic template to compile dynamic Component with Angular 2.0?
Using [innerHTML] directly without using Angular's DOM sanitizer is not an option if it contains user-created content. The safeHtml pipe suggested by #GünterZöchbauer in his answer is one way of sanitizing the content. The following directive is another one:
import { Directive, ElementRef, Input, OnChanges, Sanitizer, SecurityContext,
SimpleChanges } from '#angular/core';
// Sets the element's innerHTML to a sanitized version of [safeHtml]
#Directive({ selector: '[safeHtml]' })
export class HtmlDirective implements OnChanges {
#Input() safeHtml: string;
constructor(private elementRef: ElementRef, private sanitizer: Sanitizer) {}
ngOnChanges(changes: SimpleChanges): any {
if ('safeHtml' in changes) {
this.elementRef.nativeElement.innerHTML =
this.sanitizer.sanitize(SecurityContext.HTML, this.safeHtml);
}
}
}
To be used
<div [safeHtml]="myVal"></div>
Please refer to other answers that are more up-to-date.
This works for me: <div innerHTML = "{{ myVal }}"></div> (Angular2, Alpha 33)
According to another SO: Inserting HTML from server into DOM with angular2 (general DOM manipulation in Angular2), "inner-html" is equivalent to "ng-bind-html" in Angular 1.X
Just to make for a complete answer, if your HTML content is in a component variable, you could also use:
<div [innerHTML]=componentVariableThatHasTheHtml></div>
Short answer was provided here already: use <div [innerHTML]="yourHtml"> binding.
However the rest of the advices mentioned here might be misleading. Angular has a built-in sanitizing mechanism when you bind to properties like that. Since Angular is not a dedicated sanitizing library, it is overzealous towards suspicious content to not take any risks. For example, it sanitizes all SVG content into empty string.
You might hear advices to "sanitize" your content by using DomSanitizer to mark content as safe with bypassSecurityTrustXXX methods. There are also suggestions to use pipe to do that and that pipe is often called safeHtml.
All of this is misleading because it actually bypasses sanitizing, not sanitizing your content. This could be a security concern because if you ever do this on user provided content or on anything that you are not sure about — you open yourself up for a malicious code attacks.
If Angular removes something that you need by its built-in sanitization — what you can do instead of disabling it is delegate actual sanitization to a dedicated library that is good at that task. For example — DOMPurify.
I've made a wrapper library for it so it could be easily used with Angular:
https://github.com/TinkoffCreditSystems/ng-dompurify
It also has a pipe to declaratively sanitize HTML:
<div [innerHtml]="value | dompurify"></div>
The difference to pipes suggested here is that it actually does do the sanitization through DOMPurify and therefore work for SVG.
EDIT: Angular no longer sanitizes CSS as of Ivy renderer so below info, kept for history sake, is irrelevant:
One thing to keep in mind is DOMPurify is great for sanitizing HTML/SVG, but not CSS. So you can provider Angular's CSS sanitizer to handle CSS:
import {NgModule, ɵ_sanitizeStyle} from '#angular/core';
import {SANITIZE_STYLE} from '#tinkoff/ng-dompurify';
#NgModule({
// ...
providers: [
{
provide: SANITIZE_STYLE,
useValue: ɵ_sanitizeStyle,
},
],
// ...
})
export class AppModule {}
It's internal — hence ɵ prefix, but this is how Angular team use it across their own packages as well anyway. That library also works for Angular Universal and server side rendering environment.
I apologize if I am missing the point here, but I would like to recommend a different approach:
I think it's better to return raw data from your server side application and bind it to a template on the client side. This makes for more nimble requests since you're only returning json from your server.
To me it doesn't seem like it makes sense to use Angular if all you're doing is fetching html from the server and injecting it "as is" into the DOM.
I know Angular 1.x has an html binding, but I have not seen a counterpart in Angular 2.0 yet. They might add it later though. Anyway, I would still consider a data api for your Angular 2.0 app.
I have a few samples here with some simple data binding if you are interested: http://www.syntaxsuccess.com/viewarticle/angular-2.0-examples
Just simply use [innerHTML] attribute in your HTML, something like this below:
<div [innerHTML]="myVal"></div>
Ever had properties in your component that contain some html markup or
entities that you need to display in your template? The traditional
interpolation won't work, but the innerHTML property binding comes to
the rescue.
Using {{myVal}} Does NOT work as expected! This won't pick up the HTML tags like <p>, <strong> etc and pass it only as strings...
Imagine you have this code in your component:
const myVal:string ='<strong>Stackoverflow</strong> is <em>helpful!</em>'
If you use {{myVal}}, you will get this in the view:
<strong>Stackoverflow</strong> is <em>helpful!</em>
but using [innerHTML]="myVal"makes the result as expected like this:
Stackoverflow is helpful!
<div [innerHTML]="HtmlPrint"></div><br>
The innerHtml is a property of HTML-Elements, which allows you to set it’s html-content programatically. There is also a innerText property which defines the content as plain text.
The [attributeName]="value" box bracket , surrounding the attribute defines an Angular input-binding. That means, that the value of the property (in your case innerHtml) is bound to the given expression, when the expression-result changes, the property value changes too.
So basically [innerHtml] allows you to bind and dynamically change the html-conent of the given HTML-Element.
You can apply multiple pipe for style, link and HTML as following in .html
<div [innerHTML]="announcementContent | safeUrl| safeHtml">
</div>
and in .ts pipe for 'URL' sanitizer
import { Component, Pipe, PipeTransform } from '#angular/core';
import { DomSanitizer } from '#angular/platform-browser';
#Pipe({ name: 'safeUrl' })
export class SafeUrlPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) {}
transform(url) {
return this.sanitizer.bypassSecurityTrustResourceUrl(url);
}
}
pipe for 'HTML' sanitizer
import { Component, Pipe, PipeTransform } from '#angular/core';
import { DomSanitizer } from '#angular/platform-browser';
#Pipe({
name: 'safeHtml'
})
export class SafeHtmlPipe implements PipeTransform {
constructor(private sanitized: DomSanitizer) {}
transform(value) {
return this.sanitized.bypassSecurityTrustHtml(value);
}
}
this will apply both without disturbing any style and link click event
In Angular 2 you can do 3 types of bindings:
[property]="expression" -> Any html property can link to an
expression. In this case, if expression changes property will update,
but this doesn't work the other way.
(event)="expression" -> When event activates execute expression.
[(ngModel)]="property" -> Binds the property from js (or ts) to html. Any update on this property will be noticeable everywhere.
An expression can be a value, an attribute or a method. For example: '4', 'controller.var', 'getValue()'
Example here
We can always pass html content to innerHTML property to render html dynamic content but that dynamic html content can be infected or malicious also. So before passing dynamic content to innerHTML we should always make sure the content is sanitized (using DOMSanitizer) so that we can escaped all malicious content.
Try below pipe:
import { Pipe, PipeTransform } from "#angular/core";
import { DomSanitizer } from "#angular/platform-browser";
#Pipe({name: 'safeHtml'})
export class SafeHtmlPipe implements PipeTransform {
constructor(private sanitized: DomSanitizer) {
}
transform(value: string) {
return this.sanitized.bypassSecurityTrustHtml(value);
}
}
Usage:
<div [innerHTML]="content | safeHtml"></div>
You can also bind the angular component class properties with template using DOM property binding.
Example: <div [innerHTML]="theHtmlString"></div>
Using canonical form like below:
<div bind-innerHTML="theHtmlString"></div>
Angular Documentation: https://angular.io/guide/template-syntax#property-binding-property
See working stackblitz example here
You can use the Following two ways.
<div [innerHTML]="myVal"></div>
or
<div innerHTML="{{myVal}}"></div>
Angular 2+ supports an [innerHTML] property binding that will render HTML. If you were to otherwise use interpolation, it would be treated as a string.
Into .html file
<div [innerHTML]="theHtmlString"></div>
Into .ts file
theHtmlString:String = "enter your html codes here";
I have build below library which will help to rebind html formatted bindings.
Please find below steps to use this library. This library basically allows to inject JIT compilter code in AOT
Install library using
npm i angular-html-recompile
Add below code in app.component.html file
<pk-angular-html-recompile *ngIf="template !== ''"
[stringTemplate]="template"
[data]="dataObject">
</pk-angular-html-recompile>
Use below code in app.component.ts file
import { Component, OnInit, ViewChild } from '#angular/core';
import { AngularHtmlRecompileComponent, AngularHtmlRecompileService } from 'angular-html-recompile';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
#ViewChild(AngularHtmlRecompileComponent, { static: true }) comp !: AngularHtmlRecompileComponent;
constructor(
private angularHtmlRecompileService: AngularHtmlRecompileService) {
}
public dataObject: any;
public template = `<div class="login-wrapper" fxLayout="row" fxLayoutAlign="center center">
<mat-card class="box">
<mat-card-header>
<mat-card-title>Register</mat-card-title>
</mat-card-header>
<form class="example-form">
<mat-card-content>
<mat-form-field class="example-full-width">
<input matInput placeholder="Username" [value]="Username" (keydown)="onControlEvent($event,'Username')">
</mat-form-field>
<mat-form-field class="example-full-width">
<input matInput placeholder="Email" [value]="Email" (keydown)="onControlEvent($event,'Email')">
</mat-form-field>
<mat-form-field *ngIf="isShow" class="example-full-width">
<input matInput placeholder="Password" [value]="Password" (keydown)="onControlEvent($event,'Password')">
</mat-form-field>
<mat-form-field class="example-full-width">
<mat-label>Choose a role...</mat-label>
<mat-select (selectionChange)="onControlEvent($event, 'selectedValue')">
<mat-option [value]="roles" *ngFor="let roles of Roles">{{roles}}
</mat-option>
</mat-select>
</mat-form-field>
</mat-card-content>
<button mat-stroked-button color="accent" class="btn-block" (click)="buttomClickEvent('submit')" >Register</button>
</form>
</mat-card>
</div>`;
ngOnInit(): void {
this.angularHtmlRecompileService.sharedData.subscribe((respose: any) => {
if (respose) {
switch (respose.key) {
case `Username`:
// Call any method on change of name
break;
case `Password`:
//Update password from main component
this.comp[`cmpRef`].instance['Password'] = "Karthik";
break;
case `submit`:
//Get reference of all parameters on submit click
//1. respose.data OR
//use this.comp[`cmpRef`].instance
break;
default:
break;
}
}
});
this.prepareData();
}
prepareData() {
//Prepare data in following format only for easy binding
//Template preparation and data preparation can be done once data received from service
// AngularHtmlRecompileComponent will not be rendered until you pass data
this.dataObject = [
{ key: 'Username', value: 'Pranay' },
{ key: 'Email', value: 'abc#test.com', },
{ key: 'Password', value: 'test123', },
{ key: 'Roles', value: ['Admin', 'Author', 'Reader'] },
{ key: 'isShow', value: this.updateView() }
];
}
updateView() {
//Write down logic before rendering to UI to work ngIf directive
return true;
}
}
Add module into app.module.ts file
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { AppComponent } from './app.component';
import { AngularHtmlRecompileModule } from "angular-html-recompile";
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AngularHtmlRecompileModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
This library supports basic html, Angular material, flex layouts.
To use this features install below dependencies
npm i -s #angular/material #angular/flex-layout
The way to dynamically add elements to DOM, as explained on Angular 2 doc, is by using ViewContainerRef class from #Angular/core.
What you have to do is to declare a directive that will implement ViewContainerRef and act like a placeholder on your DOM.
Directive
import { Directive, ViewContainerRef } from '#angular/core';
#Directive({
selector: '[appInject]'
})
export class InjectDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
Then, in the template where you want to inject the component:
HTML
<div class="where_you_want_to_inject">
<ng-template appInject></ng-template>
</div>
Then, from the injected component code, you will inject the component containing the HTML you want:
import { Component, OnInit, ViewChild, ComponentFactoryResolver } from '#angular/core';
import { InjectDirective } from '../inject.directive';
import { InjectedComponent } from '../injected/injected.component';
#Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.css']
})
export class ParentComponent implements OnInit {
#ViewChild(InjectDirective) injectComp: InjectDirective;
constructor(private _componentFactoryResolver: ComponentFactoryResolver) {
}
ngOnInit() {
}
public addComp() {
const componentFactory = this._componentFactoryResolver.resolveComponentFactory(InjectedComponent);
const viewContainerRef = this.injectComp.viewContainerRef;
const componentRef = viewContainerRef.createComponent(componentFactory);
}
public removeComp() {
const componentFactory = this._componentFactoryResolver.resolveComponentFactory(InjectedComponent);
const viewContainerRef = this.injectComp.viewContainerRef;
const componentRef = viewContainerRef.remove();
}
}
I added a fully working demo app on Angular 2 dynamically add component to DOM demo
You can use several approaches to achieve the solution. As already said in the approved answer, you can use:
<div [innerHTML]="myVal"></div>
depending on what you are trying to achieve, you can also try other things like javascript DOM (not recommended, DOM operations are slow):
Presentation
<div id="test"></test>
Component
var p = document.getElementsById("test");
p.outerHTML = myVal;
Property Binding
Javascript DOM Outer HTML
If you want that in Angular 2 or Angular 4 and also want to keep inline CSS then you can use
<div [innerHTML]="theHtmlString | keepHtml"></div>
Working in Angular v2.1.1
<div [innerHTML]="variable or htmlString">
</div>
Just to post a little addition to all the great answers so far: If you are using [innerHTML] to render Angular components and are bummed about it not working like me, have a look at the ngx-dynamic-hooks library that I wrote to address this very issue.
With it, you can load components from dynamic strings/html without compromising security. It actually uses Angular's DOMSanitizer just like [innerHTML] does as well, but retains the ability to load components (in a safe manner).
See it in action in this Stackblitz.
If you have templates in your angular (or whatever framework) application, and you return HTML templates from your backend through a HTTP request/response, you are mixing up templates between the frontend and the backend.
Why not just leave the templating stuff either in the frontend (i would suggest that), or in the backend (pretty intransparent imo)?
And if you keep templates in the frontend, why not just respond with JSON for requests to the backend. You do not even have to implement a RESTful structure, but keeping templates on one side makes your code more transparent.
This will pay back when someone else has to cope with your code (or even you yourself are re-entering your own code after a while)!
If you do it right, you will have small components with small templates, and best of all, if your code is imba, someone who doesn't know coding languages will be able to understand your templates and your logic! So additionally, keep your functions/methods as small you can.
You will eventually find out that maintaining, refactoring, reviewing, and adding features will be much easier compared to large functions/methods/classes and mixing up templating and logic between the frontend and the backend - and keep as much of the logic in the backend if your frontend needs to be more flexible (e.g. writing an android frontend or switching to a different frontend framework).
Philosophy, man :)
p.s.: you do not have to implement 100% clean code, because it is very expensive - especially if you have to motivate team members ;)
but: you should find a good balance between an approach to cleaner code and what you have (maybe it is already pretty clean)
check the book if you can and let it enter your soul:
https://de.wikipedia.org/wiki/Clean_Code

Externally pass values to an Angular application

I'm thinking of separating our MVC-based website's front-end into few components and I was thinking about using Angular for that purpose (e.g. creating cart application that I can include to my view afterwards).
Problem is that I need to pass few variables into that application and I'm wondering how to do that safely and professionally. I was thinking about something like:
I'm going to ng build --prod --aot
I inject all my scripts to my view
I inject variables passed to view to my app
... and "code" representation for my thoughts:
Controller:
public function viewAction()
{
$this->view->addCss('angular/app/styles.css'); // adds styles for cart app
$this->view->addJS('angular/app/scripts.js'); // adds scripts for cart app
$this->view->setJSVariable('idCustomer', 555); // sets global var idCustomer
}
View:
<!-- bunch of HTML for view ... -->
<script>
// CartApp would be an angular app.
CartApp.use([
idCustomer
]);
</script>
So my question is... would it be possible (and would it be a good solution) to get the CartApp as an object and then make some function (like use in above example) that would set/pass the data? (let's say to some globally provided service/component or anything else). Or is there any other way to do this? Like taking the variables from inside the application from the window object? (as they're going to be bound to the window anyways). Thank you.
So I was going to suggest using input bindings... I've done that before in AngularJS but was surprised to find that using input bindings on the root component isn't supported yet. You can have fun reading this giant issue: https://github.com/angular/angular/issues/1858
The best post I saw there was Rob Wormald's which had a couple of suggestions:
to the initial question, if you really want to grab data from the
root, that's possible via
https://plnkr.co/edit/nOQuXE8hMkhakDNCNR9u?p=preview - note that it's not an input (because there's no angular context outside of it to do the input...) - its a simple string attribute
which you'd need to parse yourself.
ideally though, you'd do as
#robtown suggested and actually pass the data in javascript, rather
than passing it as a DOM string and retrieving / parsing it yourself
(which has never really been a supported case in angular, despite the
explicit-warned-against usage of ng-init in angular1 to accomplish
this) - see https://plnkr.co/edit/PoSd07IBvYm1EzeA2yJR?p=preview for a
simple, testable example of how to do this.
So the 2 good options I saw were:
Add normal HTML attributes to the root component:
<app-root appData='important stuff'></app-root>
and use ElementRef to fetch them:
#Component({
selector: 'app-root'
})
export class AppComponent {
constructor(el: ElementRef) {
console.log(el.nativeElement.getAttribute('appData'));
}
}
Would probably work best if you are just dealing with strings or config flags. If you are passing JSON, you will need to manually parse it.
Have the server render the data as JavaScript and import it in your app:
Have the server render something like this to a script tag or JS file that is loaded before Angular is bootstrapped:
window.APP_DATA = { ids: [1, 2, 3] }
Tell your NgModule about it using a provider:
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppComponent } from './app.component';
#NgModule({
providers: [{ provide: 'AppData', useValue: (<any> window).APP_DATA }],
bootstrap: [AppComponent]
})
export class AppModule { }
And then use it as a normal Angular service:
import {Component, Inject} from '#angular/core';
#Component({
selector: 'app-root'
})
export class AppComponent {
constructor(#Inject('AppData') appData) {
console.log(appData.ids);
}
}

Angular 2: can custom component handle its own events?

I built an Angular 2 component and I have a doubt: is there a standard that defines if the events triggered inside the component should be broadcasted (vie #Output, for example) or the component can handle them itself?
Let me show some code:
the component may be called content-detail and the template is:
<ion-card (click)="navigate()">
<h3>{{ rawcontent.name }}</h3>
</ion-card>
the .ts has a navigate() method, and inside it I call some route (in my case it's an Ionic 2 app, which uses NavController, but it could be anything else).
#Component({
selector: 'content-detail',
templateUrl: 'content-detail.html'
})
export class ContentDataComponent {
navigate() {
//do seomething, like navigate to another page
}
}
Is this a good practice?
Or the best is that the component use EventEmitter to emit it to it's parent (as explained here, for example)
I can't find any documentation about it. What I guess is that it would be easy to test the component if it simply emit the event to the parent instead of navigate (or do anything else), because I wouldn't need to inject some router or navcontroller.
Could anyone help?
(someEvent)="someMethod()" is a common way to handle events in templates
#Output() xxx; is for communicating from child to parent
for all other scenarios use a shared service
for communication between siblings there is a shortcut using template variables
<my-a-comp #a></my-a-comp>
<my-b-comp (someEvent)="a.someMethod()"
See also https://angular.io/docs/ts/latest/cookbook/component-communication.html
you can use host listeners like :
#Component({
selector: 'content-detail',
templateUrl: 'content-detail.html'
})
export class ContentDataComponent {
#HostListener('click') onClick() {
// do navigation ...
}
}

Angular 2 - service - dependency injection from another service

I have written two services in Angular 2. One of those is a basic, customised class of Http with some custom functionality in (it looks basic for now, but it will be expanding):
ServerComms.ts
import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';
#Injectable()
export class ServerComms {
private url = 'myservice.com/service/';
constructor (public http: Http) {
// do nothing
}
get(options) {
var req = http.get('https://' + options.name + url);
if (options.timout) {
req.timeout(options.timeout);
}
return req;
}
}
Another class, TicketService utilises this class above, and calls one of the methods in the service. This is defined below:
TicketService.ts
import {Injectable} from 'angular2/core';
import {ServerComms} from './ServerComms';
#Injectable()
export class TicketService {
constructor (private serverComms: ServerComms) {
// do nothing
}
getTickets() {
serverComms.get({
name: 'mycompany',
timeout: 15000
})
.subscribe(data => console.log(data));
}
}
However, I receive the following error whenever I try this:
"No provider for ServerComms! (App -> TicketService -> ServerComms)"
I do not understand why? Surely I do not need to inject every service that each other service relies upon? This can grow pretty tedious? This was achievable in Angular 1.x - how do I achieve the same in Angular 2?
Is this the right way to do it?
In short since injectors are defined at component level, the component that initiates the call services must see the corresponding providers. The first one (directly called) but also the other indirectly called (called by the previous service).
Let's take a sample. I have the following application:
Component AppComponent: the main component of my application that is provided when creating the Angular2 application in the bootstrap function
#Component({
selector: 'my-app',
template: `
<child></child>
`,
(...)
directives: [ ChildComponent ]
})
export class AppComponent {
}
Component ChildComponent: a sub component that will be used within the AppComponent component
#Component({
selector: 'child',
template: `
{{data | json}}<br/>
Get data
`,
(...)
})
export class ChildComponent {
constructor(service1:Service1) {
this.service1 = service1;
}
getData() {
this.data = this.service1.getData();
return false;
}
}
Two services, Service1 and Service2: Service1 is used by the ChildComponent and Service2 by Service1
#Injectable()
export class Service1 {
constructor(service2:Service2) {
this.service2 = service2;
}
getData() {
return this.service2.getData();
}
}
#Injectable()
export class Service2 {
getData() {
return [
{ message: 'message1' },
{ message: 'message2' }
];
}
}
Here is an overview of all these elements and there relations:
Application
|
AppComponent
|
ChildComponent
getData() --- Service1 --- Service2
In such application, we have three injectors:
The application injector that can be configured using the second parameter of the bootstrap function
The AppComponent injector that can be configured using the providers attribute of this component. It can "see" elements defined in the application injector. This means if a provider isn't found in this provider, it will be automatically look for into this parent injector. If not found in the latter, a "provider not found" error will be thrown.
The ChildComponent injector that will follow the same rules than the AppComponent one. To inject elements involved in the injection chain executed forr the component, providers will be looked for first in this injector, then in the AppComponent one and finally in the application one.
This means that when trying to inject the Service1 into the ChildComponent constructor, Angular2 will look into the ChildComponent injector, then into the AppComponent one and finally into the application one.
Since Service2 needs to be injected into Service1, the same resolution processing will be done: ChildComponent injector, AppComponent one and application one.
This means that both Service1 and Service2 can be specified at each level according to your needs using the providers attribute for components and the second parameter of the bootstrap function for the application injector.
This allows to share instances of dependencies for a set of elements:
If you define a provider at the application level, the correspoding created instance will be shared by the whole application (all components, all services, ...).
If you define a provider at a component level, the instance will be shared by the component itself, its sub components and all the "services" involved in the dependency chain.
So it's very powerful and you're free to organize as you want and for your needs.
Here is the corresponding plunkr so you can play with it: https://plnkr.co/edit/PsySVcX6OKtD3A9TuAEw?p=preview.
This link from the Angular2 documentation could help you: https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html.
Surely you do.
In Angular2, there are multiple injectors. Injectors are configured using the providers array of components. When a component has a providers array, an injector is created at that point in the tree. When components, directives, and services need to resolve their dependencies, they look up the injector tree to find them. So, we need to configure that tree with providers.
Conceptually, I like to think that there is an injector tree that overlays the component tree, but it is sparser than the component tree.
Again, conceptually, we have to configure this injector tree so that dependencies are "provided" at the appropriate places in the tree. Instead of creating a separate tree, Angular 2 reuses the component tree to do this. So even though it feels like we are configuring dependencies on the component tree, I like to think that I am configuring dependencies on the injector tree (which happens to overlay the component tree, so I have to use the components to configure it).
Clear as mud?
The reason Angular two has an injector tree is to allow for non-singleton services – i.e., the ability to create multiple instances of a particular service at different points in the injector tree. If you want Angular 1-like functionality (only singleton services), provide all of your services in your root component.
Architect your app, then go back and configure the injector tree (using components). That's how I like to think of it. If you reuse components in another project, it is very likely that the providers arrays will need to be changed, because the new project may require a different injector tree.
Well, i guess you should provide both services globally:
bootstrap(App, [service1, service2]);
or provide to component that uses them:
#Component({providers: [service1, service2]})
#Injectable decorator adds necessary metadata to track dependecies, but does not provide them.

Angular 2 lazy loading techniques

I already wrote a big application with Angular 1 and requireJS with AMD for lazy loading and structuring. The application don't use routes but parts of the application like HTML, css and Javascript (angular modules) are lazy loaded.
Now I want to change to Angular 2 and I am looking for the best lazy loading technique for HTML, css and JS (angular) content which doesn't depends on routes and which doesn't depends on thousands of different javascript frameworks.
So lazy loading route components seems to be quite simple:
http://blog.mgechev.com/2015/09/30/lazy-loading-components-routes-services-router-angular-2
but how would you accomplish that scenario without routes?
Would you recommend something like webpack, or should I keep requireJS? Is there something like OClazyload for angular 2? Or does it work somehow with Angular 2 even without any frameworks?
I'm a friend of "keep it simple" and I really would like to keep it as small and simple as possible.
Thanks!
Angular 2 is based on web components. The easiest way ("keep it simple" as you said) is using routes and components.
You can also load components simply by using directives in your html. for example:
#Component({
selector: 'my-component', // directive name
templateUrl: './app/my.component.html',
directives: []
})
export class MyComponent {}
#Component({
selector: 'root-component', // directive name
directives: [MyComponent],
template: '<my-component></my-component>',
})
export class myComponent {}
if you modify your template to include <my-component> dynamically it will load the component dynamically. This is not a best practice.
there is a dynamic component loader for angular 2, but that is not as simple as using routes or directives. it will create an instance of a Component and attache it to a View Container located inside of the Component View of another Component instance.
with it you can use:
#Component({
selector: 'child-component',
template: 'Child'
})
class ChildComponent {
}
#Component({
selector: 'my-app',
template: 'Parent (<child id="child"></child>)'
})
class MyApp {
constructor(dcl: DynamicComponentLoader, injector: Injector) {
dcl.loadAsRoot(ChildComponent, '#child', injector);
}
}
bootstrap(MyApp);
The resulting DOM:
<my-app>
Parent (
<child id="child">Child</child>
)
</my-app>
There is another option (look at angular2 link above) in which you can optionally provide providers to configure the Injector provisioned for this Component Instance.
Hope this helps.
With angular 2 Latest version we can use loadchildren property to perform lazy loading.
For example :
{
path: 'Customer',
loadChildren: './customer.module#Customer2Module?chunkName=Customer'
},
In this above example I am using webpack Bundling(angular 2 router loader) + Anguar 2 Routing to lazy load the modules.
https://medium.com/#daviddentoom/angular-2-lazy-loading-with-webpack-d25fe71c29c1#.582uw0wac
Lets assume we have two pages in our application, “home” and “about”. Some people might never reach the about page, so it makes sense to only serve the load of the about page to people that actually need it. This is where we will use lazy loading.

Categories