Angular2 load component from external file with javascript - javascript

I'm following the angular tutorial on the basics of Angular2, trying to translate it to javascript since it is currently only available for Typescript.
I currently have two files: app.component.js and hero-detail.component.js.
Located in app.component.js i have my AppComponent. From this, I would like to load the component in hero-detail.component.js as a directive.
My current code looks like this, but I can't figure out how to load HeroDetailComponent:
app.AppComponent =
ng.core.Component({
selector: 'my-app',
inputs : ['hero'],
directives: [HeroDetailComponent],
template: `<h1>{{title}}</h1>My Heroes<h2></h2>
<ul class="heroes">
<li *ngFor="let hero of heroes"
[class.selected]="hero === selectedHero"
(click)="onSelect(hero)">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
<my-hero-detail [hero]="selectedHero"></my-hero-detail>
`
})
.Class({
constructor: function() {
this.title = 'Tour of Heroes'
this.heroes = HEROES
this.selectedHero = null
},
onSelect(hero) { this.selectedHero = hero; }
});
})(window.app || (window.app = {}));enter code here

In Javascript, all variables are bound to the app which is in turn bound to the window.
You should define your HeroDetailComponent the same way AppComponent is defined.
(function (app) {
app.HeroDetailComponent = ng.core.Component({
selector: 'hero-detail',
inputs: ['hero'], // <-- this is necessary to receive the "selectedHero"
template: ` <!-- ... --> `
}).Class({
constructor: function () {
}
});
})(window.app || (window.app = {}));
Make sure to include the file in your index.html
<!-- 2. Load our 'modules' -->
<script src='app/hero.js'></script>
<script src='app/hero-detail.component.js'></script>
<script src='app/app.component.js'></script>
<script src='app/main.js'></script>
In your AppComponent, add the newly created HeroDetailComponent like so
app.AppComponent = ng.core.Component({
directives: [app.HeroDetailComponent], <-- // note the dependence on the `app` object
// ... and other things
}).Class({ /* Class Declaration */ });

Related

AngularJs 1.7 Data inside es6 component not available on template file

