Angular 2 custom directive with es5 - javascript

I'm trying to build a custom directive / Angular 2 using vanilla es5.
Here is the code
(function(app) {
app.CustomDirective = ng.core.
Directive({
selector: 'test',
})
.Class({
constructor: function CustomDirective() {
console.log('ds')
},
ngOnInit: function() {
}
});
})(window.app || (window.app = {}));
I declare it into the ngmodule
(function(app) {
app.AppModule =
ng.core.NgModule({
imports: [ ng.platformBrowser.BrowserModule, ng.http.HttpModule ],
declarations: [ app.AppComponent, app.CustomDirective],
providers: [app.MyService ],
bootstrap: [ app.AppComponent ]
})
.Class({
constructor: function() {}
});
})(window.app || (window.app = {}));
in The AppComponent
(function(app) {
app.AppComponent = ng.core.
Component({
selector: 'my-app',
templateUrl: '//app/app.component.html',
directives: [app. CustomDirective ]
})
.Class({
constructor: [app.MyService, function AppComponent(myService) {
this.myService = myService;
}],
ngOnInit: function() {
}
});
})(window.app || (window.app = {}));
and in the html
<h2 test >Test</h2>
But the console log i have in the constructor of the directive never gets fired.
If i declare it instead of the declarations array, to the providers array it gets called which is odd since it's a directive and because of that I can't access the elementRef for the element I apply the directive.
I'm using the latest angular 2 version as is in the package.json form the quickstart guide
UPDATE:
I need to update this line
Directive({
selector: 'test',
})
to this
Directive({
selector: '[test]',
})

Related

How to write Test cases for below angular method

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

Javascript: How can i use /core functions in app/js/services/controllers Angular

(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);
}]);
})

Angular 5 with es5

Angular 4 supports below syntax
var HelloComponent = ng.core
Component({
selector: 'hello-cmp',
template: 'Hello World!',
viewProviders: [Service]
.Class({
constructor: [Service, function (service) {
},`
});
In Angular 5 , Class is missing anyone can provide Angular 5 with ES5 syntax currently
i am not able to switch ES6 so please avoid that suggestion.
if switching to ES6 is the only way then i will stick to angular 4 as of now
You need to use static properties annotations and parameters on your component class function something like this:
function Service() {}
function AppComponent(service) {
console.log(service);
}
AppComponent.prototype.ngOnInit = function() {
console.log('test ngOnInit');
};
AppComponent.annotations = [
new Component({
selector: 'my-app',
template: '<h1>Example of Angular 5.0.5 in ES5</h1>',
viewProviders: [Service]
})
];
AppComponent.parameters = [ Service ];
Plunker Example

TypeError: Cannot read property 'Autocomplete' of undefined in Angular 2

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>

How to refer to a template by id in angular2?

I'm trying to find a way to refer to a file by using an unique name instead of a folder path. With absolute and relative path, if I move the components folder I have to refactor all the link. That is quite annoying also because by using gulp all the javascript file are moved to another directory.
In Angular 1 we just refer to the ID of the ng-template.
Is there a way to do a similar thing in Angular 2?
Additional information:
Code I'm currently using
(function(app) {
app.AppComponent =
ng.core.Component({
selector: 'my-app-test',
templateUrl: '/assets/base/components/comp-test-app/test-template.html'
})
.Class({
constructor: function() {}
});
})(window.app || (window.app = {}));
You can reference the template by relative urls by setting the moduleId of the component.
(function(app) {
app.AppComponent =
ng.core.Component({
moduleId: module.id,
selector: 'my-app-test',
templateUrl: 'test-template.html'
})
.Class({
constructor: function() {}
});
})(window.app || (window.app = {}));
Source: https://angular.io/docs/ts/latest/cookbook/component-relative-paths.html

Categories