I've been trying to make a very basic app pulling user information from a .json file and "logging them in", and have a json storing whether a user is logged into the app and their user ID. I'm stuck on a method which would take the given email and password, match them to an entry in the users json, then update the login json with the new information (login true, and user ID.) This is what I have so far in the method:
setUserLogIn(email, password):any{
if (this.users){
this.users.forEach(foundUser => {
if (foundUser.email === email && foundUser.password === password){
this.currentUser=foundUser;
let login:Login = {"id": 1, "loginStatus":true, "userId":foundUser.id}
return this.httpService.put<Observable<any>>('http://localhost:7800/loginCheck/1', login)
.pipe(map((log:Observable<Login>) =>{
console.log(log) //this isn't reached, never prints in console
if (log !== undefined){
return true;
}
return false;
}))
}
if (this.currentUser != null){
FetchUserService.isLoggedIn = true;
} else{
FetchUserService.isLoggedIn = false;
}
})
}
}
From my previous tests I know everything else in the method works correctly, just the put only returns undefined. I am subscribing to the method in the controller:
this.fetchUserService.setUserLogIn(this.userEmail, this.userPassword).subscribe(data => {
console.log(data);
})
This method of subscription returns an error. I also tried subscribing in the service itself, like:
return this.httpService.put<Observable<any>>('http://localhost:7800/loginCheck/1', login)
.pipe(map((log:Observable<Login>) =>{
console.log(log)
if (log !== undefined){
log.subscribe(data => {
return data
})
Taking this into the component and logging the result also just returns undefined.
Any suggestions? I have no idea what I'm doing wrong, after searching put methods for the past few hours I can't see any differences in what I have there. Any help is greatly appreciated!
There are multiple issues here.
Parallel subscriptions. Avoid them if possible. Here you could use forkJoin to combine all observables and trigger them in parallel.
Why would an HTTP request emit an Observable as it's response? Most probably it wouldn't.
Currently you aren't returning anything from the function.
Try the following
setUserLogIn (email, password): Observable<any> { // <-- return `Observable` here
if (!this.users) return NEVER;
return forkJoin(
this.users.map(foundUser => {
if (foundUser.email === email && foundUser.password === password) {
this.currentUser = foundUser;
FetchUserService.isLoggedIn = true;
let login: Login = {
"id": 1,
"loginStatus": true,
"userId": foundUser.id
};
return this.httpService.put('http://localhost:7800/loginCheck/1', login).pipe(
map((log: Login) => { // why would an HTTP request emit an observable?
console.log(log);
return (!!log);
})
);
}
FetchUserService.isLoggedIn = false;
return EMPTY; // `forkJoin` emits only when all observables complete
})
);
}
Related
Create an async function getUsers(names), that gets an array of GitHub logins, fetches the users from GitHub and returns an array of GitHub users.
The GitHub url with user information for the given USERNAME is: https://api.github.com/users/USERNAME.
There’s a test example in the sandbox.
Important details:
1.There should be one fetch request per user.
2.Requests shouldn’t wait for each other. So that the data arrives as soon as possible.
3.If any request fails, or if there’s no such user, the function should return null in the resulting array.
Input:array;
output:array;
TypeError: r.json is not a function
async function getUsers(names) {
let requests = names.map(name => fetch(`https://api.github.com/users/${name}`));//gets users
let users = [];//Final answer
await Promise.allSettled(requests)
.then(responses => new Promise(function(resolve) {// returrn correct users promise
let corrects = [];
responses.forEach((result) => {
if (result.value.ok) { //check statuse 200-299
corrects.push(result);
} else {
users.push(result); // else add to Finell answer null
}
})
resolve(corrects); //return users with 200-299 statuse
}))
.then(corrects => Promise.all(corrects.map(r => r.json()))) //processing
.then(results => results.forEach(result => users.push(result))); //add to finel answer correct requests
return users;
}
//Input:array;
//output:array;
//TypeError: r.json is not a function
There's a number of things slightly wrong with your code, but I think the main issue is that you're pushing the results of allSettled into 'corrects' but, you want to push the .value instead.
You also don't actually do anything with corrects and only return failed requests.
But here's a version that cleans it all up. I'm assuming you want to ignore failed requests, but not sure, because it's hard to tell from your code:
async function getUsers(names) {
const requests = names.map(name => fetch(`https://api.github.com/users/${name}`));//gets users
const results = await Promise.allSettled(requests);
const successResponses = results
.filter(result => {
// Filter out rejected promises and error responses.
// I think this is what you want but not sure?
if (result.status!=='fulfilled' || !result.value.ok) return false;
});
return Promise.all(successResponses.map(response => response.json()));
}
Promise.allSettled is a very special-purpose function and you will not need it in most cases. There are other pain points like the explicit promise constructor anti-pattern. Instead decompose the problem into smaller, simple parts -
getUser(name) takes a single name and returns a user object or null
getUsers(names) takes a list of names and maps getUser over each
async function getUser(name) {
try {
const res = await fetch(`https://api.github.com/users/${name}`)
return res.ok ? res.json() : null
}
catch (err) {
return null
}
}
function getUsers(names) {
return Promise.all(names.map(getUser))
}
getUsers(["ivg", "glennsl", "jeffsco", "nosuchuser111"]).then(console.log, console.error)
.as-console-wrapper { min-height: 100%; top: 0; }
[
{
"login": "ivg",
"id": 2336698,
"node_id": "MDQ6VXNlcjIzMzY2OTg=",
...
},
{
"login": "glennsl",
"id": 5207036,
"node_id": "MDQ6VXNlcjUyMDcwMzY=",
...
},
{
"login": "jeffsco",
"id": 4043178,
"node_id": "MDQ6VXNlcjQwNDMxNzg=",
...
},
null // user not found
]
I have created a service which sends a request to the backend and makes the result available to the component via an observable.
export class QuestionManagementService {
questionsArray$: Observable<Result>;
private questionsArraySubject: BehaviorSubject<Result>; //to send list of questions
...
constructor(private http: HttpClient, private helper:HelperService, private bs:WebToBackendInterfaceService, private loaderService:LoaderService) {
this.questionsArraySubject = new BehaviorSubject<Result>(new Result('initial',{})); //A Subject can act both as an Observable and an Observer
this.questionsArray$ = this.questionsArraySubject.asObservable(); //create Observable. Other components can subcribe to it now to get notifications/values
...
}
//this method sends the request to network via another `bs` service. The request is sent using `http.post`
getQuestions(questionFilter:GetQuestionsfilter){
console.log("In QuestionManagementService: getQuestions");
let observable:Observable<HttpEvent<any>> = this.bs.getQuestions(questionFilter);
let subscription:Subscription = observable.subscribe((ev:HttpEvent<any>)=>{
if(ev.type === HttpEventType.Response) { //null means that the response wasn't an HttpResponse but probably some internal Rxjs event (eg type 0)
let response= <HttpResponse<any>>ev;
console.log("http response received: ",response);
//should remove the token from storage
console.log('response body from server: ',ev.body);
let isResponseStructureOK: boolean = this.helper.validateServerResponseStructure(ev.body);
if (isResponseStructureOK) {
console.log("response structure is OK");
let response: ServerResponseAPI = ev.body;
let result:string = response.result;
console.log("result is : " + result);
/*if result could be success or error*/
/*additionalInformation is a string and the string contains a valid json which has array of questions
in format {"questions-list":[{"tag":"some tag1","description":"some description1"},{{"tag":"some tag2","description":"some description2"},...]}
*/
let message:string = response['additional-info'];
console.log("message is "+message);
if(result === "success") {
let jsonQuestionList: string = response['additional-info'];
console.log("jsonQuestionList response as string: ", jsonQuestionList);
//let jsonQuestions: PracticeQuestionsListAPI = JSON.parse(jsonQuestionList);
//console.log("jsonQuestion array:", jsonQuestions);
//this.questionsArraySubject.next(jsonQuestions['questions-list']);
this.questionsArraySubject.next(new Result('success', response["additional-info"]));
} else {
this.questionsArraySubject.next(new Result('error', response["additional-info"]));
}
}
else {
/**
* If something goes wrong, send error rather than send next with result="error"
*/
console.log("received incorrect response structure from server: ", ev.body);
//TODOM - need to change hard coded responses and pick them from a config or global variable.
this.questionsArraySubject.error(new Result('error',"Invalid response structure from server"));
}
}
else {
console.log("not response. ignoring");
}
},
(error:ServerResponseAPI)=>{/*web to backend service will send error in ServerResponseAPI format. This is what handleError throws*/
console.log("got error from the Observable: ",error);
this.questionsArraySubject.error(new Result('error',error['additional-info']));
},
()=>{ //observable complete
console.log("observable completed")
});
}
}
The following component subscribes to this service.
export class PraticeQuestionListComponent implements OnInit, OnDestroy {
questions: PracticeQuestionsListAPI; //the result from observable will be stored here.
questionListSubscription:Subscription; //reference of the subscription
ngOnDestroy(): void {
console.log("destroying component. unsubscribing");
this.questionListSubscription.unsubscribe()
}
//on initialisation, I subscribe to the observable
ngOnInit(){
console.log("in question list on init. question is ",this.questions);
...
this.questions= new PracticeQuestionsListAPI(new AdditionalPagingInfo("",new PartitionInfo(0,0)),
[]);
let tagSubscription = this.questionManagementService.getSupportedTags(new TagId("coding"));
console.log("subscribing to question mgmt service");
this.questionListSubscription = this.questionManagementService.questionsArray$.subscribe((result:Result)=>{
console.log('received result from question mgmgt service - array observable',result);
if(result.result === "success") { //received response from server
let questionList = JSON.parse(result.additionalInfo) as PracticeQuestionsListAPI;
console.log("got list of questions value ", questionList);
this.questions['pagination-info'] = questionList['pagination-info'];
this.questions['questions-list'] = questionList['questions-list'];
/*
0 length of questions-list means no questions.
this could be response from the server indicating that there are no more questions
*/
/*
* the server indicates that there are no more questions by either sending empty question list or by sending
* 0 values for pagination state and partition info
*/
if (questionList["questions-list"].length !== 0) { //server has send list of questions
this.questions['pagination-info']['page-state'] = questionList['pagination-info']['page-state'];
this.questions['pagination-info']['partition-info'] = questionList['pagination-info']['partition-info'];
this.questions['questions-list'] = questionList['questions-list'];
console.log("previous question filter is ",this.questionsFilter);
this.questionsFilter["pagination-info"]["page-state"]=questionList["pagination-info"]["page-state"];
this.questionsFilter["pagination-info"]["partition-info"].month=questionList["pagination-info"]["partition-info"].month;
this.questionsFilter["pagination-info"]["partition-info"].year=questionList["pagination-info"]["partition-info"].year;
console.log("new question filter is ",this.questionsFilter);
//TODOM - maybe this assignment below was causing memory leak. So changed this as above
//this.questionsFilter['pagination-info'] = questionList['pagination-info'];
this.lastPage = false; //the server indicates that there are no more questions by sending these values (no paging state and no partition info)
if (this.questions['pagination-info']['page-state'].length == 0 &&
this.questions['pagination-info']['partition-info'].year == 0 &&
this.questions['pagination-info']['partition-info'].month == 0) {
this.lastPage = true;
} else {//if the list is empty then there are no (more) questions for the selected tag
this.lastPage = false;
}
} else {
this.lastPage = true; //Don't show next button if there are no questions.
this.showDialog(new PracticeQuestionListContext("Reached end of the search. No more results available", new PracticeQuestionListAdditionalInfo()));
}
} else {
//TODOM - I should probably display the error in case there is an error from the server
console.log("ignoring value");
}
},
(err:Result)=>{
console.log("received error from QuestionArray observable",err);
//TODOM - probably should change the name of DialogContext to Component specific additional context
this.showDialog(new PracticeQuestionListContext(err.additionalInfo,new PracticeQuestionListAdditionalInfo()));
},
()=>{
console.log("question mgmt service, questionarray observable completed.");
});
}
}
The issue I am facing is that if I visit the component for the first time, I get the values from the observable after making some selections in the UI (expected behavior). Then i visit the home page of the application, the component gets destroyed (again, expected behavior). Then if I visit the component again, the observable sends the old values (from the first visit) even when I have not made any UI selections.
Why is the observable sending old values and how can I stop it from doing that? I have created another question in SO with pictures which might explain the scenario better
angular component is retaining old value maybe because the observable is resending past data
const { payload: {loginType, email, password, notification, self} } = action;
console.log("--TRY--");
Firebase.login(loginType, { email, password })
.catch(function(result) {
const message =
result && result.message ? result.message : 'Sorry Some error occurs';
notification('error', message);
self.setState({
confirmLoading: false
});
isError = true;
})
.then(function(result) {
if (isError) {
return;
}
if (!result || result.message) {
const message =
result && result.message
? result.message
: 'Sorry Some error occurs';
notification('error', message);
self.setState({
confirmLoading: false
});
} else {
self.setState({
visible: false,
confirmLoading: false
});
console.log("--RIGHT BEFORE I CHECK AUTH STATE--");
//the following does NOT fire
firebaseAuth().onAuthStateChanged(function*(user) {
console.log("THE GENERATOR RUNS");
if (user) {
console.log(user);
yield put({
type: actions.LOGIN_SUCCESS,
token: 'secret token',
profile: 'Profile'
});
yield put(push('/dashboard'));
}
else {
yield put({ type: actions.LOGIN_ERROR });
}
});
}
}); });
Hi. I'm currently working with redux saga for the first time. I've been trying to get yield put to fire in the callback of the firebaseAuth().onAuthStateChanged listener. The yield keyword won't work in a function that is not an ES6 generator, so I added an asterisk to the callback but now it won't execute at all. Would really appreciate any advice on the matter.
As you noticed, redux-saga effects can only be used within a generator function, and you cannot use a generator function as a regular function: calling a generator function only returns a special object.
The right way to approach this is to use an eventChannel: it lets you connect your saga to a source of events external to the redux ecosystem.
First create your eventChannel using the provided factory function: it hands you an emit function that you can use to emit events; then consume these events using the take effect.
import { eventChannel } from 'redux-saga';
import { cancelled, take } from 'redux-saga/effects';
// first create your eventChannel
const authEventsChannel = eventChannel( emit => {
const unsubscribe = firebaseAuth().onAuthStateChanged( user => {
emit({ user });
});
// return a function that can be used to unregister listeners when the saga is cancelled
return unsubscribe;
});
// then monitor those events in your saga
try {
while (true) {
const { user } = yield take (authEventsChannel);
// handle auth state
}
} finally {
// unregister listener if the saga was cancelled
if (yield cancelled()) authEventsChannel.close();
}
I'm working with Angular2 and a nodejs rest api. I have to do one or more http request for a same task so I'm using Observable.forkJoin() to wait for all of them to finish.
I map the result with the json parsing method and then subscribe to this result but I can't get any json properties from the result the way I used to do.
My service method returns the Observable.forkJoin() itself:
public rename(file:MyFile, newName:string){
let requests = new Array();
for(let i=0; i<file.sources.length; i++){
let url:string = this.serverUrl;
if(src.name === "src1"){
url += "rename/src1";
} else if (src.name === "src2" ){
url += "rename/src2";
}
requests[i] = this.http.get(url)
.map((res:Response) => res.json())
.catch(this.handleError);
}
return Observable.forkJoin(requests);
}
Then I subscribe to it in another method elsewhere:
this.api.rename(this.selectedFile, newFileName).subscribe(
rep => {
// The editor tells me "Property 'name' doesn't exist on type '{}'."
console.log(rep[0].name);
},
err => { console.error(err); }
);
The server correctly respond with the data I asked. The rep[0] is correctly set, it looks like this:
Object {name: "res.png", id: "HyrBvB6H-", size: 0, type: "", isShared: false…}
I suppose it's a typing problem. Usually, with a simple http.get request, it returns an 'any' object. Here it returns an '[]{}' object. res[0] is an '{}' object and I can't get the json properties on it.
Am I using the Observer.forkJoin() correctly? Am I missing something?
Thanks in advance for help :)
If is the editor complaining and it is not an error when the code executes, it likely is a typing problem. You can set the return type of rename() to:
public rename(file:MyFile, newName:string): Observable<any[]> { }
This should allow you access properties of the inner results such as name.
Or you can type the rep array in subscribe() as any[]:
this.api.rename(this.selectedFile, newFileName).subscribe(
(rep: any[]) => {
console.log(rep[0].name);
},
err => { console.error(err); }
);
If all else fails or doesn't work for your solution you can use Type Assertion to treat rep as any[]:
this.api.rename(this.selectedFile, newFileName).subscribe(
rep => {
const responses = rep as any as any[];
console.log(responses[0].name);
},
err => { console.error(err); }
);
If the results structure is consistent across the different endpoints, it would best practice to create an interface/class to replace any[] with.
Hopefully that helps!
http.get is a asynchronous process, so you can't use for loop.
Syntactically you have to nest the gets inside forkJoin, so you have something like this. You can use the for loop to build an array of urls first.:
return Observable.forkJoin([
this.http.get(url[1]).map(res => res.json()),
this.http.get(url[2]).map(res => res.json()),
this.http.get(url[3]).map(res => res.json())
])
.map((data: any[]) => {
this.part1 = data[0];
this.part2 = data[1];
this.part3 = data[2];
});
I wonder if you may be able to do something like this. I'll have a try tomorrow. It's late..
return Observable.forkJoin(let req = [];
for(let i=0; i<file.sources.length; i++){
req[i] = this.http.get(url[i]).map(res => res.json())
}
)
I have a webserver with JSON data in it. This is what my data looks like
[
{
iduser: 1,
username: "joe",
password: "****"
},
{
iduser: 2,
username: "gina",
password: "****"
}
]
In my app I take some user input and wish to compare it to the username and password field. Here is where I check the data
.service('LoginService', function ($q, $http) {
return {
loginUser: function (name, pw) {
var deferred = $q.defer();
var promise = deferred.promise;
var user_data = $http.get("http://<my ip address>:<port>/login");
user_data.then(function ($scope, result) {
$scope.user = result.data;
})
for (var x in $scope.user) {
if (name == x.username && pw == x.password) {
deferred.resolve('Welcome ' + name + '!');
} else {
deferred.reject('Wrong credentials.');
}
}
promise.success = function (fn) {
promise.then(fn);
return promise;
}
promise.error = function (fn) {
promise.then(null, fn);
return promise;
}
return promise;
}
}
})
I am still learning angularJS and I know this is not a secure way to check the data I just want this loop to work.
My understanding of what I have here is that $scope.user holds my JSON data. Then the data is cycled through with the for loop and the user input name is compared to the field username of each iteration. But this is not the case as I am getting a fail every time.
I'm almost certain its a syntax error, but I don't know JavaScript or AngularJS well enough to find the problem. Any help is really appreciated, Thanks.
Edit 1
After what Nujabes said I made some changes since I don't need $scope.
//previous code the same
user_data.then(function (result) {
var user = result.data;
})
for (var x in user) {
if (name == x.username && pw == x.password) {
//prior code the same
I don't think var can hold the data and thats why I'm still getting errors. I think it should be in an array.
I think your syntax error is that you omit $scope.
You should inject $scope service to this line:
.service('LoginService',function($q,$http,$scope){ ...
});
And this code :
user_data.then(function ($scope, result) {
$scope.user = result.data;
});
Omit the $scope.
->
user_data.then(function (result) {
$scope.user = result.data;
});
like this.
Give it a try.
I hope it work.
(However, why do you want to use $scope service in your 'service'?
I think, defining local value and returning some method is a better choice.
and you use the $scope service in your 'controller'.)
$scope.user you are trying to loop through is array right ?
using (for/in) will store the key in the variable x which is in your case the index of each element (0,1,2,..) , to loop through arrays use (for/of) like this :
for (var value of array)
this will give you the values ...