I just create the project AngularJs 1.7 and webpack 4 with ES6 class on component and module setup.
this is my app main module.
// Core Styles
import './styles/main.scss';
// Core Angular
import angular from 'angular';
import { AppComponent } from "./app.component";
import CommonModule from './modules/common/common.module';
import PackagesModule from './modules/packages/packages.module';
import PackagesService from "./services/packages.service";
// These all export the module name
// import ngAnimateModuleName from 'angular-animate';
const dependencies = [
// ngAnimateModuleName
CommonModule.name,
PackagesModule.name
];
angular.module('app', dependencies)
.component('appComponent', AppComponent)
.service('PackagesService', ['$http', PackagesService]);
this is my js component file.
import './app.component.scss';
export const AppComponent = {
template: require('./app.component.html'),
controller: [
'PackagesService',
class AppController {
constructor (PackagesService) {
this.test = 11;
}
}]
};
But seems like test variable is not available inside the template file.
<header-component></header-component>
<div class="container">
<div class="row">
<div class="col-6">
<div class="packages-name-box">
<h1 class="homepage-title">Packages Name {{ test }}</h1>
<packages-list-group pages-data="pagesPackageData"></packages-list-group>
</div>
</div>
</div>
</div>
And this is HTML LOADER inside webpack.confing
{
// HTML LOADER
// Reference: https://github.com/webpack/raw-loader
// Allow loading html through js
test: /\.html$/,
loader: 'raw-loader'
}
Am i missing something why this.test is not display anything inside the template?
Thanks for your help!
Since you are not using ControllerAs syntax, you should assign the value to the $scope variable,
$scope.test = 11;
you need to inject $scope as
'PackagesService','$scope',
class AppController {
constructor (PackagesService,$scope) {
$scope.test = 11;
}
On angularjs components, all variables declared with this can be acessed by the object $ctrl on the scope. You should use:
<h1 class="homepage-title">Packages Name {{ $ctrl.test }}</h1>
you can change the object name to whatever you want on controllerAs like this: (but it's $ctrl by default)
export const AppComponent = {
template: require('./app.component.html'),
controllerAs: 'vm',
controller: [
'PackagesService',
class AppController {
constructor (PackagesService) {
this.test = 11;
}
}]
};
see angularjs component document: https://docs.angularjs.org/guide/component

angular2 outsource code in service

I have an angular2 app that displays chart data from a rest api.
I created the drawing code in my bulletchart.component file.
Now I want to outsource the code into a service.
But it seems like there is only one active instance of the data service.
This is my page content where I want to load the charts.
<div class="col">
<app-bulletchart [ID]="1" [apiAddress]="'http://url1'"></app-bulletchart>
</div>
<div class="col">
<app-bulletchart [ID]="2" [apiAddress]="'http://url2'"></app-bulletchart>
</div>
The template for the app.bulletchart is this:
<div class="panel test{{ID}}">
</div>
In my bulletchart.service file I change the DOM of the app-bulletchart with some methods like this:
initSvg() {
const identifier = '.test' + this.ID;
console.log("ident " + identifier);
this.svg = d3.select(identifier).append('svg')
.attr('class', 'bullet')
There are also methods that update the chart
drawRange() {
console.log("range " + this.ID);
// Update the range rects.
const range = this.g.selectAll('rect.range')
.data(this.ranges);
range.enter().append('rect')
I set the ID property of the bulletchart.service in the ngOnInit in bulletchart.component
But when I now try to use this.bulletchart.drawRange(); this method is only called for ID 1 and not for ID 2.
I don't understand why, because I thought it would do something like this:
create App-Bulletchart (ID=1) -> create instance of bulletchart.service (with ID = 1)
create App-Bulletchart (ID=2) -> create instance of bulletchart.service (with ID = 2)
Edit:
I added providers: [BulletchartService] to my bulletchart.component file and removed it from the app.module and now it works.
But why?!
you may include service provider in the component to ensure service is created for each instance of component
#Component({
...
providers:[BulletchartService]
...
})
Example
#Injectable()
export class AppService{
Id: string;
someMethod(){
console.log(this.Id);
}
}
#Component({
selector: 'my-child',
template: `<h1>Child ID {{Id}}</h1>
<button (click)="invokeService()" >Invoke service</button>
`,
providers:[AppService]
})
export class ChildComponent {
#Input() Id: string;
constructor(private svc: AppService){}
ngOnInit(){
this.svc.Id = this.Id;
}
invokeService(){
this.svc.someMethod();
}
}
#Component({
selector: 'my-app',
template: `<h1>Hello {{name}}</h1>
<my-child [Id]="1" ></my-child>
<my-child [Id]="2" ></my-child>
`
})
export class AppComponent {
name = 'Angular';
}
#NgModule({
imports: [ BrowserModule ],
declarations: [ AppComponent, ChildComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
Check this Plunker.
Hope this Helps!!

How is component template transformed into template element [duplicate]

I know the textbook rules on that <div *ngFor="let foo of foobars">{{foo.stuff}}</div> turns into <template ngFor let-foo="$implicit" [ngForOf]="foobars"><div>...</div></template>. My question is two-fold:
HOW?
What do I need to do to leverage this mechanism ("microsyntax") myself?
Ie turn <div *myDirective="item">{{item.stuff}}</div> into <template myDirective let-item="$implicit"><div>{{item.stuff}}</div></template>?
Since I read ngFor's source code top to bottom, I can only assume this dark magic is in the compiler somewhere, I've been up and down the angular github, but I can't put my finger on it. Help!
Yes, all magic happens in the compiler.
Let's take this template:
<div *ngFor="let foo of foobars">{{foo}}</div>
First it will be transformed to the following:
<div template="ngFor let foo of foobars>{{foo}}</div>
And then:
<template ngFor let-foo [ngForOf]="foobars"><div>{{foo}}</div></template>
In Angular2 rc.4 it looks like this
First is generated ast tree node (Abstract Syntax Tree node) and then all magic happens in the TemplateParseVisitor.visitElement(https://github.com/angular/angular/blob/2.0.0-rc.4/modules/%40angular/compiler/src/template_parser.ts#L284) specifically at the bottom (https://github.com/angular/angular/blob/2.0.0-rc.4/modules/%40angular/compiler/src/template_parser.ts#L394)
if (hasInlineTemplates) {
var templateCssSelector = createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs);
var templateDirectiveMetas = this._parseDirectives(this.selectorMatcher, templateCssSelector);
var templateDirectiveAsts = this._createDirectiveAsts(
true, element.name, templateDirectiveMetas, templateElementOrDirectiveProps, [],
element.sourceSpan, []);
var templateElementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts(
element.name, templateElementOrDirectiveProps, templateDirectiveAsts);
this._assertNoComponentsNorElementBindingsOnTemplate(
templateDirectiveAsts, templateElementProps, element.sourceSpan);
var templateProviderContext = new ProviderElementContext(
this.providerViewContext, parent.providerContext, parent.isTemplateElement,
templateDirectiveAsts, [], [], element.sourceSpan);
templateProviderContext.afterElement();
parsedElement = new EmbeddedTemplateAst(
[], [], [], templateElementVars, templateProviderContext.transformedDirectiveAsts,
templateProviderContext.transformProviders,
templateProviderContext.transformedHasViewContainer, [parsedElement], ngContentIndex,
element.sourceSpan);
}
return parsedElement;
This method returns EmbeddedTemplateAst. It's the same as:
<template ngFor let-foo [ngForOf]="foobars"><div>{{foo}}</div></template>
If you want to turn:
<div *myDirective="item">{{item.stuff}}</div>
into
<template myDirective let-item><div>{{item.stuff}}</div></template>
then you need to use the following syntax:
<div *myDirective="let item">{{item.stuff}}</div>
But in this case you won't pass context.
Your custom structural directive might look like this:
#Directive({
selector: '[myDirective]'
})
export class MyDirective {
constructor(
private _viewContainer: ViewContainerRef,
private _templateRef: TemplateRef<any>) {}
#Input() set myDirective(prop: Object) {
this._viewContainer.clear();
this._viewContainer.createEmbeddedView(this._templateRef, prop); <== pass context
}
}
And you can use it like:
<div *myDirective="item">{{item.stuff}}</div>
||
\/
<div template="myDirective:item">{{item.stuff}}</div>
||
\/
<template [myDirective]="item">
<div>{{item.stuff}}</div>
</template>
I hope this will help you understand how structural directives work.
Update:
Let's see how it works (plunker)
*dir="let foo v foobars" => [dirV]="foobars"
So you can write the following directive:
#Directive({
selector: '[dir]'
})
export class MyDirective {
#Input()
dirV: any;
#Input()
dirK: any;
ngAfterViewInit() {
console.log(this.dirV, this.dirK);
}
}
#Component({
selector: 'my-app',
template: `<h1>Angular 2 Systemjs start</h1>
<div *dir="let foo v foobars k arr">{ foo }</div>
`,
directives: [MyDirective]
})
export class AppComponent {
foobars = [1, 2, 3];
arr = [3,4,5]
}
Here is the corresponding Plunker
See also
https://angular.io/docs/ts/latest/guide/structural-directives.html#!#the-asterisk-effect
https://teropa.info/blog/2016/03/06/writing-an-angular-2-template-directive.html
https://www.bennadel.com/blog/3076-creating-an-index-loop-structural-directive-in-angular-2-beta-14.htm
https://egghead.io/lessons/angular-2-write-a-structural-directive-in-angular-2
Live example you can find here https://alexzuza.github.io/enjoy-ng-parser/
*ngFor, *ngIf, ... are structural directives.
Either apply it on a <template> element or prefix it with a *
https://angular.io/docs/ts/latest/guide/structural-directives.html#!#unless
import { Directive, Input } from '#angular/core';
import { TemplateRef, ViewContainerRef } from '#angular/core';
#Directive({ selector: '[myUnless]' })
export class UnlessDirective {
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef
) { }
#Input() set myUnless(condition: boolean) {
if (!condition) {
this.viewContainer.createEmbeddedView(this.templateRef);
} else {
this.viewContainer.clear();
}
}
}

