I wonder to know the best way of binding result of a promise which is as an injection to html tag using angular 2(I use ionic 2)...
As you know the main problem with async coding is loosing reference to the current object. It seems I should pass current object as a prameter to Promise function generator.
I searched internet for better solution but nothing I found! So is there any better approch?
Ionic 2 itself use observation and subscribe to do async proccess. But the major problem is that for existing functions which are not observable it couldn't work!
My approch:
Injectable class:
export class PromiseComponent {
doPromise = function (obj: any) {
return new Promise(function (resolve2) {
setTimeout(function () {
resolve2({ num: 3113, obj: obj });
}, 5000);
});
}
}
Call on click:
promiseVal = 0
doMyPromise() {
this.myPromise.doPromise(this).then(this.secondFunc);//UPDATED HERE
}
//UPDATED HERE
secondFunc = function (res) {
this.promiseVal = res.num
}
Html:
<div>{{promiseVal}} </div>
<button (click)="doMyPromise()">Do Promise</button>
If you want to consume a promise inside your component:
promiseVal = 0
doMyPromise() {
this.myPromise.doPromise().then((res) => {
this.promiseVal = res.num
});
}
And I don't know the reasoning behind your Service but it usually is like this (optional):
export class PromiseComponent {
doPromise() { //This method will return a promise
return new Promise(function (resolve2) {
setTimeout(function () {
resolve2({ num: 3113, obj: obj });
}, 5000);
});
}
}
After OP edited the post:
You can change this:
doMyPromise() {
this.myPromise.doPromise(this).then(this.secondFunc);//UPDATED HERE
}
to
doMyPromise() {
this.myPromise.doPromise(this).then(this.secondFunc.bind(this));//UPDATED HERE
}
As you know the main problem with async coding is losing reference to the current object
That's not true, the arrow function does not bind its own this therefore you don't need to send this to doPromise
export class PromiseComponent {
doPromise () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve({ num: 3113 })
}, 5000)
})
}
}
promiseVal = 0
doMyPromise() {
this.myPromise.doPromise()
.then(res => {
this.promiseVal = res.num
})
}
Related
I need to call the backend function in angular's component according to the timer.
I tried 2 options:
Option N1:
_api: Service;
constructor(api: Service){
this._api = api;
if (this.timeout != null) {
clearTimeout(this.timeout);
}
this.timeout = setTimeout(() => {
this.update();
}, 20000);
}
update()
{
this._api.GetData()….
}
The Result of this implementation does not run timer every N seconds, it calls update() function only once. But with this approach I can access the methods/properties of the this.api
OPTION N2:
_api: Service;
constructor(api: Service){
this._api = api;
setInterval(this.update, 2000);
}
update()
{
this._api.GetData()….
}
And the result is: for some reason it can't see the methods/properties of this._api.
I checked whether I can see them if I don't use setInterval and it seems that the problem is caused by setInterval.
Please advise how to arrange timer work of some method in Angular?
You are correct in using setInterval to run the chunk every n seconds, but you need to use arrow function notation to refer to the class member variables using this keyword. Try the following
constructor(api: Service) {
this._api = api;
setInterval(() => { this.update() }, 2000); // <-- use arrow function here
}
update() {
this._api.GetData()….
}
But what is the purpose of using an additional variable to refer to the injected service? You could directly refer to it.
constructor(api: Service) {
setInterval(() => { this.update() }, 2000);
}
update() {
this.api.GetData()….
}
Furthermore, I presume you are making some HTTP request in the GetData() function. In that case, it would be better to cancel any impending requests before triggering a new one in the interval.
dataSubscription: any;
constructor(api: Service) {
setInterval(() => { this.update() }, 2000);
}
update() {
if (this.dataSubscription) {
this.dataSubscription.unsubscribe(); // <-- unsubscribing cancels any impending requests
}
this.dataSubscription = this.api.GetData()….
}
How about interval function provided by rxjs?
class Service {
getData() {
return rxjs.of( Array(Math.floor(Math.random() * 5)).fill(0).map((pr, index) => ({id: index})))
}
}
class Component {
constructor(service) {
this.service = service;
this.observable = null;
this.ngOnInit = this.ngOnInit.bind(this);
}
ngOnInit() {
this.observable = rxjs.interval(3000)
.pipe(rxjs.operators.mergeMap(_ => this.service.getData()));
}
}
const service = new Service();
const component = new Component(service);
component.ngOnInit();
component.observable.subscribe(values => {
console.log(values)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.js"></script>
I’ve a typescript class which built like following
export default class myClass{
myns: any;
async initializing() {
…
this.myns = na.map(function (item) {
return item["name"];
});
// here we have value
console.log(this.myns);
}
search(test, input) {
input = input || "";
return new Promise(function (resolve) {
let fuz= fuzzy.filter(input, this.myns); //here I want to access myns but here it comes undefined in debug
resolve(
fuz.map(function (el) {
return el.original;
})
);
});
}
}
I want to access myns inside the function search (in the function search is undfiend but inside init it have data) search how can I do it ?
not just myns is undefined this is undefined also
Try doing (resolve) => { instead of function (resolve) { so it will bind this to the callback
EDIT:
Running this code worked for me:
class myClass {
myns: any;
async initializing() {
this.myns = [{ name: 'test1' }, { name: 'test2' }].map(function (item) {
return item["name"];
});
console.log(this.myns);
}
search(test, input) {
input = input || "";
return new Promise((resolve) => {
console.log('test');
console.log(this.myns);
resolve();
});
}
}
const test = new myClass();
test.initializing();
test.search('lala', 'lala2');
As expected the output was:
[ 'test1', 'test2' ]
test
[ 'test1', 'test2' ]
What is that fuzzy library you are using?
I have two services:
ProductService
CartService
And a Controller:
ShoppingController
ShoppingController needs to download the cart from the server. In order for CartService to do this, ProductService must first download the products.
ProductService.js
ProductService.DownloadProducts = function(){
if(alreadyHaveDownloadedProducts){
return {
success: function (fn) {
fn(products);
}
};
}else{
if(getProductsPromise== null){
getProductsPromise= $http.post("Api/GetProducts")
}
return getProductsPromise;
}
CartService.js
CartService.DownloadCart = function(){
ProductService.DownloadProducts().success(function(){
if(alreadyHaveDownloadedCart){
//Dont need to go to server
return {
success: function (fn) {
fn(cart);
}
};
}else{
if(getCartPromise == null){
getCartPromise = $http.post("Api/GetCart")
}
return getCartPromise; //<= The promise I actually want to return
}
})
}
ProductService.DownloadProducts
ShoppingController.js
CartService.DownloadCart().success(function(){DisplayCart()});
This approach works nicely so far, because if the ProductService has already been called on a different page, I don't need to go back to the server. Same for cart service.The issue is I currently can't return the getCartPromise as it hasn't been created until the async GetProducts has returned
Is it possible to structure this so I can return the inner promise to ShoppingController while keeping the nice .success() syntax?
My way is similar to yours, but instead of saving some alreadyHaveDownloadedCart (boolean flag), i'm caching the promise it self, and then returns it.
I'm caching the promise for the data on the service class, and then returning it if it is exists otherwise initialize a server call.
Something like that:
class ProductService {
constructor() {
this.productsPromise = null;
}
DownloadProducts() {
if(!this.productsPromise) {
this.productsPromise = $http.post('Api/GetProducts');
this.productsPromise.then(products => this.products = products);
}
return this.productsPromise.then(() => this.products);
}
}
class CartService {
constructor(ProductService) {
this.ProductService = ProductService;
this.cartPromise = null;
}
DownloadCart() {
return this.ProductService.DownloadProducts().success(() => {
if (!this.cartPromise) {
this.cartPromise = $http.post('Api/GetCart');
this.cartPromise.then((cart) => {
this.cart = cart;
});
}
return this.cartPromise.then(() => this.cart);
});
}
}
When i created this question, my doubt was about how would i be able to test an asynchronous request utilizing mocha/enzyme/chai/sinon.
I am sure that there are different ways, but a possible one is to mock it with a handmade function that returns the appropriate values (check the answer for details).
My getInitialState method is this:
getInitialState: function() {
var me = this;
var documentData = null;
var promise = me.getDocuments();
promise.then(function(value) {
var documents = value.map(function(obj) {
return Object.keys(obj).sort().map(function(key) {
return obj[key];
});
});
documentData = documents;
});
return ({
cd: false
});
},
And the getDocuments() function that returns a promise is this:
getDocuments: function() {
var deferred = when.defer();
Collection.fetch({cd: workspaceInfo.getCD()}).then(
function(results) {
deferred.resolve(results);
},
deferred.reject
);
return deferred.promise;
},
How can i successfuly test it?
Would i have to mock the getInitialState method itself? (is that even possible)
Or just the getDocuments function with some predictable return values?
Thanks in advance, any help will be appreciated.
I solved this by requiring the Collection (which is a rest API that brings some values from a database)
var Collection = require("path/to/my/collection/Collection");
Afterwards, i use it in my getDefaultProps() method:
getDefaultProps() {
return {
Collection: new Collection()
};
},
And this in turn enables me to write tests that initialize a mocked Collection (fileDataResponse is a JSON with some data):
var CollectionMock= {
fetch: () => {
return {
then: callback => callback(fileDataResponse)
};
}
};
And use it in my test afterwards:
it("should open the modal without a loaded configuration", function() {
var instance, wrapper;
wrapper = mount(
<DocumentPreview
Collection={CollectionMock}/>
);
instance = wrapper.component.getInstance();
instance.openModal();
expect(wrapper.find('#MockedTest' + 'docOid251085').exists()).to.equal(true);
wrapper.unmount();
});
I need some help.
This is fragment of my code, i can't return boolean from it("present or not")
, so everything is working incorrectly. where I was mistaken?
describe("first TEST", function () {
var boolean, parsingAllProfiles, getRandomProfile, randomProfile;
it("present or not", function () {
freelan.notFreelancersFound.isPresent().then(function (result) {
**return boolean = result;**
})
})
if (boolean) {
console.log("NOTHING!!!!!")
} else {
it("array of profiles", function() {
Promise.resolve(freelan.parsingAllProfilePage()).then(function (profiles) {
var arrForCheck = freelan.cloneArray(profiles);
freelan.checkKeywordInProfile(arrForCheck, params.keyword);
return randomProfile = profiles[Math.floor(Math.random() * profiles.length)];
})
});
}
});
I'm not sure exactly what you are trying to do with the boolean, but here's what it might look like in a test with chained promises.
describe("first TEST", function () {
var boolean, parsingAllProfiles, getRandomProfile, randomProfile;
it("present or not", function () {
freelan.notFreelancersFound.isPresent().then(function(result) {
if (result) {
freelan.parsingAllProfilePage().then(function(profiles) {
var arrForCheck = freelan.cloneArray(profiles);
expect(freelan.checkKeywordInProfile(arrForCheck, params.keyword).toBe(true);
});
} else {
console.log("NOTHING!!!!!");
}
});
});
});
I don't know the library in question, but promise-based code async, meaning that this inner code **return boolean = result;** won't run until after other things in the main function.
it("present or not", function () {
freelan.notFreelancersFound.isPresent().then(function (result) {
**return boolean = result;**
})
})
What you really need to do is read up on Promises and learn how to chain then. If you return a promise from your test, it will wait for that promise to resolve before moving onto the next test.