Migrate AngularJS to Angular 8 - angularjs

i need to put this code line (angularJS) in angular 2+ , how can i do this?
I already do some searchs but this line is confusing me.
scope.variable.value = event.color.toHex()
Old Code:
function callColorpicker() {
var colorpicker = $('.colorpicker')
colorpicker.colorpicker().on('changeColor', function(event) {
var scope = angular.element(this).scope()
scope.variable.value = event.color.toHex()
if ($scope.autoapplysass) {
$scope.autoApplySass()
}
})
}
New Code:
callColorpicker() {
var colorpicker = $('.colorpicker')
colorpicker.colorpicker();
var _this = this;
colorpicker.colorpicker().on('changeColor', function(event) {
--> scope.variable.value = event.color.toHex()
if (_this.applySass) {
_this.applySass(false)
}
})
}

In general, if you can avoid jQuery with Angular it's a good thing because Angular handles things completely differently than JQ and in many ways the two step on each other's toes. This can be difficult to troubleshoot and down the line could cause issues that are increasingly harder to diagnose.
In your case, as luck would have it, there is an angular color picker component you could substitute with.
NPMJS page: https://www.npmjs.com/package/ngx-color-picker
Demo: https://zefoy.github.io/ngx-color-picker/
StackBlitz: https://stackblitz.com/github/zefoy/ngx-color-picker/tree/master
install with npm npm install ngx-color-picker --save
Then in app.module.ts
import { ColorPickerModule } from 'ngx-color-picker';
#NgModule({
...
imports: [
...
ColorPickerModule
]
})
In your HTML
<input [(colorPicker)]="color"
[style.background]="color"
(colorPickerChange)="onColorChange($event)" />
and in your component.ts
import { Component, ViewContainerRef } from '#angular/core';
import { ColorPickerService, Cmyk } from 'ngx-color-picker';
constructor(public vcRef: ViewContainerRef, private cpService: ColorPickerService) {}
export class AppComponent {
color: string;
onColorChange(newColor) {
this.color = newColor;
if (_this.applySass) {
_this.applySass(false)
}
}

Related

Display values from API In Angular 6 page

I am completely new to frontend dev and trying to display API data in an Angular 6 application and can't figure out how to do it.
I can display values in the top level of the returned details but it's the sub level details I am struggling with.
I am using an Angular 6 app using Routing.
Below is all my code
Homepage.component.html
<h2>Book ID List</h2>
<button (click)="getBooks()">Get</button>
<div *ngFor="let book of books.items">
<p>ID: {{book.id}}</p>
</div>
I can get the 'ID'
I am using a service to get the data from the test API
Service.component.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class ApiServiceService {
url = 'https://www.googleapis.com/books/v1/volumes?q=HTML5 Wire-frames';
constructor(private http: HttpClient) { }
private extractData(res: Response) {
const body = res;
return body || {};
}
getBooks(): Observable<any> {
return this.http.get(this.url).pipe(
map(this.extractData));
}
}
Homepage.component.ts
import { Component, OnInit } from '#angular/core';
import { ApiServiceService } from '../../services/api-service.service';
#Component({
selector: 'app-homepage',
templateUrl: './homepage.component.html',
styleUrls: ['./homepage.component.css']
})
export class HomepageComponent implements OnInit {
books: any = [];
constructor(private apiService: ApiServiceService) { }
ngOnInit() { }
getBooks() {
this.books = [];
this.apiService.getBooks().subscribe((data: {}) => {
console.log(data);
this.books = data;
});
}
}
At present this return the following:
What I want to do is display the value from the 'eBook' which is under the 'saleInfo' level. I know I need to the change the loop for each array returned in the HTML but this is where I'm stuck.
Also I'm not sure the code I have is the best, but it's working. As I said I'm new to all this, and can pull values from top level but not sublevels in the API.
I would recommend better naming for your service, Service.compoent.ts isn't ideal, api.service.ts is much more understandable.
Also you can see that when you subscribe, you are using data: {}, this means that the function should expect a value of type Object, but I would use any, since you use Observable<any>
Now for the problem.
I have created stackblitz which does just what you wanted. I think you have got confused with the comments. You don't want to change let book of books.items to let book of books because you would be iterating over object, which you cannot do in *ngFor.
Change the line this.books = data; to this.books.push(data);
Since, if it is this.books = data; and because the books is of type any. It will accept anything. So, now after this line, this.books = data; it becomes object which contains value of data variable. So, you should use,
this.books.push(data);
To make it behave like an array too. Then, you can access books with *ngFor.
So, now in the HTML you can access via *ngFor as:
<div *ngFor="let book of books">
<div *ngFor="let items of book.items">
<p>ID: {{items.id}}</p>
<p>ebook: {{items.saleInfo.isEbook}}</p>
</div>
</div>

