Dynamic template in templatURL in angular2 - javascript

I have been using ng-include in angular 1 whenever I had to include a tamplate dynamically in the page.
Now how to acheive this in angular 2. I have tried searching and found these :
https://groups.google.com/forum/#!topic/angular/ROkKDHboWoA ,
https://github.com/angular/angular/issues/2753
can someone explain how to do this in angular2 as the link says ng-include is not included due some security reasons.
Or atleast how to use a veriable in templateUrl property so that the veriable value can be handled on server side to serve the template...

And as you can see in this issue on the Angular repo, most probably we won't get that directive. There has been a long discussion whether we need this directive or not, and if not provided how we can implement it by our self.
I tried to make a simple example of how it can be implemented.
#Component({
selector: 'my-ng-include',
template: '<div #myNgIncludeContent></div>'
})
export class MyNgInclude implements OnInit {
#Input('src')
private templateUrl: string;
#ViewChild('myNgIncludeContent', { read: ViewContainerRef })
protected contentTarget: ViewContainerRef;
constructor(private componentResolver: ComponentResolver) {}
ngOnInit() {
var dynamicComponent = this.createContentComponent(this.templateUrl);
this.componentResolver.resolveComponent(dynamicComponent)
.then((factory: any) => this.contentTarget.createComponent(factory));
}
createContentComponent(templateUrl) {
#Component({
selector: 'my-ng-include-content',
templateUrl: templateUrl,
directives: FORM_DIRECTIVES,
})
class MyNgIncludeContent {}
return MyNgIncludeContent ;
}
}
For a demo check this Plunker.

Actually angular 2 has not featured this in the current build. Also as per the links added, I don't think this feature will be included.
A piece of javascript to dynamically add template using ajax call may be used.
Or possibly in future a dynamic template loader library will be available for use.

As of alpha.46 (and with ES6 JS):
In the parent file import file you wanted to include:
#Component({
selector: 'account'
})
#View({
templateUrl: './folder/containing/template.html'
})
Easy as that.
If you meant to import a component, this is what you do in the parent file:
import ComponentClassName from './folder/with/componentName';
...
#View({
directives: [ComponentClassName]
})
And inside the imported file of the child/component:
Define your ComponentClassName (you may add templateUrlto the #View just as demonstrated at the top).
Don't forget to export default ComponentClassName; at the bottom of the file.
There are not many examples in the official Angular 2 docs, but you stumble across it every once in a while.

As #binariedMe accurately describes, ng-include is off in Angular 2 due to security considerations. The recommended method is to use a custom directive with slightly more programmatical overhead.
Additionally, to prepare your Angular code for 2.0:
myApp.directive('myInclude', function() {
return {
templateUrl: 'mytemplate.html'
};
});
And rather than using ng-include on an element, simply add my-include:
<div my-include></div>

Following #binariedMe and this blog post http://blog.lacolaco.net/post/dynamic-component-creation-in-angular-2/, I was able to construct a solution that may work for you. Using an AJAX call and creating the custom component dynamically from the returned html content should fix this problem in creating a new my-ng-include custom directive.
import {
Component,
Directive,
ComponentFactory,
ComponentMetadata,
ComponentResolver,
Input,
ReflectiveInjector,
ViewContainerRef
} from '#angular/core';
import { Http } from '#angular/http';
export function createComponentFactory(resolver: ComponentResolver, metadata: ComponentMetadata): Promise<ComponentFactory<any>> {
const cmpClass = class DynamicComponent {};
const decoratedCmp = Component(metadata)(cmpClass);
return resolver.resolveComponent(decoratedCmp);
}
#Directive({
selector: 'my-ng-include'
})
export class MyNgInclude {
#Input() src: string;
constructor(private vcRef: ViewContainerRef, private resolver: ComponentResolver, private http: Http) {
}
ngOnChanges() {
if (!this.src) return;
this.http.get(this.src).toPromise().then((res) => {
const metadata = new ComponentMetadata({
selector: 'dynamic-html',
template: res.text(),
});
createComponentFactory(this.resolver, metadata)
.then(factory => {
const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
this.vcRef.createComponent(factory, 0, injector, []);
});
});
}
}
Just simply use it as follows:
<my-ng-include [src]="someChangingProperty"></my-ng-include>

Related

Import js script to Angular from URL

i am building an app with angular but found a problem I don't know how to solve.
I need to import a js script and it has to be imported from the internet. (because these are their rules, so that they can do hotfixes)
The problem is, when I import the script into my index.html I can only use it in js files not in the typescript files. How can i use the libary in my ts files?
I can't install it via npm and i also can't download the file and add it to my project folder.
The libary is the airconsole API (https://developers.airconsole.com/#!/api)
Thanks for your help
it's only add in your component declare var AirConsole to use it, a fool Component
import { Component, VERSION } from '#angular/core';
declare var AirConsole //<--this is the "magic"
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular ' + VERSION.major;
airConsole=new AirConsole() //<--now you can use airConsole in your component
}
Update Well, generally we can use a service to mannage the airConsole, this allow us that all components that inject the service can use the functions,variables,...
Disclamer: I don't know about "AirConsole", so I imagine control it like we can control others .js like Cordova)
As we need that Angular know when a function is executed in .js, our service can be like
import { Injectable,NgZone } from '#angular/core';
declare var AirConsole;
#Injectable({
providedIn: 'root',
})
export class AirConsoleService implements OnInit {
airConsole=new AirConsole()
message:Subject=new Subject<any>();
constructor(private ngZone: NgZone) { }
ngOnInit(){
this.airconsole.onMessage = (from, data)=>{
this.ngZone.run(() => {
this.message.next({from:from,data:data})
});
})
}
message(device:any,message:any){
this.ngZone.run(() => {
this.airConsole.message(device,message);
});
}
}
So, e.g. you can subscribe to airConsoleService.message

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

