Angular2 return data from validation service after Http call - forms

I have build a validation service for my registration form and one of the static methods is checking if the entered email is available by calling my API the following:
static emailAvailable(control){
let injector = ReflectiveInjector.resolveAndCreate([HTTP_PROVIDERS]);
let http = injector.get(Http);
let valid = "E-mail is available";
http.post('https://secretapi.com/email', JSON.stringify({ email: control.value }))
.map((res: Response) => res.json())
.subscribe(function(result){
if(result.success){
valid = result.success; //The console.log on the line below is correct, the one at the bottom of the script never changes.
console.log(valid);
return null; //Doesn't do anything?
}else{
valid = result.error; //The console.log on the line below is correct, the one at the bottom of the script never changes.
console.log(valid);
return { 'invalidEmailAddress': true }; //Doesn't do anything, just like the return above
}
});
console.log(valid); //Output always "E-mail is available"
}
It should return "null" to the form validator when the email is available. The last console.log at the bottom should output the message that it recieves in the subscribe call. This doesn't happen and I'm not sure why. For some reason everything that happens within the subscribe call is contained there and never reaches the validator. What should I change? I have no idea and been searching the web for hours now.

You have to return Observable or Promise from your validator:
return http.post('https://secretapi.com/email', ...
console.log(...) doesn't make any sense here, since it will be executed after the Observable has been created as an object, but not after the ajax call has bee made.
If you want to output something after a response has been received, you have to move it inside subscribe

So in the end this website had the right answer. Also important to notice with the Angular2 Form validator to put the Async validators in the third (3) parameter and not together in an array in the second (2) parameter. That took me about 3 hours to figure out.
function checkEmail(control: Control){
let injector = ReflectiveInjector.resolveAndCreate([HTTP_PROVIDERS]);
let http = injector.get(Http);
return new Observable((obs: any) => {
control
.valueChanges
.debounceTime(400)
.flatMap(value => http.post('https://secretapi.com/email', JSON.stringify({ email: control.value })))
.map((res: Response) => res.json())
.subscribe(
data => {
if(data.success){
obs.next(null);
obs.complete();
} else {
obs.next({ 'invalidEmailAddress': true });
obs.complete();
}
}
);
});
}
The validator should look something like this, with the first validators checking on required and if it's actually an email address and the last doing an async call to the server to see if it's not already in use:
this.registerForm = this.formBuilder.group({
'email': ['', [Validators.required, ValidationService.emailValidator], ValidationService.emailAvailable],
});

Related

How to display API data using fetch on Dialogflow GUI

I want to display data from OMDB API on Dialogflow GUI but it's not happening. Data is being displayed fine on Google Cloud Console.
function infoHandler(agent){
let movieName = agent.parameters.movie;
agent.add(`The information for ${movieName} is as follow`);
fetch('http://www.omdbapi.com/?apikey=e255decd%20&s='+ movieName)
.then(result => result.json())
.then((json) => {
let id = json.Search[0].imdbID;
fetch('http://www.omdbapi.com/?apikey=e255decd%20&i=' + id)
.then(result => result.json())
.then((json) => {
agent.add(json.Title + json.Plot + json.imdbRatinng);
return;
}).catch((ex) => {
console.log(ex);
});
})
.catch((e) => {console.log(e);});
The issue is that fetch() causes an asynchronous operation. However, there is nothing that indicates to the Dialogflow handler dispatcher that it is asynchronous and that it should wait for it to complete before sending back a reply. To do that, you'd need to return a Promise.
Fortunately the then/catch chain that you have that are built off the fetch() return a Promise. So all you need to do is return the Promise that they have. This is as simple, in your case, by placing a return before the fetch() call. So it would look something like this:
return fetch('http://www.omdbapi.com/?apikey=e255decd%20&s='+ movieName)
// Other lines remain the same

Google Action Webhook Inline Editor Returns Before the API call

This is my first Google Action project. I have a simple slot after the invocation. User enters the value on prompt and slot invokes the webhook and make a call to API using the user input. All works fine. However the webhook returns to users even before the API call finish processing and returns the value (line 1 conv.add). I do see in the logs that everything from API is logged fine after the webhook returns to user. Below is the code I am using. I am using inline editor. What am I missing? Thanks for help in advance.
const { conversation } = require('#assistant/conversation');
const functions = require('firebase-functions');
var https = require('https');
const fetch = require('node-fetch');
const app = conversation({debug: true});
app.handle('SearchData', conv => {
const body = JSON.stringify({
val: "this is my body"
});
// prepare the header
var postheaders = {
'Content-Type' : 'application/json',
'Auth' : 'MyAuthCreds'
};
fetch('https://host.domain.com/data', {
method: 'post',
body: body,
headers: postheaders,
})
.then(res => res.json())
.then(d => {
console.log(d);
var profile = d;//JSON.parse(d);
console.log(d.entries);
console.log("Length: "+ d.entries.length);
if(d.entries.length > 0)
{
console.log("Data found");
conv.add("Data found"); //line 1
}
else
{
console.log("no data found");
conv.add("no data found"); //line 1
}
})
.catch(function (err) {
// POST failed...
console.log(err);
});
});
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);
Your issue is that your handler is making API calls which are asynchronous, but the Assistant Conversation library doesn't know that you're doing so. So as soon as the handler finishes, it tries to send back a response, but your asynchronous responses (the stuff in the then() blocks) haven't executed yet.
To address this, you need to return a Promise object so the library knows to wait till the Promise is fulfilled before it returns.
Fortunately, in your case, this should be pretty straightforward. fetch and all the .then() blocks return a Promise. So all you need to do is add a return statement in front of the call to fetch. So something like this:
return fetch('https://host.domain.com/data', {

How to set Ionic's LocalStorage as synchronization?

Here's what I want: When I send a net request, hoping the intercept can add headers automatically.
So I made an intercept to realize the goal. I plan to get value by LocalStorage. Howerer the way of it's executive is asynchronous, so the request can't catch up with the storage.
What should I do next?
My code as belows:
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
console.log('into intercept');
let url = req.url;
let opuser;
this.storage.get('userInfo').then((val) => {
opuser = val.Noid; // get the value what I need
});
if (opuser === undefined) {
opuser = 'undefined';
}
const newReq = req.clone({
url: url
}).clone({
setHeaders: { opuser: 'undefined' }
});// set the headers
return next.handle(newReq).pipe(
mergeMap((event: any) => {
return of(event);
})
);
}
You should prepare your header before the interceptor will be called.
Good option:
When your application starts, get the header and store it in a provider. This way you will have synchronous/direct access to it.
opUserHeader: any;
setOpuserHeader(){
return this.storage.get('userInfo').then((val) => {
this.opUserHeader = val.Noid; // get the value what I need
});
}
getOpuserHeader(){
return this.opUserHeader ? this.opUserHeader : 'undefined';
}
and when you start the app, you can call the method from your service in app.component.ts or in the first page(your choice), and your header value will exist in memory:
headersService.setOpuserHeader().then(() => { console.log('Header is set')};
now the interceptor should look very clean and you can directly get the value:
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Get the header from service, attach to the cloned request and continue
const opUserHeader = this.headersService.getOpuserHeader();
const authReq = req.clone({setHeaders: {opuser: opUserHeader}});
return next.handle(authReq);
}
Bad Option:
You can put your existing code in the localStorage promise and get the value from it for all requests but performance will be affected.
Edit for Part 2:
You don't really need the address inside the interceptor function but you should call the POST method with the correct qualifier.
A complex option can be to override the HttpClient and concatenate your address to all the methods(if you don't change the endpoint) or the easiest and most sustainable in your case would be to save the address in a service and require it everytime you need like so:
apiURL = 'http://120.XXX.XXX.XXX:81/';
getApiURL(){
return this.apiURL;
}
and everytime you call the HttpClient:
const apiURL = myService.getApiURL();
this.httpClient.post(apiURL + 'api/login', params);
Good luck!

Protractor- automate the error message on tab out when input field is empty

I have an angular2 application where I am trying to write end to end test cases to automate things.I have just begun with learning Protractor for this and trying to implement a negative test case for a form field where if any field is empty, the error message should be shown. I have tried something like below to automate the form and its working fine.
In my spec.ts-
import userDetailsPage from './userDetails.e2e-po;
it('should fill out User Details', () => {
const userDetail: IUserDetail = {
firstName: 'Lorem',
lastName: 'Ipsum'
};
userDetailsPage.populateUserDetails(userDetail);
});
In userDetails.e2e-po-
populateUserDetails(details: IUserDetail) {
this.fillFirstName(details.firstName)
.fillLastName(details.lastName)
return this;
}
I am writing the below code which automatically inputs the firstName and lastName field.
fillLastName(last: string) {
let el = element(by.css('input[name="lastName'));
el.clear().then(() => {
el.sendKeys(last);
});
return this;
}
The above scenario works fine. But I am also trying to achieve a scenario where I do not input either first name or last name field, should throw me an error message.Can someone let me know what else should I add to achieve this.
I am already handling the validation in my HTML.
Any help is much appreciated.
Instead of details.firstname and details.lastname put empty strings and then validate the error that occurs on the page.
I think you can try the following method as a reusable function
function formValidate(donefn){
newProjBtn.click().then(async function () {
var lastName_fld = element(by.css('input[name="lastName'));
await lastName_fld.sendKeys("", protractor.Key.TAB);
//browser.sleep(2000);
var elm = element(by.css(".error-message"));
elm.isPresent().then(function(result){
if(result){
console.log("Error message displayed")
//some more code to do like selecting the field and enter the test
return result;
}else{
console.log("Error message not displayed")
return result;
}
})
donefn();
})
I solved it in this way:
await input.sendKeys(protractor.Key.CONTROL, 'a');
await input.sendKeys(protractor.Key.BACK_SPACE);
await input.sendKeys(protractor.Key.TAB);
//then the error-message will appear

Asynchronous Request to Facebook API with Redux

I am using Redux and trying to make a call to Facebook API with their JS SDK. I've only ever used promises with Redux and so since the method FB.getLoginStatus just returns a simple JS object, I'm not sure how to ensure that the payload doesn't return undefined.
With redux-promise, you add it to the applyMiddleware(ReduxPromise)... and then it ensures nothing is returned until the promise resolves. But I don't know how to do that here.
I've also used async/await functions with React Native without an issue, but I tried using them here and for some reason the code still returns the payload, before the asynchronous request (await ...) is finished. So I tried working with redux-await, but couldn't get it to work.
export function getLoginStatus() {
var res = FB.getLoginStatus(function(res) {
console.log(res);
});
console.log("res ", res);
return {
type: GET_LOGIN_STATUS,
payload: res
}
}
Hm, things can get a little tricky as I've not used redux-promise. And I can't tell exactly what else you have tried. But this would be my first shot:
async function _getLoginStatus() {
var payload = new Promise( (resolve, fail) => {
FB.getLoginStatus((res)=>resolve(res));
});
return {
type: GET_LOGIN_STATUS,
payload: payload
}
}
// Last time I exported an async function I needed this HYMMV
export let getLoginStatus = _getLoginStatus;
And then elsewhere in the code:
import {getLoginStatus} from 'whatever.js';
var payloadResult = await getLoginStatus();