I am using Google API Javascript library in my Angular 2 application. I have created a service which is injected in components. Here is the code:
import { Injectable } from '#angular/core';
const url = 'https://apis.google.com/js/client.js?onload=__onGoogleLoaded';
#Injectable()
export class GoogleAPIService {
public client: any;
public calculatorService: any;
public actionService: any;
loadAPI: Promise<any>
constructor(){
this.loadAPI = new Promise((resolve) => {
window['__onGoogleLoaded'] = (ev) => {
console.log('gapi loaded');
resolve(window['gapi']);
this.client = window['gapi'].client;
this.loadEndPoints('{Endpoint URL}/_ah/api');
}
this.loadScript();
});
}
doSomethingGoogley(){
return this.loadAPI.then((gapi) => {
console.log(gapi);
});
}
loadScript(){
console.log('loading..')
let node = document.createElement('script');
node.src = url;
node.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(node);
}
loadEndPoints(apiRoot) {
// Loads the OAuth and calculatorendpoint APIs asynchronously, and triggers login
// when they have completed.
var apisToLoad;
var callback = function() {
console.log('API Loaded '+apisToLoad);
if (--apisToLoad == 0) {
//this.endpoint1= this.client.endpoint1; //Doesn't Work
//this.endpoint2= this.client.endpoint2;
}
}
apisToLoad = 3; // must match number of calls to gapi.client.load()
this.client.load('oauth2', 'v2', callback);
this.client.load('endpoint1', 'v1', callback, apiRoot);
this.client.load('endpoint2','v1',callback,apiRoot);
}
}
I have three questions:
How do I get the endpoints gapi.client.endpoint1 as a public variable in the service?
How do I call the methods in the api? In javascript, u can jst call gapi.client.endpoint1.method().execute()
How do I make this service singleton?
Any help is appreciated.
EDIT:
Here is the working version of the service. I use it as provider in my Root module. thus, its available as singleton throughout the application.
import { Injectable } from '#angular/core';
const url = 'https://apis.google.com/js/client.js?onload=__onGoogleLoaded';
const gapiOnLoaded = '__onGoogleLoaded';
const clientName = 'gapi';
const endpointhost = '[HTTPS URL FOR ENDPOINTS]';
const apiEndPoint = endpointhost + '/_ah/api';
#Injectable()
export class GoogleAPIService {
private gapi: any;
private loadAPI: Promise<any>;
constructor() {
this.loadAPI = new Promise((resolve) => {
window[gapiOnLoaded] = (ev) => {
this.gapi = window[clientName];
// Loads the OAuth and other APIs asynchronously, and triggers login
// when they have completed.
let apisToLoad;
let callback = function() {
if (--apisToLoad === 0) {
resolve(window[clientName]);
}
};
apisToLoad = 3; // must match number of calls to gapi.client.load()
this.gapi.load('client:auth2', callback);
this.gapi.client.load('[ENDPOINT_1_NAME]', 'v1', callback, apiEndPoint);
this.gapi.client.load('[ENDPOINT_2_NAME]', 'v1', callback, apiEndPoint);
};
this.loadScript();
});
}
public GetClient(): any {
return this.loadAPI.then((res) => {
return this.gapi;
});
}
private loadScript() {
let node = document.createElement('script');
node.src = url;
node.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(node);
}
}
Inject this service in other services. I created a service for each of the endpoints.
#Injectable()
export class Endpoint1Service {
private gapi: any;
constructor(private googleApiService: GoogleAPIService) {
}
public isLoad() {
return this.googleApiService.GetClient().then((gapi) => {
this.gapi = gapi;
return true;
});
}
public action(data: DataType){
this.gapi.client.endpointname.apimethod(data).execute();
}
}
Services are singletons by default. You should provide it in your AppModule, and then it will be available to all of your components. Just make sure to include it in your component constructors.
import {NgModule} from '#angular/core';
import {BrowserModule} from '#angular/platform-browser';
import {HttpModule} from '#angular/http';
import { AppComponent } from './app.component';
import { routing } from './app.routing';
import { GoogleService } from './google.service'; // important
#NgModule({
imports: [
BrowserModule,
HttpModule,
routing,
],
declarations: [ AppComponent],
providers: [ GoogleService ], // important
bootstrap: [ AppComponent],
})
export class AppModule {
}
To make an endpoint available outside of your service, you can use the public keyword in front of the function that calls the endpoint. To call the endpoint in angular2, you can use the built-in http service from #angular/http. Here's an example service (only using HTTP GET) that will return an Observable for the endpoints you call.
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import { Observable } from 'rxjs';
#Injectable()
export class GoogleService {
constructor(private http: Http) { }
public endpoint1(): Observable<any> {
return this.http.get("http://endpoint.one.com");
}
public endpoint2(): Observable<any> {
return this.http.get("http://endpoint.two.com");
}
}
You can then use the service like this in your component.
import { Component, OnInit } from '#angular/core';
import { GoogleService } from './google.service'; // important
#Component({
selector: 'app',
templateUrl: 'app.component.html'
})
export class AppComponent implements OnInit {
constructor(private googleService: GoogleService) { }
ngOnInit() {
this.googleService.endpoint1().subscribe(callback1, handleError);
this.googleService.endpoint2().subscribe(callback2, handleError);
}
callback1(data){
// do something with the data from ONE
}
callback2(data){
// do something with the data from TWO
}
handleError(error: any){
console.error(error);
}
}
I recommend reading up a bit on using Observables in this post from Angular University.
I don't think modifying the DOM to load gapi is particularly good practice. It's probably better to use gapi and gapi.auth TypeScript definitions by installing them with NPM.
I've posted instructions on how to do this in my answer to Import gapi.auth2 in angular 2 typescript.
Related
I have created a component that opens my custom type dialog, I just want to create Jasmine unit test cases for this method.
export class OpenPopUpComponent implements OnInit {
constructor(public dialog:NewCustomDialog) {}
ngOnInit() {
}
openModel(){
this.dialog.open(NewComponent,<NewCustomDialogConfig>{
size: 'double',
data: {
title: 'New Dialog'
}
});
}
}
You will not test the dialog itself. What you need to do is to mock the NewCustomDialog and provide it as injected.
In your spec.ts
beforeEach(() => {
const spy = jasmine.createSpyObj('NewCustomDialog', ['open']);
TestBed.configureTestingModule({
// Provide (spy) dependency
providers: [
{ provide: NewCustomDialog, useValue: {newCustomDialogSpy} }
]
});
// Inject both the service-to-test and its (spy) dependency
masterService = TestBed.get(MasterService);
valueServiceSpy = TestBed.get(ValueService);
});
Then you can check that the spy has been called with parameters (the ones you expect).
The intension of the unit test is to test the feature of component itself and not to start testing the features which is outside the scope of component which is to be tested. So,
you do not need to test dialog.open as this should be tested in unit test of NewCustomDialog itself.
start by creating a Stub which you can use as a placeholder for NewCustomDialog, such as
export class NewCustomDialogStub{
open(){ return null; }
close(){ return null; }
// and similar dummy methods which is required by "OpenPopUpComponent"
}
Inject this stub as useClass in providers as below:
export class NewCustomDialogStub{
open(){ return null; }
close(){ return null; }
// and similar dummy methods which is required by "OpenPopUpComponent"
}
describe('OpenPopUpComponent', () => {
let component: OpenPopUpComponent;
let fixture: ComponentFixture<OpenPopUpComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [],
declaration: [OpenPopUpComponent],
providers: [
{ provide: NewCustomDialog, useClass: NewCustomDialogStub }
]
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(OpenPopUpComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be defined',()=>{
expect(component).toBeDefined();
})
it('should call "open" method of dialog on calling openModel() ',()=>{
spyon(component.dialog,'open').and.callThrough();
component.openModel();
expect(component.dialog.open).toHaveBeenCalled();
})
})
This is very basic testing but if you want to know more about writing tests , you can refer to this series of articles where I have covered almost all basic testing scenarios . Check the bottom of article for all links. The one which I used here is this one
(ANGULAR-JS)
I have function locationChange(x,y) which takes 2 args and the function is located in core/services.
var locationChange = function(x, y) {
return new Promise(function(resolve, reject) {
---
Now i want to use that function in app/js/services/controllers, and I don't know how to.
angularApp.controller('EditLocationController,function(...'
What are the steps to linking it?
EDIT: the OP modified the post specifying that they're talking about the previous version of Angular, but the logic shouldn't change. Create a service and inject it into your component.
I assume you are talking about angular (v2+) and not AngularJS.
usually, you would define your methods in a service, like:
#Injectable()
export class MyService{
constructor() { }
myMethod(): {
...
}
}
You can then inject the service into a component to use its methods, as in:
import { MyService} from '...';
#Component({
selector: '...',
templateUrl: '...',
styleUrls: ['...']
})
export class MyComponent {
constructor(private service: MyService) { }
this.service.myMethod()
}
In AngularJS 1, you can create a new service module who hold your function and use it in your directive.
Service
(function() {
'use strict';
// Implementation of service
angular.module('your.library.module', [])
.factory('mylib', function() {
return {
locationChange : function(x, y) {
...
},
myOtherFunction : function() {
...
}
}
});
Directive
(function() {
'use strict';
angularApp = angular.module('your.module', [ 'your.library.module' ]);
...
angularApp.controller('EditLocationController', ['mylib', function(mylib) {
mylib.locationChange (x,y);
}]);
})
I want to use an Angular 2 Google map autocomplete and I found this directive.
When I tried to use it, it gives me this error:
errors.ts:42 ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'Autocomplete' of undefined
I dont know if I missed something. Anyway, here's the code of the directive:
import {Directive, ElementRef, EventEmitter, Output} from '#angular/core';
import {NgModel} from '#angular/forms';
declare var google:any;
#Directive({
selector: '[Googleplace]',
providers: [NgModel],
host: {
'(input)' : 'onInputChange()'
}
})
export class GoogleplaceDirective {
#Output() setAddress: EventEmitter<any> = new EventEmitter();
modelValue:any;
autocomplete:any;
private _el:HTMLElement;
constructor(el: ElementRef,private model:NgModel) {
this._el = el.nativeElement;
this.modelValue = this.model;
var input = this._el;
this.autocomplete = new google.maps.places.Autocomplete(input, {});
google.maps.event.addListener(this.autocomplete, 'place_changed', ()=> {
var place = this.autocomplete.getPlace();
this.invokeEvent(place);
});
}
invokeEvent(place:Object) {
this.setAddress.emit(place);
}
onInputChange() {
console.log(this.model);
}
}
Here's how to use it:
<input type="text" class="form-control" placeholder="Location" name="Location" [(ngModel)]="address" #LocationCtrl="ngModel"
Googleplace (setAddress)="getAddressOnChange($event,LocationCtrl)">
If you are using google maps you have to import the Api in the ngmodule like this:
#NgModule({
declarations: [...],
imports: [...,
AgmCoreModule.forRoot({
clientId: '<mandatory>',
//apiVersion: 'xxx', // optional
//channel: 'yyy', // optional
//apiKey: 'zzz', // optional
language: 'en',
libraries: ['geometry', 'places']
})
],
providers: [...],
bootstrap: [...]
})
the library 'places' is needed to use the autocomplete module.
Than use it like this:
import {MapsAPILoader} from "#agm/core";
...
constructor(private mapsAPILoader: MapsAPILoader,
...
this.mapsAPILoader.load().then(() => {
let autocomplete = new window['google'].maps.places.Autocomplete(..., ...);
autocomplete.addListener("place_changed", () => { ...
You can take a look here: Angular 2 + Google Maps Places Autocomplete
This approach helped me to get the rid of this error. Just change the google maps api import to like this:
https://maps.googleapis.com/maps/api/js?key=YOURAPIKEY
Then add &libraries=places to the end of URL so it looks like this:
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=YOURAPIKEY&libraries=places">
</script>
I have coded a js file to return some values to ts files, in my angular project.
var webGlObject = (function() {
return {
init: function() {
alert('webGlObject initialized');
}
}
})(webGlObject||{})
import { Component, OnInit } from '#angular/core';
import '../../../lib/jquery-3.2.1.min.js';
import '../../../server/getSummary.js';
declare var webGlObject: any;
#Component({
selector: 'mop-designer-dashboard',
templateUrl: 'mop-designer-dashboard.component.html',
styleUrls: ['mop-designer-dashboard.component.css'],
})
export class MopDesignerDashboardComponent implements OnInit {
debugger;
scoreBoardList: any = [];
breadcrumb: any = [];
constructor() {
/* prepare breadcrumb */
this.breadcrumb = [
{
label: 'Mop Designer',
href: '/#/mop-designer'
}
];
debugger;
webGlObject.init();
}
ngOnInit() {
console.log('test');
}
}
but declare var webGlObject: any; doesn't create any object
and I get following error:
>
ZoneTask.invoke # zone.js?fad3:339
VM292602:61 Error: Uncaught (in promise): Error: Error in :0:0 caused by: webGlObject is not defined
ReferenceError: webGlObject is not defined
at new MopDesignerDashboardComponent
This is because you are creating it as a class and using as an object.
Try this:
Put following in js file:
var webGlObject = function() {
this.init= function() {
alert('webGlObject initialized');}}
And create an instance in ts file:
declare var webGlObject: any;
export class YourComponent{
constructor(){
let wegObj = new webGlObject();
wegObj.init();
}
}
I have an angular service, from which I would like to consume a plain javascript module.
When I instantiate it from he constructor I get the following error:
TypeError: Engine is not a constructor
My service is as follows:
import { Injectable } from "angular2/core";
import * as Engine from './engine';
#Injectable()
export class MyService {
constructor() {
this.engine = new Engine(); // causing error
}
doStuff() {
return this.engine.doStuff();
}
}
And my module code:
var _ = require('lodash');
function Engine() {
this.stuff = [];
};
WorkoutEngine.prototype.doStuff = function() {
console.log('do stuff');
};
module.exports = Engine;
I can't figure out where I am going wrong?
Since you are trying to inject Engine to the constructor of MyService,
#Injectable()
export class MyService {
// Let angular inject the engine.
constructor(private engine: Engine) {
}
.....
}