Under my Angular 6 app, I have a variable "permittedPefs" which is getting value after an HTTP call (asynchronous)
#Injectable()
export class FeaturesLoadPermissionsService {
permittedPefs = [];
constructor() {
this.loadUserPefsService.getUserRolePefs(roleId)
.subscribe(
(returnedListPefs) => {
this.permittedPefs = returnedListPefs;
},
error => {
console.log(error);
});
}
}
in another method, I'm using that same variable:permittedPefs
But as it's initially empty and it gots its value after such a time, so I need to wait for it to re-use it.
I've tried to use async-await, and my purpose is waiting for permittedPefs to got aobject value
async checkPefPresence(pefId) {
const listPefs = await this.permittedPefs
}
how to fix it ??
As loadUserPefsService.getUserRolePefs method returns Observable you can store it and subscribe to it later when you need it.
#Injectable()
export class FeaturesLoadPermissionsService {
permittedPefs = [];
constructor() {
this.userRolePefsObservable = this.loadUserPefsService.getUserRolePefs(roleId);
}
}
checkPefPresence(pefId) {
let listPefs;
this.userRolePefsObservable.subscribe(
(returnedListPefs) => {
listPefs = returnedListPefs;
},
error => {
console.log(error);
});
}
Use a behaviorSubject
#Injectable()
export class FeaturesLoadPermissionsService {
permittedPefs: BehaviorSubject<any[]> = new BehaviorSubject([]);
constructor() {
this.loadUserPefsService.getUserRolePefs(roleId)
.subscribe((returnedListPefs) => {
this.permittedPefs.next(returnedListPefs);
},
error => {
console.log(error);
});
}
}
Then where ever you are checking for it(remember to unsubscribe when you are done)
if it has to be async you can do it like below
async checkPefPresence(pefId): Promise {
return new Promise((resolve, reject) => {
this.featuresLoadPermissionsService.permittedPefs.subscribe(listPefs => {
//handle whatever check you want to do here
resolve();
},reject);
})
Related
This code should do the following:
Search all links on the current page and check for multiple errors.
The check should be before showing up the results on the page.
So I want to fill an array with errors and return it after all checks are finished.
interface LinkObject {
element: HTMLAnchorElement;
url: URL;
checkTrigger?: HTMLButtonElement;
}
interface ErrorObject {
element: HTMLElement;
errors: string[];
warnings: string[];
}
export default class LinkCheckTool {
protected linksObjects: LinkObject[] = [];
protected errors: ErrorObject[] = [];
constructor() {
document.querySelectorAll('a').forEach((linkElement) => {
const button: HTMLButtonElement = document.createElement('button');
button.classList.add('tb-o-linkobject__btn');
const url: URL = new URL(linkElement.href);
if (url) {
linkElement.appendChild(button);
this.linksObjects.push({
element: linkElement,
url: url,
checkTrigger: button
})
}
})
const errors = this.fetchErrors();
console.log(errors); // results in an empty array, so need to use async / await here
}
protected fetchErrors() {
const errors = [];
this.linksObjects.forEach((linkObject) => {
if (linkObject.url.protocol !== 'javascript:') {
fetch(linkObject.url.href)
.then((response) => (response))
.then((response) => {
if (!response.ok) {
// push to errors here
}
})
.catch((error) => {
// push to errors here
})
}
})
}
}
In this case, the console output of errors returns an empty array, of course. How can I use async / await and return a promise here?
Function fetchErrors is not async here, because it does not return Promise. And since you try to call the function in the constructor async/await syntax won't really work in this context.
What you need to do instead is to use Promise callback here. You can apply the Promise.allSettled method. It will help you to wait until your requests will get resolved and then you can handle the responses one by one.
constructor() {
// ...
const errors = [];
this.fetchErrors().then(results => {
results.forEach(result => {
if (result.status === "rejected") {
errors.push(result.value)
}
})
console.log(errors); // will print you list of errors
});
}
protected fetchErrors() {
const requests = this.linksObjects
.filter(linkObject => linkObject.url.protocol !== 'javascript:')
.map((linkObject) => fetch(linkObject.url.href))
return Promise.allSettled(requests);
}
I have a websocket interface which I implemented so that I can use to send requests.
The problem is that the response is asynchronous and it initially returns the empty array because retObj is not updated from the callback function that I sent in. How can I make this function so that it will return the populated array when it has been updated.
This is how my Service looks like:
import * as interface from '../webSocket'
const carService = () => {
return {
getCars: () => {
interface.sendRequest(function (returnObject) {
//
}).then(d => d)
}
}
}
export default carService()
And this is how my action looks like:
import { GET_CARS } from '../constants'
import carService from '../carService'
export const getCars = () => async (dispatch) => {
try {
const cars = await carService.getCars()
console.log("At cars actions: ", cars) // logs: Array []
dispatch(getCarsSuccess(cars))
} catch (err) {
console.log('Error: ', err)
}
}
const getCarsSuccess = (cars) => ({
type: GET_CARS,
payload: cars
})
You simply have to wrap your callback into promise, since it was not a promise to begin with, which is why you cannot use then or await
import * as interface from '../webSocket'
const carService = () => {
return {
getCars: () => {
return new Promise(resolve => interface.sendRequest(function (returnObject) {
resolve(returnObject.msg)
}));
}
}
}
export default carService()
The problem is, you cant await a function unless it returns a Promise. So, as you can guess, the problem lies in carService.getCars's definition. Try this:
getCars: () => {
return new Promise((resolve, reject) => {
interface.sendRequest(function(returnObject) {
// if theres an error, reject(error)
resolve(returnObject);
})
})
}
Or, if sendRequest os am async function, simply return the return value of sendRequest:
getCars: () => {
return interface.sendRequest()
}
I'm new in react and what i'm trying to do is a proxy pattern with axios ouside my component "home" but when i put axios in my separated class it doesn't work. But it works inside my component
Here's my proxy class:
import axios from "axios";
export default class daniProxy {
constructor(proxyUrl, apiUrl) {
this.proxyUrl = proxyUrl;
this.apiUrl = apiUrl;
}
getDanis(restUrt) {
let ds = {};
axios
.get(this.proxyUrl + this.apiUrl + restUrt)
.then((danis) => {
ds = {
data: danis.data,
};
})
.catch((err) => {
console.log(err);
});
return ds;
}
}
And here is my home component class:
class home extends Component {
state = {
danis: null,
};
componentDidMount() {
let prox = new daniProxy(proxyurl,apiurl);
console.log(prox.getDanis('/getDanis'))
}
any idea why it doesn't return something?
your return ds statement wont wait axios promise to be resolved. once you call axios, next line is evaluated right away and ds is still an empty object. instead, try returning the promise:
getDanis(restUrt) {
return axios
.get(this.proxyUrl + this.apiUrl + restUrt)
.then((danis) => {
return {
data: danis.data,
};
})
.catch((err) => {
console.log(err);
});
}
now at your componentDidMount you may declare as async and do:
async componentDidMount() {
let prox = new daniProxy(proxyurl,apiurl);
const result = await prox.getDanis('/getDanis');
console.log(result);
}
or chain your promise:
componentDidMount() {
let prox = new daniProxy(proxyurl,apiurl);
prox.getDanis('/getDanis')
.then(console.log);
}
It's because the GET request is asychronous so the return execute before. You should catch the promise.
getDanis(restUrt) {
return axios
.get(this.proxyUrl + this.apiUrl + restUrt)
.then((danis) => {
return {
data: danis.data,
};
})
.catch((err) => {
console.log(err);
});
}
Then, in your component you can get the result
componentDidMount() {
let prox = new daniProxy(proxyurl,apiurl);
prox.getDanis('/getDanis').then((res) => {
console.log(res);
});
}
I'm unable to get the data response from unsplashAxios within the GetPhoto constructor, at the moment it just says 'Promise { }'. Have I missed something blatantly obvious or does this need rethinking?
Attempt 1
class Unsplash {
constructor(path) {
return new Promise((resolve, reject) => {
unsplashAxios(path)
.then(response => {
resolve(response);
})
.catch(error => {
reject(error);
});
});
}
}
class GetPhoto {
constructor() {
console.log('Get Photo');
const unsplash = new Unsplash('/photos/PgHc0Ka1E0A');
console.log(unsplash)
// Console.log Response - Promise { <pending> }
}
}
Attempt 2
class Unsplash {
constructor(path) {
return unsplashAxios(path)
}
}
class GetPhoto {
constructor() {
const unsplash = new Unsplash('/photos/PgHc0Ka1E0A');
unsplash.then((response) => {
console.log(response)
}).catch((response) => {
console.log(response);
});
}
}
Attempt 3 - After #Klaycon I've rewrote the above and this seems to work. But feedback would be great (good or bad).
const unsplashAxios = require('./unsplashAxios');
// Class
class Unsplash {
// Constructor
constructor() {
this.unsplash = null;
}
// Method
getPhoto(id){
this.unsplash = unsplashAxios( `/photos/${id}`);
this.unsplash.then((response) => {
console.log(response)
}).catch((response) => {
console.log(response);
});
}
// Method
getCollection(id){
this.unsplash = unsplashAxios(`/collections/${id}/photos`);
this.unsplash.then((response) => {
console.log(response)
}).catch((response) => {
console.log(response);
});
}
}
// Instance
myUnsplash = new Unsplash();
// Method
myUnsplash.getPhoto('PgHc0Ka1E0A');
// Method
myUnsplash.getCollection('62890');
You return a Promise.
As #Bergi said in your comment, avoid to use them in a constructor.
Use :
unsplash.then((data) => {
//do something with data
});
to wait til the promise is resolved. You can read more about promises here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Since unsplashAxios is already returning a promise, you don't need to wrap it in another promise (you're resolving unsplashAxios's promise then putting that result into another Promise that would have to be resolved elsewhere. Changing your code to this should work:
constructor(path) {
unsplashAxios(path)
.then(response => {
//do something with the response
})
.catch(error => {
//do something with the error
}
}
}
I ended up rewriting and found the following to work as expected:
class Unsplash {
constructor(path) {
return unsplashAxios(path)
}
}
class GetPhoto {
constructor() {
const unsplash = new Unsplash('/photos/PgHc0Ka1E0A');
unsplash.then((response) => {
console.log(response)
}).catch((response) => {
console.log(response);
});
}
}
Take this function as example:
getSessionInfo() {
this._http.get(someCorrectUrlHere)
// Don't know what this map function do
.map(res => res.json())
// Don't know what this subscribe function do
.subscribe(
data => {
if (data.response.statusCode === 200) {
** the data.response.data in this case is an object**
return data.response.data;
} else {
console.log("cant get session info");
}
},
err => console.log(err),
() => { }
);
}
What my understanding is, the return data.response.data will not actually return something when the getSessionInfo() is called.
For example, the this.session will still be undefined.
getSession() {
this.session = this.service.getSessionInfo();
}
What I want to do is, return the value data.response.data and assign it to this.session.
Note, they are in different class in Angular2 Project.
export class HeaderComponent {
service: ApiService;
session: Object;
constructor(service: ApiService) {
this.service = service;
this.getSession();
}
getSession() {
this.session = this.service.getSessionInfo();
// I expect this is the true value I expected, not undefined
console.log(this.session);
}
}
The ApiService class is in a different folder
#Injectable()
export class ApiService {
_http: Http;
regions: Object;
constructor(http: Http) {
this._http = http;
}
getSessionInfo() {
.......
}
}
I used to know they can do what I want using $q, $defer, but what should I do using Angular 2
Do return create either new Promise/Observable as you are going to use .subscribe over observable. So this._http.get(someCorrectUrlHere) promise from getSessionInfo method and then do have .then with Arrow function over getSessionInfo method.
getSessionInfo() {
//creating custom promise & then resolving and rejecting it on condition.
return new Promise((resolve, reject) =>{
this._http.get(someCorrectUrlHere)
.map(res => res.json())
.subscribe(
data => {
if (data.response.statusCode === 200) {
resolve(data.response.data); //returning data by resolving promise
} else {
console.log("cant get session info");
reject("Error occured");
}
},
err => {
console.log(err);
reject(err);
},
() => {}
);
});
}
Code
export class HeaderComponent {
service: ApiService;
session: Object;
constructor(service: ApiService) {
this.service = service;
this.getSession();
}
getSession() {
this.service.getSessionInfo().then(
//success function.
(session) => {
this.session = session;
console.log(session);
},
(error)=>{
//error function
});
}
}
You can convert Observable returned by Angular 2 http call to Promise if you are comfortable working with that
getSessionInfo() {
return this._http.get(someCorrectUrlHere).toPromise();
);
More about it in the official Angular docs here.
Or, you can try to do it Angular 2 way. Keeping your service as
getSessionInfo() {
this._http.get(someCorrectUrlHere)
.map(res => res.json())
}
and subscribing in your HeaderComponent
getSession() {
this.service.getSessionInfo().subscribe(
data => {
if (data.response.statusCode === 200) {
this.session = data.response.data;
console.log(this.session);
} else {
console.log("cant get session info");
}
},
err => console.log(err),
() => { }
);
}