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

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!

Related

Angular web component directive its not working

I am trying to convert angular components to angular web component, I followed few articles to do it which is good, but when I tried to convert angular directive as web component it throws some error which I am not able to figure out what is wrong I am doing, also I don't have any idea on can we convert an angular directive to angular web component. Below is code which I tried to convert directive as web component.
Tooltip Directive:
<pre>#directive({
selector: '[tooltip]'
})
export class TooltipDirective {
constructor(private el: ElementRef) {
this.el.nativeElement.classList.add('my-tooltip');
}
}</pre>
Creation of tooltip web components snippet:
<pre>export class AppModule implements DoBootstrap {
constructor(private injector: Injector) {}
ngDoBootstrap() {
const webTooltip = createCustomElement(TooltipDirective, {
injector: this.injector,
});
customElements.define("web-tooltip", webTooltip);
}
}
</pre>
How I am trying to use in app HTML file is below code
Web tooltip selector:
<span web-tooltip id="tooltip" content="This is a tooltip.">
enter image description here
If I try to write same code for any component its working fine but for directive as I shown code above its not working.

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 1.X Typescript Controller Undefined when using SystemJS

I am using TypeScript with AngularJS 1.5.x and in my tsconfig it all gets compiled down into SystemJS code. Now the issue I am having is only in controller classes that have an import at the top (see example below). If I take these imports out everything works just fine and the controller is found, but if I put them in, the controller is wrapped in a System.register block and it is not found when I go to my view.
I'm a beginner with SystemJS so I'm hoping I am just doing something really dumb, but it has been driving me nuts as to why when I have the imports it does not work.
import { testService} from '../services/test.service';
import { testClass} from '../models/testClass';
namespace app.controllers {
export class testController{
private testService: any;
constructor(public $http: angular.IHttpService) {
}
getData(): any {
}
}
angular.module('app')
.controller('testController', testController);
}

What is the equivalent of a factory in Angular2?

So I am used to using factories & services in Angular.
I am reading through the Angular2 docs and I don't see any equivalent of a factory. What is the equivalent for Angular2?
Factories, services, constants and values are all gone in Angular2. Angular2 is radically and fundamentally different from the classic Angular. In Angular2, the core concepts are
components
dependency injection
binding
The idea of services, factories, providers and constants has been criticized in Angular 1. It was difficult to choose between one. Removing them simplifies things a bit.
In the original Angular, you would define a service like so
app.service('BookService', ['$http', '$q', BookService]);
function BookService($http, $q){
var self = this;
var cachedBooks;
self.getBooks = function(){
if (cachedBooks) {
return $q.when(cachedBooks);
}
return $http.get('/books').then(function(response){
cachedBooks = response.data.books;
return cachedBooks;
})
}
}
Angular2 significantly leverages ES6 syntax to make the code more readable and easier to understand.
One new keyword in ES6 is class, which can be thought of as a service.
ES6 classes are a simple sugar over the prototype-based OO pattern. Having a single convenient declarative form makes class patterns easier to use, and encourages interoperability. Classes support prototype-based inheritance, super calls, instance and static methods and constructors.
Here's how that same code might look in Angular2
import {HttpService, Promise} from '../Angular/Angular2';
export class BookService{
$http, $q, cachedBooks;
constructor($http: HttpService, $q: Promise) {
this.$http = $http;
this.$q = $q
}
getBooks() {
if (this.cachedBooks) {
return this.$q.when(this.cachedBooks);
}
return this.$http.get('/books').then(function(data) {
this.cachedBooks = data.books;
return this.cachedBooks;
})
}
}
#Richard Hamilton's answer is appreciated and in addition to that there are few points to note.
For Factories,Service, and etc, in Angular2 we have service (or shared service). we have to make our service Injectable in order to use it.
NOTE: This code belongs to beta version and not RC.
import {Component, Injectable,Input,Output,EventEmitter} from 'angular2/core'
import {Router} from 'angular2/router';
import {Http} from 'angular2/http';
export interface ImyInterface {
show:boolean;
}
#Injectable() <---------------------------- Very Important
export class sharedService { <----------------- Service Name
showhide:ImyInterface={show:true};
constructor(http:Http;router:Router)
{
this.http=http;
}
change(){
this.showhide.show=!this.showhide.show;
}
}
If I want to use everywhere in my app, then I have to inject this service in bootstrap function like this,
bootstrap(App, [HTTP_PROVIDERS,sharedService <--------Name Injection
ROUTER_PROVIDERS,bind(APP_BASE_HREF).toValue(location.pathname)
]);
This way it creates single instance of your service. If you don't want to go with single instance, what you can do is - you can use Providers:[sharedService] metadata in you #component decorator.
Then, use it in your one of components like this,
export class TheContent {
constructor(private ss: sharedService) { <--------Injection dependency of your newly created service
console.log("content started");
}
showhide() {
this.ss.change(); <----- usage
}
}
Check working example here
I don't know what factories do exactly in Angular1 but in Angular2 there is useFactory:
{
provide: SomeClass,
useFactory: (dep1, dep2) => (x) => new SomeClassImpl(x, dep1, dep2),
deps: [Dep1, Dep2]
}
to provide your own instance construction logic if the default doesn't match your needs.
You can also inject a factory to create new instances yourself:
/* deprecated or removed depending on the Angular version you are using */
provide(SomeClass, {
useFactory: (dep1, dep2) => {
(x) => new SomeClassImpl(x, dep1, dep2),
},
deps: [Dep1, Dep2]
})
constructor(#Inject(SomeClass) someClassFactory: any) {
let newSomeClass = someClassFactory(1);
}
Argument x must have type assignment, otherwise angular doesn't know how to deal with it.
class SomeClassImpl {
constructor(x: number, dep1: Dep1, dep2: Dep2){}
}
If you need a new instance of a service in some component you need to provide it in that component like this:
#Component({
selector: 'hero-list',
templateUrl: './hero-list.component.html',
providers: [ HeroService ]
})
This will generate a new instance of the HereService as a factory does.

