Using Angular 2 component in Angular 1 app - angularjs

I am following https://angular.io/docs/ts/latest/guide/upgrade.html steps, namely "Using Angular 2 Components from Angular 1 Code" part.
Have created hero-detail.component.ts with following code:
import { Component } from '#angular/core';
#Component({
selector: 'hero-detail',
template: `
<h2>Windstorm details!</h2>
<div><label>id: </label>1</div>
`
})
export class HeroDetailComponent {
}
Then added required code to main.ts:
import { upgradeAdapter } from './upgrade-adapter'; //this existed already
import { HeroDetailComponent } from './hero-detail.component'; //added
//... here other code inbetween
angular.module('heroApp', [])
.directive('heroDetail', upgradeAdapter.downgradeNg2Component(HeroDetailComponent));
Now I insert <hero-detail></hero-detail> in my html, but see nothing. What is missing?

Directive part of code should have been added to app.ts, not main.ts.

Related

Angular component not showing template

I downloaded angular form https://angular.io/quick-start and then added a file to the app folder "tutoriel.component.ts" and wrote the following code
import { Component } from "#angular/core";
#Component({
selector: 'tutoriel',
template: '<h2>Tutoriel</h2>'
})
export class TutorielComp{
}
and then changed this in the app.module
#NgModule({
declarations: [
AppComponent,
TutorielComp
],
and the class got imported import { TutorielComp } from './tutoriel.component'; The problem is when I deleted all the code in the app.component.html and wrote
<tutoriel></tutoriel>
It did not show.

Convert $window.UserSettings array from angularjs component to angular 8 component

I have an angularjs 1.7 component which I need to upgrade to angular 8 component. It has an external script, which I cannot modify. That script inserts an iframe into the div and it expects some settings from the component to customize the iframe.
The old component code:
angular.module('myApp.shared').component("userExternal", {
template: '<div id="userIframe"></div>',
controller: function ($window) {
this.scriptUrl = "//myurl/widget/addIframe.js";
this.$onInit = function () {
$window.UserSettings = [];
$window.UserSettings.push(['set', {
btn_color: '#008A00',
bg_color: 'white'
}]);
});
};
}
});
I have two problems here:
I don't know how to convert $widnow to angular 8 window object.
When I convert $window to angular 8 window, how can I add UserSettings array to it?
This is my angular 8 component, but my code did not work correctly.
HTML Template
<script src="//myurl/widget/addIframe.js"></script>
<div class="user_external></div>
TS Code
import { Component} from '#angular/core';
#Component({
selector: 'app-user',
templateUrl: './user-external.component.html'
})
export class UserExternalComponent {
constructor() {
}
ngOnInit() {
window.UserSettings = [];
window.UserSettings.push(['set', {
btn_color: '#008A00',
bg_color: 'white'
}]);
console.log(window);
}
}
Thank you
Following a combination of this tutorial for the window reference and this tutorial for upgrading from AngularJS to Angular in general, I created an injectable service that seems to be doing the job, at least so far in a downgraded context. (Next step is to start upgrading the modules that use the dependency, but I successfully replaced all AngularJS injections of $window with my new APIWindow class, and everything works as before with no breaking errors.)
Keeping in mind this is being used as a downgraded Angular class inside a currently mostly AngularJS app, the class looks like this:
// api.window.service.ts
import { Injectable } from '#angular/core'
import { downgradeInjectable } from '#angular/upgrade/static'
import * as angular from 'angular'
// You could change this to return any property on Window, but external is the one I use:
function _external (): any {
return window.external
}
#Injectable()
export class APIWindow {
get external (): any {
return _external()
}
}
angular
.module('APIModule')
.service('APIWindow', downgradeInjectable(APIWindow))
Hopefully this helps someone else with a similar situation following this upgrade path!

Angular UpgradeModule: can't get element to bind during app bootstrap

I have an old AngularJS app I'm trying to implement an upgrade-in-place using the Angular 6 UpgradeModule. I can get all the code to execute -- I'm logging out states as expected through both the Angular 6 and AngularJS apps.
The problem is that I'm failing utterly to bind anything to the DOM.
All the documentation and examples use NgDoBootstrap thus, inside the core AppModule of the new Angular 6 app:
this.upgrade.bootstrap(document.body, ['angularJS-app-name'], {strictDi: true});
I can execute that. I can see my AngularJS app bootstrapping (via console.logs) via the UpgradeModule. I can see my Angular 6 app bootstrapped (via console.logs). But nothing is bound to the DOM.
Logging out document gives me the HTML document I'd expect. I can manually examine that in the Chrome console, and see all the elements I would expect to. But all document methods and properties seem to be returning null.
document.body: null.
document.getElementById('an-elementId-I-can-see-when-logging-out-document'): null.
Tell me I'm just doing something dumb like not injecting something properly so that Angular/TS is interpreting document differently than vanilla JS does.
master.app.ts
import {APP_BASE_HREF} from "#angular/common";
import {Component, NgModule, Inject} from '#angular/core';
import {BrowserModule} from '#angular/platform-browser';
import {UpgradeModule} from '#angular/upgrade/static';
import {platformBrowserDynamic} from '#angular/platform-browser-dynamic';
import {RouterModule, Routes, UrlHandlingStrategy} from '#angular/router';
#Component({
selector: 'ng6-router-root',
template: '<router-outlet></router-outlet><div class="ng-view"></div>'
})
export class Ng6RouterRoot{}
export class HybridUrlHandlingStrategy implements UrlHandlingStrategy {
shouldProcessUrl(url: any) {return false;}
extract(url:any) {return url;}
merge(url:any, whole:any) {return url;}
}
#NgModule({
declarations: [
Ng6RouterRoot
],
imports: [
BrowserModule,
UpgradeModule,
RouterModule.forRoot([])
],
providers: [
{ provide: UrlHandlingStrategy, useClass: HybridUrlHandlingStrategy },
{ provide: APP_BASE_HREF, useValue: '/' }
]
})
export class AppModule {
constructor (private upgrade: UpgradeModule) {
}
ngDoBootstrap() {
console.log('master.app.ts ngDoBootstrap start', document);
console.log('document.body', document.body);
this.upgrade.bootstrap(document.getElementById('master'), ['angularJsApp'], {strictDi: true});
console.log('master.app.ts bootstrap end');
}
}
platformBrowserDynamic().bootstrapModule(AppModule);
console.log('master.app.ts end readyState', document.readyState);
relevant html
<div id="master">
<ng6-router-root>
</ng6-router-root>
</div>

