import { Component, Input, AfterViewInit, ViewChild, ComponentFactoryResolver, OnDestroy } from '@angular/core';
import { AdDirective } from './ad.directive';
import { AdItem } from './ad-item';
import { AdComponent } from './ad.component';
@Component({
selector: 'add-banner',
template: `
<div class="ad-banner">
<h3>Advertisements</h3>
<ng-template ad-host></ng-template>
</div>
`
})
export class AdBannerComponent implements AfterViewInit, OnDestroy {
@Input() ads: AdItem[];
currentAddIndex: number = -1;
@ViewChild(AdDirective) adHost: AdDirective;
subscription: any;
interval: any;
constructor(private componentFactoryResolver: ComponentFactoryResolver) { }
ngAfterViewInit() {
this.loadComponent();
this.getAds();
}
ngOnDestroy() {
clearInterval(this.interval);
}
loadComponent() {
this.currentAddIndex = (this.currentAddIndex + 1) % this.ads.length;
let adItem = this.ads[this.currentAddIndex];
let componentFactory = this.componentFactoryResolver.resolveComponentFactory(adItem.component);
let viewContainerRef = this.adHost.viewContainerRef;
viewContainerRef.clear();
let componentRef = viewContainerRef.createComponent(componentFactory);
(<AdComponent>componentRef.instance).data = adItem.data;
}
getAds() {
this.interval = setInterval(() => {
this.loadComponent();
}, 10000);
}
}
/*
Copyright 2017 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import { Type } from '@angular/core';
export class AdItem {
constructor(public component: Type<any>, public data: any) {}
}
/*
Copyright 2017 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
export interface AdComponent {
data: any;
}
/*
Copyright 2017 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import { Directive, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[ad-host]',
})
export class AdDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
/*
Copyright 2017 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import { Injectable } from '@angular/core';
import { HeroJobAdComponent } from './hero-job-ad.component';
import { HeroProfileComponent } from './hero-profile.component';
import { AdItem } from './ad-item';
@Injectable()
export class AdService {
getAds() {
return [
new AdItem(HeroProfileComponent, {name: 'Bombasto', bio: 'Brave as they come'}),
new AdItem(HeroProfileComponent, {name: 'Dr IQ', bio: 'Smart as they come'}),
new AdItem(HeroJobAdComponent, {headline: 'Hiring for several positions',
body: 'Submit your resume today!'}),
new AdItem(HeroJobAdComponent, {headline: 'Openings in all departments',
body: 'Apply today'}),
];
}
}
/*
Copyright 2017 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import { Component, OnInit } from '@angular/core';
import { AdService } from './ad.service';
import { AdItem } from './ad-item';
@Component({
selector: 'my-app',
template: `
<div>
<add-banner [ads]="ads"></add-banner>
<h1>Drop below:</h1>
<div myDroppable style="border: 1px solid black; width: 200px; height: 100px;"></div>
Open the browser console log and note that the "destroyed!" text is logged every time the ad banner changes, even when the "Drag Me!" text was dragged into the drop box.
</div>
`
})
export class AppComponent implements OnInit {
ads: AdItem[];
constructor(private adService: AdService) {}
ngOnInit() {
this.ads = this.adService.getAds();
}
}
/*
Copyright 2017 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { HeroJobAdComponent } from './hero-job-ad.component';
import { AdBannerComponent } from './ad-banner.component';
import { HeroProfileComponent } from './hero-profile.component';
import { AdDirective } from './ad.directive';
import { AdService } from './ad.service';
import { DragDropService } from './dragdrop.service';
import { DraggableDirective } from './draggable.directive';
import { DroppableDirective } from './droppable.directive';
@NgModule({
imports: [ BrowserModule ],
providers: [ AdService,
DragDropService
],
declarations: [ AppComponent,
AdBannerComponent,
HeroJobAdComponent,
HeroProfileComponent,
AdDirective,
DraggableDirective,
DroppableDirective
],
entryComponents: [ HeroJobAdComponent, HeroProfileComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule {
constructor() {}
}
/*
Copyright 2017 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import { Component, Input } from '@angular/core';
import { AdComponent } from './ad.component';
@Component({
template: `
<div class="job-ad">
<h4>{{data.headline}}</h4>
{{data.body}}
<strong myDraggable>Drag Me!</strong>
</div>
`
})
export class HeroJobAdComponent implements AdComponent {
@Input() data: any;
}
/*
Copyright 2017 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import { Component, Input } from '@angular/core';
import { AdComponent } from './ad.component';
@Component({
template: `
<div class="hero-profile">
<h3>Featured Hero Profile</h3>
<h4>{{data.name}}</h4>
<p>{{data.bio}}</p>
<strong myDraggable>Drag Me!</strong>
</div>
`
})
export class HeroProfileComponent implements AdComponent {
@Input() data: any;
}
/*
Copyright 2017 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
/*
Copyright 2017 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
.hero-profile {
border: 1px solid gray;
padding: 5px;
padding-bottom: 20px;
padding-left: 20px;
border-radius: 10px;
background-color: lightgreen;
color: black;
}
.job-ad {
border: 1px solid gray;
padding: 5px;
padding-bottom: 20px;
padding-left: 20px;
border-radius: 10px;
background-color: lightblue;
color: black;
}
.ad-banner {
width: 400px;
}
/*
Copyright 2017 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
/* Master Styles */
h1 {
color: #369;
font-family: Arial, Helvetica, sans-serif;
font-size: 250%;
}
h2, h3 {
color: #444;
font-family: Arial, Helvetica, sans-serif;
font-weight: lighter;
}
body {
margin: 2em;
}
body, input[text], button {
color: #888;
font-family: Cambria, Georgia;
}
a {
cursor: pointer;
cursor: hand;
}
button {
font-family: Arial;
background-color: #eee;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
cursor: hand;
}
button:hover {
background-color: #cfd8dc;
}
button:disabled {
background-color: #eee;
color: #aaa;
cursor: auto;
}
/* Navigation link styles */
nav a {
padding: 5px 10px;
text-decoration: none;
margin-right: 10px;
margin-top: 10px;
display: inline-block;
background-color: #eee;
border-radius: 4px;
}
nav a:visited, a:link {
color: #607D8B;
}
nav a:hover {
color: #039be5;
background-color: #CFD8DC;
}
nav a.active {
color: #039be5;
}
/* everywhere else */
* {
font-family: Arial, Helvetica, sans-serif;
}
/*
Copyright 2017 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script>document.write('<base href="' + document.location + '" />');</script>
<title>Dynamic Component Loader</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="sample.css">
<script src="https://unpkg.com/core-js/client/shim.min.js"></script>
<script src="https://unpkg.com/zone.js@0.7.4?main=browser"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="https://unpkg.com/systemjs@0.19.39/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('main.js').catch(function(err){ console.error(err); });
</script>
</head>
<body>
<my-app>Loading app...</my-app>
</body>
</html>
<!--
Copyright 2017 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
-->
/**
* WEB ANGULAR VERSION
* (based on systemjs.config.js in angular.io)
* System configuration for Angular samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
// DEMO ONLY! REAL CODE SHOULD NOT TRANSPILE IN THE BROWSER
transpiler: 'ts',
typescriptOptions: {
// Copy of compiler options in standard tsconfig.json
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": ["es2015", "dom"],
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true
},
meta: {
'typescript': {
"exports": "ts"
}
},
paths: {
// paths serve as alias
'npm:': 'https://unpkg.com/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
'app': 'app',
// angular bundles
'@angular/animations': 'npm:@angular/animations/bundles/animations.umd.js',
'@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/router/upgrade': 'npm:@angular/router/bundles/router-upgrade.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
'@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js',
'@angular/upgrade/static': 'npm:@angular/upgrade/bundles/upgrade-static.umd.js',
// other libraries
'rxjs': 'npm:rxjs@5.0.1',
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js',
'ts': 'npm:plugin-typescript@5.2.7/lib/plugin.js',
'typescript': 'npm:typescript@2.3.2/lib/typescript.js',
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
main: './main.ts',
defaultExtension: 'ts',
meta: {
'./*.ts': {
loader: 'systemjs-angular-loader.js'
}
}
},
rxjs: {
defaultExtension: 'js'
}
}
});
})(this);
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
var templateUrlRegex = /templateUrl\s*:(\s*['"`](.*?)['"`]\s*)/gm;
var stylesRegex = /styleUrls *:(\s*\[[^\]]*?\])/g;
var stringRegex = /(['`"])((?:[^\\]\\\1|.)*?)\1/g;
module.exports.translate = function(load){
if (load.source.indexOf('moduleId') != -1) return load;
var url = document.createElement('a');
url.href = load.address;
var basePathParts = url.pathname.split('/');
basePathParts.pop();
var basePath = basePathParts.join('/');
var baseHref = document.createElement('a');
baseHref.href = this.baseURL;
baseHref = baseHref.pathname;
if (!baseHref.startsWith('/base/')) { // it is not karma
basePath = basePath.replace(baseHref, '');
}
load.source = load.source
.replace(templateUrlRegex, function(match, quote, url){
var resolvedUrl = url;
if (url.startsWith('.')) {
resolvedUrl = basePath + url.substr(1);
}
return 'templateUrl: "' + resolvedUrl + '"';
})
.replace(stylesRegex, function(match, relativeUrls) {
var urls = [];
while ((match = stringRegex.exec(relativeUrls)) !== null) {
if (match[2].startsWith('.')) {
urls.push('"' + basePath + match[2].substr(1) + '"');
} else {
urls.push('"' + match[2] + '"');
}
}
return "styleUrls: [" + urls.join(', ') + "]";
});
return load;
};
import { Directive, HostBinding, HostListener, ElementRef, OnDestroy } from '@angular/core';
import { DragDropService } from './dragdrop.service';
@Directive({
selector: '[myDraggable]',
})
export class DraggableDirective implements OnDestroy {
@HostBinding() readonly draggable = true;
constructor(private dragDropService: DragDropService,
private elRef: ElementRef) { }
ngOnDestroy() {
console.log('destroyed!');
}
@HostListener('dragstart', ['$event']) onDragStart(e: DragEvent) {
this.dragDropService.draggable = this.elRef.nativeElement;
}
}
import { Directive, HostListener, Renderer2, ElementRef } from '@angular/core';
import { DragDropService } from './dragdrop.service';
@Directive({
selector: '[myDroppable]',
})
export class DroppableDirective {
constructor(private dragDropService: DragDropService,
private renderer: Renderer2,
private elRef: ElementRef) { }
@HostListener('dragover', ['$event']) onDragOver(e: DragEvent) {
e.preventDefault();
}
@HostListener('drop', ['$event']) onDrop(e: DragEvent) {
e.preventDefault();
e.stopPropagation();
this.renderer.appendChild(this.elRef.nativeElement, this.dragDropService.draggable);
}
}
import { Injectable } from '@angular/core';
@Injectable()
export class DragDropService {
draggable: Element;
}