Cant get angular component router to work with lazyloading of components

Hello fellow stackoverflowers,
I've been trying for some time now without success to implement
lazy loading of angular components with the new component router (ngComponentRouter).
This is the code I used for the configuration of the main component:
//define main app component
app.value("$routerRootComponent","simpleTrackerApp");
//main app component config
var lazyLoaderMod = null;
var httpService = null;
app.component("simpleTrackerApp", {
/*#ngInject*/
controller:function ($ocLazyLoad,$http) {
lazyLoaderMod = $ocLazyLoad;
httpService = $http;
},
templateUrl: CONFIG_APP_SHARED_URL + 'mainComponent/simpleTrackerApp.html',
$routeConfig: [
{
useAsDefault:true,
path:"/bugs",
name:"Bugs",
loader: function () {
return lazyLoaderMod.load(CONFIG_APP_COMPONENTS_URL + 'bug/bugs/bugsCtrl.js');
}
},
{path:'/**',redirectTo:['Bugs']}
]
});
My first problem was I could'nt get it to work with ocLazyLoad injected in loader propety so I loaded it in the main controller and referenced it in the loader property.
After that when I finally got it working inside loader propety
I couldn't seem to get it to actually load the component I kept getting this error:
lib.js:2 TypeError: Cannot read property 'then' of undefined
at e._loader (http://simpletracker.co.il/client/production/app/lib.js:9:10670)
at e.resolveComponentType (http://simpletracker.co.il/client/production/app/lib.js:9:21463)
at e.recognize (http://simpletracker.co.il/client/production/app/lib.js:9:23535)
at http://simpletracker.co.il/client/production/app/lib.js:9:25949
at Array.forEach (native)
at e.recognize (http://simpletracker.co.il/client/production/app/lib.js:9:25921)
at e._recognize (http://simpletracker.co.il/client/production/app/lib.js:10:4834)
at e.recognize (http://simpletracker.co.il/client/production/app/lib.js:10:4605)
at t.e.recognize (http://simpletracker.co.il/client/production/app/lib.js:10:13656)
at http://simpletracker.co.il/client/production/app/lib.js:10:10757
Now I understand I'm obviously doing something wrong in the loader
and that I need to somehow register the component. but I couldn't find
any docs on angular for the loader and nobody else lazyloading components with the new router so I really couldn't figure how this could be achieved.
I'd really appreciate some help on this but I'm also wondering in general
if maybe it is premature to use angular component router
with its lack of documentation and support.
So I'd really like to hear from other expierenced angular programmers
their opinion on the matter.
Edit:Anyone have any insight on the matter?..
You could use $router instead of the http-service. Applied on your code:
app.component("simpleTrackerApp", {
templateUrl: 'mainComponent/simpleTrackerApp.html',
controller: ['$router', '$ocLazyLoad', function ($ocLazyLoad, $http) {
$router.config([
{
path: '/bugs/',
name: "Bugs",
loader: function () {
return $ocLazyLoad.load('/bugsFolder/bugs.component.js') //no url, set the path to your component
.then(function () {
return 'bugs' //name of your component
})
}
}
])
}],
});
And don't forget to inject your dependencies in the app.module.

Resources