Angular UpgradeComponent cannot find $scope

I have a hybrid angular-cli that roughly follows Victor Savkin's Lazy Loaded AngularJS guide. AngularJS is bootstraped in the constructor of a LazyLoaded Angular module. The main difference between my app and the guide is that I am trying to wrap the <ui-view> directive inside of some Angular components. Because of how my layout is structured the <ui-view> element will not be available when AngularJS is bootstrapped and may be added or removed at any time.
import { Component, Directive, ElementRef, Injector } from '#angular/core';
import { UpgradeComponent } from '#angular/upgrade/static';
import * as angular from 'angular';
#Component({
template: `
<layout-wrapper>
<my-toolbar></my-toolbar>
<layout-contents>
<ng2-ui-view>
<h3 class="text-center">AngularJS page not loaded</h3>
</ng2-ui-view>
</layout-contents>
</layout-wrapper>
`,
})
export class LegacyOutputComponent { }
#Directive({selector: 'ng2-ui-view'})
export class UpgradedUiViewComponent extends UpgradeComponent {
constructor(ref: ElementRef, inj: Injector) {
super('uiViewWrapper', ref, inj);
}
}
export const routerPatchModule = 'arcs.router.patch';
// We need to define a wrapper for ui-view because we can only upgrade
// components with only one definition. uiView cannot be automatically
// upgraded because its definition is too complex
angular.module(routerPatchModule, ['ui.router'])
.component('uiViewWrapper', { template: '<ui-view></ui-view>'})
When I run the code a Error: No provider for $scope! error is thrown. Checking the stack trace I can see that it is thrown in the UpgradeComponent super class. The injector tries to get $scope and
Alternative is to let Angular know that it needs to provide the $scope.
import { Injector } from '#angular/core';
// allow $scope to be provided to ng1
export const ScopeProvider = {
deps: ['$injector'],
provide: '$scope',
useFactory: (injector: Injector) => injector.get('$rootScope').$new(),
};
#Directive({
providers: [ ScopeProvider ],
selector: 'ng2-ui-view',
})
export class UpgradedUiViewComponent extends UpgradeComponent {
constructor(ref: ElementRef, inj: Injector) {
super('uiViewWrapper', ref, inj);
}
}
This setup will not work. AngularJS needs to be able to load in the root of your application in order for the scope to be defined correctly.
A better way to approach this problem is to use the <div ui-view> directive in the root of your application (as in the upgrade guide) and then to downgrade a layout component from Angular into AngularJS to wrap your content.

Angular 2 Hybrid App: Unknown provider: ng2.ComponentFactoryRefMapProvider

Scenario
I'm trying to use a very simple angular 2 (rc.5) component as a directive inside my angular 1.5 app but I'm getting an error when adding the directive to one of my modules.
Error
[$injector:unpr] Unknown provider: ng2.ComponentFactoryRefMapProvider <- ng2.ComponentFactoryRefMap <- dummyDirective
Code
Angular2
jspm command for bundling: jspm bundle-sfx embeddedModule.ts dist/component-sfx.min.js --skip-source-maps --format global --global-name __myglobal
This component-sfx.min.js gets moved over into the proper location within the angular1 project
embeddedModule.ts
import 'rxjs/Rx';
import 'core-js/client/shim.js';
import 'zone.js';
import 'reflect-metadata/Reflect';
import { UpgradeAdapter, UpgradeAdapterRef } from '#angular/upgrade';
import { Component, provide, Inject, NgModule, forwardRef } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
const adapter = new UpgradeAdapter(forwardRef(() => EmbeddedModule));
#Component({
selector: 'dummy',
template: '<p>Hello!</p>'
})
class DummyComponent {
constructor() {
console.log('Dummy.constructor...');
}
}
#NgModule({
imports: [
BrowserModule
],
declarations: [
DummyComponent
]
})
class EmbeddedModule {}
var downgradedComponent = adapter.downgradeNg2Component( DummyComponent );
export { adapter, DummyComponent, downgradedComponent };
Angular 1 app
index.html
<script src=... angular vendor scripts ...></script>
<script src="path/to/my/angular2-sfx.js></script>
main.js
angular.element(document).ready(function() {
...
angular.bootstrap(document.body, ['my-app']);
...
});
my.controller.js (where I actually want to use my downgraded component)
(function() {
'use strict';
angular.module('data-gov-reference.state.index')
.controller('MyController', Index)
.directive('dummy', __myglobal.adapter.downgradeNg2Component(DummyComponent));
function Index() {
console.log('MyController...');
}
}());
myview/myview.html (the view where I want the downgraded directive to show up)
<div>
<dummy></dummy>
</div>
Nevermind, figured out the issue.
In main.ts, angular.bootstrap(...) should have been __myglobal.adapter.bootstrap(...)
main.ts
angular.element(document).ready(function() {
...
__myglobal.adapter.bootstrap(document.body, ['my-app']);
...
});
Hope it helps someone else!

Resources