JavaScript Alchemy University task(creating custom Promise class) - javascript

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.

Related

How to use zone.js in Angular?

I would like to use zone.js in my Angular project ( not just the runOutsideAngularZone function ).
I tried to include it like this:
import { zone } from 'zone.js';
Unfortunately I get this error:
error TS2306: File 'C:/projects/MyApp/node_modules/zone.js/dist/zone.js.d.ts' is not a module.
Then I removed the { zone } part:
import 'zone.js';
But now I get this error:
error TS2552: Cannot find name 'zone'. Did you mean 'Zone'?
My code is this:
let myZoneSpec = {
beforeTask: function () {
console.log('Before task');
},
afterTask: function () {
console.log('After task');
}
};
let myZone = zone.fork(myZoneSpec);
myZone.run(() => {console.log('Task');});
If I replace żone with Zone, I get this:
error TS2339: Property 'fork' does not exist on type 'ZoneType'.
How could I import and use zone.js from Angular?
Angular has a wrapper class for Zone.js called ngZone. You can inject it into your component like any service.
constructor(private zone: NgZone) {
this.zone.run(() => { console.log('This is zone'});
}
However, with this approach, we cannot use the full functionality of zone.js. For that, we have to declare:
declare let Zone: any;
public class MyComponent {
constructor() {
Zone.current.fork({
name: 'myZone'
}).run(() => {
console.log('in myzone? ', Zone.current.name);
});
}
}
Also, the APIs have changed since v 0.6.0. For running beforeTask and afterTask, you can look about it here, however, I looked into it and was unable to find anything related to beforeTask and afterTask.
Updated
For running beforeTask and afterTask, this is how it is possible in the new API.
constructor() {
const parentZone = new Zone();
const childZone = parentZone.fork({
name: 'child',
onInvoke: (...args) => {
console.log('invoked\n', args);
const valueToReturn = args[3](); // Run the function provided in the Zone.run
console.log('after callback is run');
return valueToReturn;
}
});
console.log(childZone.run(() => {console.log('from run'); return 'from child run';}));
}
NOTE:
If you want to create a scheduleMicroTask and want to have same functionality in it also, then you will need to implement onInvokeTask and/or onScheduleTask in the ZoneSpec (inside the parentZone.fork()).
constructor() {
const parentZone = new Zone();
const childZone = parentZone.fork({
name: 'child',
onScheduleTask: (...args) => {
console.log('task schedule...\n', args);
return args[3];
},
onInvokeTask: (...args) => {
console.log('task invoke...\n', args);
return args[3].callback();
}
});
const microTask = childZone
.scheduleMicroTask(
'myAwesomeMicroTask',
() => {
console.log('microTask is running');
return 'value returned from microTask';
} );
console.log(microTask.invoke());
}
I Highly recommend you to use NgZone with Angular.
Documentation here

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.

What's an angular lifecycle-hook need when changing sets in componentInstance property by service?

I have a component that i send to MdDialog(Angular Material Dialog in my custom service.ts)
dialogRef = this.dialog.open(component, config);
And when I change a public property of this component by componentInstance like that:
dialogRef.componentInstance.task = task;
Angular shows me an error:
Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined'. Current value: 'dialog'. It seems like the view has been created after its parent and its children have been dirty checked. Has it been created in a change detection hook ?
Full code of open-modal.service.ts
#Injectable()
export class TasksPopupService {
constructor(
private dialog: MdDialog,
private router: Router,
private tasksService: TasksService
) { }
public open(component: any, id?: string) {
if (id) {
this.tasksService.find(id)
.subscribe(task => {
this.bindDialog(component, task);
});
} else {
this.bindDialog(component, new Task());
}
}
bindDialog(component, task: Task) {
let dialogRef;
let config = new MdDialogConfig();
config.height = '80%';
config.width = '70%';
dialogRef = this.dialog.open(component, config);
dialogRef.componentInstance.task = task;
dialogRef.afterClosed().subscribe(res => {
this.router.navigate([{ outlets: { popup: null } }], { replaceUrl: true });
});
return dialogRef;
}
}
But an error occured only if id is undefined (in ELSE block) I think it's because of this.tasksService.find return Observable (async), and block ELSE is not async. But I'm not sure.
I has some confuse becouse error eccured in MdContainer of Angular Material.
If i get data from server it's need some time, but when i pass a new object it's occur fast and change detection is not finished if i understend right.
Also, it's not parent/child component and lifecycle hooks maybe not works as we expect.
I found solution, but it's not right. Just fast solution.
if (id) {
this.tasksService.find(id)
.subscribe(task => {
this.bindDialog(component, task);
});
} else {
Observable.of(new Task()).delay(300).subscribe(task => {
this.bindDialog(component, task);
});
}
I use delay for change detection has finished and error will not throw.

Service call in controller in AngularJs using ES6

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

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