Moving Angular controller functions to a separate Typescript class

I am working on an application using AngularJS and Typescript. Many requirements are being added to the application so I want to nail down the structure before the code gets out of hand.
I have an Angular controller with 1500 lines and I would like to move the functions to a separate file like so:
export class CtrlFunctions{
private scope: any;
constructor($scope) {
this.scope = $scope;
}
updateHeader = header => {
//more logic here
this.scope.header = header;
}
}
And I would access the values in the controller like this:
import { CtrlFunctions } from "../controller-functions/entry.ctrl.func";
namespace Entry {
class EntryCtrl {
private funcs: CtrlFunctions
constructor(){
this.funcs = new CtrlFunctions($scope);
}
}
}
Is this bad practice? Is there a better way to take advantage of the modularity provided by Typescript?

Implementing ng2-completer

I am currently using ng2-completer (https://github.com/oferh/ng2-completer) for angular 2 and struggling to add in the suggestions to have the full response instead of just having one value.
Also, when the suggestion is selected, how is the method assigned to handle this?
The code I've got so far is:
import { Component } from '#angular/core';
import { AutoComplete } from './autocomplete';
import { CompleterService, CompleterData, RemoteData } from 'ng2-completer';
import { SearchComponent } from './search.component';
import { QueryService } from './query.service';
#Component({
selector: 'app-home',
template: `<ng2-completer [(ngModel)]="searchStr" [dataService]="dataService" [minSearchLength]="0" [inputClass]="['form-control input-list']" [autofocus]="['true']" [selected]="selected()"></ng2-completer>`,
//directives: [ AutoComplete ]
})
export class HomeComponent {
public searchStr: string;
private dataService: CompleterData;
constructor(private completerService: CompleterService, private queryService: QueryService) {
//this.dataService = completerService.local(this.searchData, 'color', 'color');
this.dataService = completerService.remote('http://localhost:61227/machine/?query=','ComputerHostname, AssetID', 'ComputerHostname').descriptionField('ComputerType');
//this.dataService = this.queryService.search(searchStr).then(items => this.items = items);
}
selected () {
console.log("test");
}
}
However it shows the following error:
Can't bind to 'selected' since it isn't a known property of 'ng2-completer'.
selected is an event and not a property and therefor the syntax for it (as described in Angular template syntax) should be (selected)="selected($event)"
autofocus expects a boolean value (see in ng2-completer doc) not an array so you should use [autofocus]="true"

Binding Angular2 components inside of a Jquery plugin template