Angular 5 - Loading Script After View Is Loaded

I have a legacy script that I need to include in my angular application.
The thing about this script is that it relates to a specific component, and it has to be loaded only after the view of the component is loaded.
As for today, I succeeded to include it on OnInit function but sometimes (not always for some reason) the CLI throws an error about it.
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-player-page',
templateUrl: './player-page.component.html',
styleUrls: ['./player-page.component.scss']
})
export class PlayerPageComponent implements OnInit {
public itemId: string;
constructor() {}
ngOnInit() {
//We loading the player srcript on after view is loaded
require('assets/scripts/player/player.js');
}
}
The script assumes that elements in the UI exists and it searches them by the id.
When adding it on top of the page, it doesn't work.
What is the best way to achieve the desired behavior?
There are multiple solutions to this issue.
declare the require const on top of your component
declare const require: any;
import { Component, OnInit } from '#angular/core';
#Component({})
...
use the dynamic import() function from typescript
ngAfterViewInit() {
//We loading the player script on after view is loaded
import('assets/scripts/player/player.js');
}
change the library to only start running after you call a function from the component, this way you can add it to the scripts array of your angular.json

How to load script file from component html?

Basically I wanted to load component html specific script file, so that script I'm going to put script file reference inside component html itself, I'm seeing that inner script file has been ignored while rendering component html on page.
Component
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: 'test.html'
})
export class AppComponent { }
test.html
<div>
<h1>My First Angular 2 App</h1>
</div>
<script src="test.js"></script>
Above is my code what I tried & I already have test.js there in place.
Plunkr Here
Is there any way to load component specific javascript file with component OR with its html?
Working Plunker
Security
It looks like Angular takes out script tags from Html templates.
From the Angular Docs:
It removes the <script> tag but keeps safe content, such as the text content of the <script> tag
Angular provides methods to bypass security, but for your use case it looks like a service would be helpful.
Services
The preferred way to include your own custom script in your component from a separate dedicated file would be to create a service.
I took the code from your Plunker's script.js file and put it into a service like this:
// File: app/test.service.ts
import { Injectable } from '#angular/core';
#Injectable()
export class TestService {
testFunction() {
console.log('Test');
}
}
Then I imported the service and called the custom code like this:
// File: app/app.component.ts
import { Component, OnInit } from '#angular/core';
import { TestService } from './test.service';
#Component({
selector: 'my-app',
templateUrl: 'test.html',
providers: [TestService]
})
export class AppComponent implements OnInit {
constructor(private testService: TestService) {}
ngOnInit() {
this.testService.testFunction();
}
}
Lifecycle hooks
If you want to call your service's custom code at a specific point you can take advantage of lifecycle hooks. For example you can call your code using the ngAfterViewInit() instead of ngOnInit() if you want to wait until the view has loaded.

Angular 2 alpha 22, How to pass down a value to a component through attributes?

I declared a component with a selector 'app'
then I used it like follow:
<app title="Awesome Title"></app>
in the template I wrote this:
The title passed {{ title }}
I did add write the Component annotation:
#Component({
selector: 'app',
properties: {title:'title'} // also tried ['title']
});
But nothing shows up, the output is
The title passed
Any help? Thanks
At the time of writing the most recent version is alpha.26.
The property definition has changed slightly, so here is the new syntax
#Component({
selector: 'app',
properties: ['title:title']
});
<div [title]="Some Title"></div>
I have started a series of blog articles to demo Angular 2 concepts. Check it out here:
http://www.syntaxsuccess.com/viewarticle/angular-2.0-examples
I have a working example of how to assign properties to components via properties. Check out the tree view example as it uses it heavily.
For some reason, properties don't seem to work on the bootstrapped root app.
Try creating a component called 'title-app' and loading it inside of 'app'.
// inner component
#Component({
selector: 'title-component',
properties: {title:'title'}
})
#View({ template: '<h1>{{title}}</h1>'})
class titleComponent{}
// root app
#Component({ selector: 'app' })
#View({
directives: titleComponent,
template: '<title-component [title]="Some Title"></title-component>'
})
class App{}
You might also want to use the latest version of Angular2: currently alpha-26. From alpha-26+, if your property is the same name you can just call it once.
properties: {'title': 'title'} => properties: 'title'
the correct way to do this is:
import {
ComponentAnnotation as Component,
ViewAnnotation as View, bootstrap
} from 'angular2/angular2';
import {Attribute} from 'angular2/src/core/annotations_impl/di';
#Component({
selector: 'app'
})
#View({
template: '{{goofy}} stuff'
})
class App {
goofy:string;
constructor(#Attribute('goofy') goofy:string) {
this.goofy = goofy;
}
}
bootstrap(App);
See the full Plunker here http://plnkr.co/edit/n2XWez69HNJbixH3tEmR?p=preview
However, this is currently broken and is a known issue https://github.com/angular/angular/issues/1858

Categories