Service call in controller in AngularJs using ES6 - javascript

I am trying to call a service in a controller using Weakmap(), but its giving an error as Cannot read property 'getWeather' of undefined. Below is my code:
const SERVICE = new WeakMap();
export default class WeatherController {
constructor(apiService) {
SERVICE.set(this, apiService);
if (navigator.geolocation) {
navigator.geolocation.watchPosition(this.geoSuccess, this.geoFailed);
}
}
geoSuccess(position) {
// This gives an error mentioned above
SERVICE.get(this).getWeather(position.coords.latitude, position.coords.longitude);
}
geoFailed(err) {
console.log('Error:', err);
}
}
WeatherController.$inject = ['apiService'];

I think, your this - context is lost when getSuccess is called, You may try this:
if (navigator.geolocation) {
navigator.geolocation.watchPosition(
this.geoSuccess.bind(this),
this.geoFailed.bind(this)
);
}

Related

JavaScript Alchemy University task(creating custom Promise class)

Can some one please help me understand this?
How come this.thenFn(value); is not showing error like undefined /not defined when new instance of Pact is created?
Full Code:
class Pact {
constructor(fn) {
this.resolve = (value) => {
this.thenFn(value);
}
this.reject = () => {
}
fn(this.resolve, this.reject);
}
then(_then) {
this.thenFn = _then;
}
catch() {
}
}
I simulated this on pythontutor and it gives error but on AU platform it works actually.

Error when delegating class method in ES6

I have this UseCase class:
class UseCase {
constructor(repository) {
this.repository = repository;
}
execute() {
//do stuff
}
}
module.exports = UseCase;
and this Service class:
class Service {
constructor(repository) {
this.useCase = new UseCase(repository);
}
doWork = this.useCase.execute;
}
module.exports = Service;
What I want is delegate service.doWork() call to useCase.execute(), but when I execute it, I get this error:
TypeError: Cannot read property 'execute' of undefined
However, if I change my Service code to this:
class Service {
constructor(repository) {
this.repository = repository;
}
doWork = new UseCase(this.repository).execute;
}
module.exports = Service;
it works properly! Why is that? What am I missing?
Class fields run as soon after the constructor they can, right after any super calls, if any. Your code is equivalent to:
class Service {
constructor(repository) {
this.doWork = this.useCase.execute;
this.useCase = new UseCase(repository);
}
}
It's not defined in time.
Put doWork in the constructor instead, after the assignment to useCase.
You also need to make sure that .execute is called with the proper calling context - just passing this.useCase.execute loses the calling context of useCase.
class Service {
constructor(repository) {
this.useCase = new UseCase(repository);
this.doWork = () => this.useCase.execute();
}
}
You could also use a class field which calls .execute when called:
class Service {
constructor(repository) {
this.useCase = new UseCase(repository);
}
doWork = () => this.useCase.execute();
}

Prevent AudioWorkletNode memory leak

I have an AudioWorkletNode that is a member of a class instance. When I delete/kill/remove that instance, the AudioWorkletNode and MessagePort leak.
Before deleting the instance, I make sure the corresponding AudioWorkletProcessor's process method isn't running. I've also tried calling the port's close() method, and even setting the AudioWorkletNode to null. It also doesn't seem to matter if the node is connected or disconnected at the time. It leaks either way.
To establish the audioWorklet module:
AG_Recorder = class AG_Recorder {
constructor(outNode) {
AG_AudioManager.audioCtx.audioWorklet.addModule( '/audioWorkers/recorder.js').then(() => {
this.recorderNode = new AudioWorkletNode(AG_AudioManager.audioCtx, 'recorder');
this.recorderNode.connect(outNode);
this.recorderNode.port.onmessage = (event) => {
this.handleMessage(event.data);
};
}).catch(function (err) {
console.log('recorder audioworklet error: ', err.name, err.message);
});
}
}
And the processor, strongly abbreviated for relevancy:
class RecorderWorkletNode extends AudioWorkletProcessor {
constructor (options) {
super(options);
this._running = true;
this.port.onmessage = event => {
if (event.data.release) {
this._running = false;
}
}
this.port.start();
}
process (inputs, outputs, parameters) {
return this._running;
}
}
And before the node is disconnected, and the AG_Recorder instance is deleted, I've tried doing this:
release() {
this.recorderNode.port.postMessage({release: true});
this.recorderNode.port.onmessage = null;
this.recorderNode.port.close();
this.recorderNode = null;
}
It seems to be a confirmed bug for Chromium:
https://bugs.chromium.org/p/chromium/issues/detail?id=1298955
Update: not very sure about Firefox etc