I'm working on using a kendo inside of an angular 2 project.
Getting the widget set up correctly is no problem:
ngOnInit() {
let options = inputsToOptionObject(KendoUIScheduler, this);
options.dataBound = this.bound;
this.scheduler = $(this.element.nativeElement)
.kendoScheduler(options)
.data('kendoScheduler');
}
When that runs, the plugin modifies the DOM (and, to my knowleged, without modifiying the shadow DOM maintained by angular2). My issue is that if I want to use a component anywhere inside of the plugin, like in a template, Angular is unaware of it's existence and won't bind it.
Example:
public views:kendo.ui.SchedulerView[] = [{
type: 'month',
title: 'test',
dayTemplate: (x:any) => {
let date = x.date.getDate();
let count = this.data[date];
return `<monthly-scheduler-day [date]="test" [count]=${count}"></monthly-scheduler-day>`
}
}];
The monthly-scheduler-day class:
#Component({
selector: 'monthly-scheduler-day',
template: `
<div>{{date}}</div>
<div class="badge" (click)=dayClick($event)>Available</div>
`
})
export class MonthlySchedulerDayComponent implements OnInit{
#Input() date: number;
#Input() count: number;
constructor() {
console.log('constructed');
}
ngOnInit(){
console.log('created');
}
dayClick(event){
console.log('clicked a day');
}
}
Is there a "right" way to bind these components inside of the markup created by the widget? I've managed to do it by listening for the bind event from the widget and then looping over the elements it created and using the DynamicComponentLoader, but it feels wrong.
I found some of the details I needed in this thread: https://github.com/angular/angular/issues/6223
I whipped this service up to handle binding my components:
import { Injectable, ComponentMetadata, ViewContainerRef, ComponentResolver, ComponentRef, Injector } from '#angular/core';
declare var $:JQueryStatic;
#Injectable()
export class JQueryBinder {
constructor(
private resolver: ComponentResolver,
private injector: Injector
){}
public bindAll(
componentType: any,
contextParser:(html:string)=>{},
componentInitializer:(c: ComponentRef<any>, context: {})=>void):
void
{
let selector = Reflect.getMetadata('annotations', componentType).find((a:any) => {
return a instanceof ComponentMetadata
}).selector;
this.resolver.resolveComponent(componentType).then((factory)=> {
$(selector).each((i,e) => {
let context = contextParser($(e).html());
let c = factory.create(this.injector, null, e);
componentInitializer(c, context);
c.changeDetectorRef.detectChanges();
c.onDestroy(()=>{
c.changeDetectorRef.detach();
})
});
});
}
}
Params:
componentType: The component class you want to bind. It uses reflection to pull the selector it needs
contextParser: callback that takes the existing child html and constructs a context object (anything you need to initialize the component state)
componentInitializer - callback that initializes the created component with the context you parsed
Example usage:
let parser = (html: string) => {
return {
date: parseInt(html)
};
};
let initer = (c: ComponentRef<GridCellComponent>, context: { date: number })=>{
let d = context.date;
c.instance.count = this.data[d];
c.instance.date = d;
}
this.binder.bindAll(GridCellComponent, parser, initer );
Well your solution works fine until the component needs to change its state and rerender some stuff.
Because I haven't found yet any ability to get ViewContainerRef for an element generated outside of Angular (jquery, vanilla js or even server-side)
the first idea was to call detectChanges() by setting up an interval. And after several iterations finally I came to a solution which works for me.
So far in 2017 you have to replace ComponentResolver with ComponentResolverFactory and do almost the same things:
let componentFactory = this.factoryResolver.resolveComponentFactory(componentType),
componentRef = componentFactory.create(this.injector, null, selectorOrNode);
componentRef.changeDetectorRef.detectChanges();
After that you can emulate attaching component instance to the change detection cycle by subscribing to EventEmitters of its NgZone:
let enumerateProperties = obj => Object.keys(obj).map(key => obj[key]),
properties = enumerateProperties(injector.get(NgZone))
.filter(p => p instanceof EventEmitter);
let subscriptions = Observable.merge(...properties)
.subscribe(_ => changeDetectorRef.detectChanges());
Of course don't forget to unsubscribe on destroy:
componentRef.onDestroy(_ => {
subscriptions.forEach(x => x.unsubscribe());
componentRef.changeDetectorRef.detach();
});
UPD after stackoverflowing once more
Forget all the words above. It works but just follow this answer

How can I change the iOS status bar color with nativescript, angular2 and typescript?

So i've found this npm package:
https://www.npmjs.com/package/nativescript-statusbar
But i'm not able to use a Page element since i'm using angular and typescript.
I already made this:
this.page.backgroundSpanUnderStatusBar= true;
But my Background color is black.
I would need the light status bar from iOS.
Is this possible right now?
Any help much appreciated!
With angular2 in nativescript you can just register and element in your component:
registerElement("StatusBar", () => require("nativescript-statusbar").StatusBar);
And in your view use the tag you created, in that case StatusBar:
<StatusBar ios:barStyle="light" android:barStyle="red"></StatusBar>
You can try the following
import {Component } from "angular2/core";
import frameModule = require("ui/frame");
#Component({
selector: "my-app",
template: `
<StackLayout orientation="vertical">
<Label [text]="message" class="title" (tap)="message = 'OHAI'"></Label>
</StackLayout>
`,
})
export class AppComponent {
ngOnInit() {
var page = frameModule.topmost().currentPage;
page.backgroundSpanUnderStatusBar = true;
if(page.ios){
page.ios.navBarVisibility = "always";
var controller = page.ios.controller;
var navigationBar = controller.navigationBar;
navigationBar.barStyle = 2; // different styles from 0 to 3
}
}
}
More about the possibilities of styling the iOS navigation bar you can find in this tutorial here
Earlier I used 'nativescript-statusbar' package for changing status bar background in my Angular/NativeScript apps. But it leads to problems with the status bar on landscape mode.
So I found a better solution:
import { Page } from "ui/page";
#Component({...})
export class YourAnyComponent {
public constructor(private page: Page) {}
ngOnInit() {
this.page.backgroundColor = '#2c303a';
this.page.backgroundSpanUnderStatusBar = true;
this.page.actionBarHidden = false; // if you want to show only statusbar or - true if you want to show both stutus bar and action bar
}
}

Resources