Angular2 Component communication in JS

I am unable to communicate between two component in Angular 2 using JS. This is link for tutorial.
I think I am missing something with directives.
I tried to change it with declarations as well
This is app.main.js
(function () {
var Component = ng.core.Component
var bootstrap = ng.platformBrowserDynamic.bootstrap
var RandomComponent = Component({
selector: 'random-component',
template: `
<h2> Hsdsdffdsdfi <h2>
`
}).Class({
constructor: function() {
//empty
}
});
var AppComponent = Component({
selector: 'main-app',
directives: [RandomComponent],
template: `
<h1> Hi {{username}}<h1>
<random-component></random-component>
`
}).Class({
constructor: function() {
//empty
this.username = "Username"
}
});
document.addEventListener('DOMContentLoaded', function(){
bootstrap(AppComponent);
});
})();
and this is index.html
<html>
<head>
<title>Angular 2 QuickStart JS</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- 1. Load libraries -->
<!-- IE required polyfill -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/rxjs/bundles/Rx.umd.js"></script>
<script src="node_modules/#angular/core/bundles/core.umd.js"></script>
<script src="node_modules/#angular/common/bundles/common.umd.js"></script>
<script src="node_modules/#angular/compiler/bundles/compiler.umd.js"></script>
<script src="node_modules/#angular/platform-browser/bundles/platform-browser.umd.js"></script>
<script src="node_modules/#angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js"></script>
<!-- 2. Load our 'modules' -->
<script src='app/app.main.js'></script>
</head>
<!-- 3. Display the application -->
<body>
<main-app>Loading....</main-app>
</body>
</html>
I am currently using Angular 2 RC5, and do not wish to go to ts. I also could not find any documentation regarding JS as well. If anyone knows a good documentation link for 'JS' (not ts), please help with that as well.
NgModules
Angular2 RC5 introduced NgModules. According to the documentation,
Angular Modules help organize an application into cohesive blocks of functionality.
You need to define at least one module in your Angular application. You also should bootstrap that (root/main) module instead of a component (AppComponent in your case).
Another change is the way you declare your components and directives. Instead of using the directives property in every component, now you list them in the module using the declarations property.
Here's the working solution:
(function () {
var Component = ng.core.Component
var NgModule = ng.core.NgModule;
var BrowserModule = ng.platformBrowser.BrowserModule;
var bootstrap = ng.platformBrowserDynamic;
var AppComponent = Component({
selector: 'main-app',
template: `
<h1>Hi {{username}}</h1>
<random-component></random-component>
`
}).Class({
constructor: function() {
this.username = 'Username';
}
});
var RandomComponent = Component({
selector: 'random-component',
template: `
<h2>Random title<h2>
`
}).Class({
constructor: function() { }
});
AppModule = NgModule({
imports: [
BrowserModule
],
declarations: [
RandomComponent,
AppComponent
],
bootstrap: [
AppComponent
]})
.Class({
constructor: function() { }
});
document.addEventListener('DOMContentLoaded', function() {
bootstrap.platformBrowserDynamic()
.bootstrapModule(AppModule);
});
})();
Tutorials
Unfortunately, the Angular 2.0 docs about Javascript are definitely not as rich as the docs about Typescript. You can check the QuickStart Guide (there's and appendix regarding NgModule) or the corresponding Typescript chapter.

Angular 2 using both typescript and javascript

I have angular 2 application written on javascript. I want to migrate it to typescript.
JS code is:
(function (app) {
app.SomeService = ng.core
.Class({
constructor: [app.AnotherService, function SomeService(s) {
this._anotherService = s;
}],
someFunction: function(){
...
}
.....
});
})
(window.app || (window.app = {}));
I want to inject this service into my typescript component. How can I do that?
I tried to use it like this:
#Component({...})
export class SomeComponent {
constructor(public someService: window.app.SomeService) {
}
}
But it does not work.
The way I've managed to mix JS and TS is to have a file with the JS, in your case let's say it's src/app.js , with the code you posted above.
At this point you can do one of two things:
You create a component that is going to use said JS code, something like:
import {Component} from "angular2/core";
#Component({
selector: 'app',
template: ` <a> <onclick='app()'>When the function is clicked, use app() code</a>
,
directives: []
})
export class App {
constructor(){
}
}
The template is going to depend on what your code is supposed to do. I often use onClick, but here you have an example of what it might look like:
template: `<input value="Select Date" type="text" class="datepicker" onclick ="myCalendar()" >
`
Another option is to call the JS code from the constructor
import {Component} from 'angular2/core';
#Component({
selector: 'app',
template: `
<div>
</div>
`,
directives: []
})
export class MyApp {
name:string;
constructor() {
this.name='Angular 2';
app();
}
}
So it depends on what you want to do, and when, as far as I can tell. There are people with more experience but I tried to not say anything wrong, and provide code similar to what I have working right now.

Categories