testing a service call in jasmine

I am trying to write a unit-test for a function that calls a service. But am running into the error: TypeError: undefined is not a constructor
What I am trying to test is a service call that, on success, sets the value of the variable 'cards'.
I've created the appropriate mock for the service (CardService), which you can see in the spec file below
test.component.spec.ts
class MockCardService extends CardService {
constructor() {
super(null); // null is the http in service's constructor
}
getCardDetails(): any {
return Observable.of([{ 0: 'card1' }, { 1: 'card2' }]);
}
}
describe('MyComponent', () => {
let component: MyComponent;
let mockCardService: MockCardService;
beforeEach(() => {
mockCardService = new MockCardService();
component = new MyComponent(
mockCardService // add the mock service that runs before each test
);
});
// The failing test :(
it('should set the card variable to the value returned by the service', () => {
spyOn(mockCardService, 'getCardDetails').and.callThrough();
// Act
component.ngOnInit();
component.updateCards(); // call the function I am testing
// Assert
expect(component.cards).toConTainText('Card1');
});
And the component file with the function I'm testing:
export class MyComponent implements OnInit {
public cards: CardModel[] = [];
constructor(
private cardService: CardService,
) {
}
ngOnInit() {
this.updateCards(); // call the update card service
}
updateCards(): void {
this.cardService.getCardDetails().subscribe(
(cardsDetails) => {
this.cards = cardsDetails;
},
(err) => {
// todo: error handling
console.log(err);
}
);
}
}
Whenever this test runs I recieve the error:
TypeError: undefined is not a constructor (evaluating 'Observable_1.Observable.of([{ 0: 'card1' }, { 1: 'card2' }])') (line 22)
getCardDetails
updateCards
ngOnInit
And I can't figure out what I'm doing wrong, why 'getCardDetals.subscribe' is undefined. The MockCardService class I provided doesn't appear to be working for some reason.
(note that this.cardService.getCardDetails() is defined, if I log it out in the component itself )
Any help would be very much appreciated.
Author here:
I'm still not sure what is going wrong. But I was able to fix this by changing class MockCardService extends CardService { to just let MockCardService, and using that variable throughout. Good luck to anyone who runs into this!
MockCardService.getCardDetails() should return an Observable, so you can run subscribe in the component.

Angular 2 with TypeScript: View not updating after setting variable in response handler

I ran into an issue with Angular 2 using TypeScript that I could use an extra set of eyes on. I am requesting a token from an API which works great. In my response handler I am checking for basic errors and displaying them to the end users. If I log out the error and my message from the console it displays correctly but the view/template does not update.
In my class I have the following:
public message: string;
In my constructor I have:
constructor() {
this.message = 'Message to enduser';
}
My two methods are the following:
myRequest() {
(<any>window).privateAPI.getToken({
token: this.tmpData.token
}, this.responseHandler);
return false;
}
responseHandler(response: any) {
setTimeout(function() {
if (response.error) {
// this.message update is not updating in the template
this.message = response.error.message;
console.log('error: ', this.message);
} else {
// success
}
}, 100);
}
Any assistance would be greatly appreciated.
I was able to solve this issue by utilizing ngZone. The following resolves my problem with my component not updating my template in the API response.
// import NgZone
import {Component, NgZone} from '#angular/core';
// pass NgZone to constructor
constructor(private _zone: NgZone) {
this.message = 'Message to enduser';
}
requestToken() {
(<any>window).privateAPI.getToken({
token: this.tmpData.token
}, (status: number, response: any) => {
this._zone.run(() => {
if (response.error) {
this.message = response.error.message;
} else {
// good to go
}
});
});
}
This is because you created a new context within setTimeout. The keyword function does this automatically. In TypeScript you can use lambdas (under Lambdas and using this), also called arrow-functions. When using a lambda, it will automatically capture the this available when the function is created rather than when it is invoked.
Try this:
setTimeout(() => {
if (response.error) {
// this.message update is not updating in the template
this.message = response.error.message;
console.log('error: ', this.message);
} else {
// success
}
}, 100);

Categories

Resources