Showing a system error on a form after submitting via AJAX - ajax

I'm trying to find the best way in Angular to invalidate a form not due to a specific element, but due to a system-level error AFTER submission via AJAX. For example, you could put in a valid email and password (both good strings), press submit, and find out there is a system error that should trigger a generic error message on the form. Since this isn't tied to anything in the data model, what is the best way I can generically call the form 'invalid'?
<form name="loginForm" class="loginForm" ng-submit="loginSubmit(loginForm)">
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" name="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email" ng-model="login.email" required>
<span class="error" ng-show="loginSubmit.email.$error">Required!</span><br>
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password" ng-model="login.password" minlength="8" required>
</div>
<button type="submit" class="btn btn-default">Log In</button>
</form>
and...
ngModule.controller('LoginController', function($scope, $location) {
$scope['login'] = {};
$scope['loginSubmit'] = function(form) {
var loginPromise = myAsyncLoginFuncYouCanAssumeWorks();
loginPromise.done(function(){
$location.path('/');
});
loginPromise.fail(function() {
//how best to trigger a generic error in the form here?
});
};
});
As you can see, I'd like to trigger some form-wide error state after submission. It really could be as simple as adding an invalid form class to the form, but again, I'd like to know the purest Angular way to do this.

Add a label to your form with your generic error which shows upon a scope variable being true when an error occurs:
<div class="alert alert-danger" role="alert" ng-show="loginError">There was an error with your login details. Please check them and try again</div>
then when your promise fails:
loginPromise.fail(function () {
$scope.loginError = true;
});
maybe also could be nice if you have many system messages to abstract them all out into a separate service so you can inject the systemmessages service into your controller and then simply bind:
<div class="alert alert-danger" role="alert" ng-show="loginError">{{ systemMessages.loginError }}</div>
Alternatively as you use Bootstrap maybe inject the $modal service and show the error message inside a popup.
It is also important to make sure you try to use a bearer token stored in localatorage as oppose to cookies for persistence, so it doesn't get sent to the server on each request.
Anti forgery token would also be very beneficial for SPAs.

Your server could return some sort or error pay load with error key or error code.
{errors:[{key:"invalid.password"}]}
Assign the error response to your scope:
loginPromise.fail(function(response) {
$scope.errors = response.data;
});
Next, add filter to translate error key/code into error messages:
angular.module('mtApp').filter('errorFilter', function() {
return function(e) {
if (e === 'invalid.password') {
return 'Invalid password, please try again.';
}
};
});
Finally, display the appropriated errors as a list:
<div ng-show="errors">
<ul>
<li ng-repeat="e in errors">{{e.key | errorFilter}}</li>
</ul>
</div>
Optionally, you can reuse this same "$scope.erros" object combined to ng-class and control the CSS of each field with error.

Related

What is this.loading = false?

I am using Laravel and Vue.
When I was searching on the internet I saw the following code.
<template>
<div>
<h3 class="text-center">Create Movie</h3>
<div class="row">
<div class="col-md-6">
<form #submit.prevent="createMovie">
<div class="form-group">
<label>Name</label>
<input type="text" class="form-control" v-model="movie.name">
</div>
<div class="form-group">
<label>Director</label>
<input type="text" class="form-control" v-model="movie.director">
</div>
<button type="submit" class="btn btn-primary">Create movie</button>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
movie: {}
}
},
methods: {
createMovie() {
this.axios
.post('http://localhost:8000/api/movie/create', this.movie)
.then(response => (
this.$router.push({name: 'movie'})
))
.catch(error => console.log(error))
.finally(() => this.loading = false)
}
}
}
I am trying to find out what the last line
.finally(() => this.loading = false)
is doing. I am searching on the internet but I can't find what it does. Also, I tried running the code without the last line however, it did not make any change.
Can someone please tell me what this is doing and when it is useful?
Without seeing the associated Vue template we cannot explain what it is doing exactly, however, we can be fairly confident that the value of loading will be used to show/hide some sort of overlay or activity spinner.
The purpose of the overlay/activity spinner is to provide visual feedback to the user that something is happening. This is useful when loading large amounts of data into your page, or when you perform a long running process (such as uploading a large file for example). So rather than the user seeing nothing on first page load, or clicking a button and wondering if it worked, they are provided with something to let them know that something is happening.
A basic example of what this might look like in the Vue template could be:
// if the value of loading is true, show this
<div v-if="this.loading">Loading, please wait ...</div>
// otherwise show this
<div v-else>Other content</div>
Your example is setting the value of loading to false once a response has been received from your axios request. You would probably want to set the value of loading to true prior to making the request to show an overlay/activity spinner.
Uncaught (in promise) ReferenceError: ture is not defined
You have a typo, it should be true not ture.

Error Handling in Thymeleaf and Spring boot

In my spring boot thymeleaf application, i am struggling to figure out how to display the error message returned from the service layer onto the UI.
My UI Code (index.html) is
<div class="u-expanded-width-xs u-form u-form-1">
<form class="u-clearfix u-form-horizontal u-form-spacing-10 u-inner-form"
method="POST"
modelAttribute="indexFormBean" name="form"
style="padding: 0;"
th:action="#{/home}" th:object="${indexFormBean}">
<div class="u-form-group u-form-name u-form-group-1">
<label for="email-dbf3"
class="u-form-control-hidden u-label">Email</label>
<input type="email" placeholder="Enter a valid email address"
id="email-dbf3" name="email"
class="u-border-1 u-border-grey-30 u-input u-input-rectangle u-white"
required="true" autofocus="autofocus">
</div>
<div class="u-form-email u-form-group u-form-group-2">
<label for="name-dbf3"
class="u-form-control-hidden u-label">Name</label>
<input type="text" placeholder="Enter a valid password" id="name-dbf3"
name="password"
class="u-border-1 u-border-grey-30 u-input u-input-rectangle u-white"
required="true">
</div>
<div class="u-align-left u-form-group u-form-submit u-form-group-3">
<button type="submit" name="submit" class="btn btn-primary btn-lg">Login
</button>
</div>
<p th:if="${#fields.hasErrors('email')}" th:errors="*{email}">Description errors</p>
</form>
</div>
The controller method invoked when the form is submitted is like this
#PostMapping("/home")
public String authenticate(#ModelAttribute IndexFormBean indexFormBean, Model model){
String loginResponse = userService.login(indexFormBean.getEmail(),
indexFormBean.getPassword());
if(StringUtils.isEmpty(loginResponse)){
//Some error that is returned from the service layer
return "index";
}
return "home";
}
Now if the authenticate method needs to show some error on the UI (index.html) how can I do so ?
There are two ways you can handle error messages in MVC.
Use an error.html template. Spring boot will automatically use this template if it encounters any uncaught exceptions within controller method.
Catch the exception in your controller method and add appropriate error message to your model. You can then use the error message somewhere in your index.html or home.html
You can do something like,
if(StringUtils.isEmpty(loginResponse)){
model.addAttribute("errorMessage","Login failed");
return "index";
}
And in your index.html, add something like,
<span th:if="${errorMessage}" th:text="${errorMessage}">Error</span>

Server side validation for angular 2 forms

I am trying to figure out how I can send some data to a server side API call, let that do the validation and then return back with some validation errors and those validation errors should be displayed along with the component that caused the validation error. Here is how my data model looks like:
export class Order {
orderNo: string;
items: Item[];
constructor() {
this.items = [];
}
}
export class Item {
recNo: string;
}
This is how the HTML form looks like
<div class="form-group">
<label name="orderNo">Order Number</label>
<input type="text" formControlName="orderNo" />
</div>
<div formArrayName="items">
<div *ngFor="let item of orderForm.controls.items.controls; let i=index" [formGroupName]="i">
<div class="form-group">
<label>Item # {{i}}</label>
<input type="text" formControlName="recNo" />
</div>
</div>
</div>
The validation errors that I receive from the server are in this form
[
{
"field": "orderNo",
"message": "order number is required"
},
{
"field": "items[2].recNo",
"message": "record number is required"
}
]
I am not able to figure out how, once I receive the validation errors, can I update the errors objects on the form controls or populate some other structure to show the errors. Its a bit simpler for static fields like orderNo but a lot more complex for nested array fields like items[0].recNo to update the errors programmatically. Any ideas would be appreciated.
This is what I ended up doing:
<div class="form-group" [class.has-danger]="!ctrl.valid && ctrl.enabled">
<input type="text" class="form-control" [formControl]="ctrl"
value="{{valueFormatter(ctrl)}}"
[class.form-control-danger]="!ctrl.valid && ctrl.enabled"
[ngbTooltip]="ctrl.errors ? ctrl.errors.e : falsy" />
</div>
Basically, setting up error classes on the form control based on the ctrl valid/error state and here is an example of specifying an error on a control:
this.ctrl.setErrors({ "e": this.label + " is required"});
I just used a predefined error key e to display any errors. This way, once I receive the error list from the backend, I can set the e error on a control and have that show up in a tooltip on the control.

How do I use an Angular.JS generated form to upload a file (and other fields) through AJAX to Symfony2?

I'm trying to send a form generated by Angular.js through an AJAX call to a Symfony controller action in order to be saved. The form is generated in an ng-repeat after receiving some JSON data (including an entry ID). The code looks as follows.
<div ng-repeat="job in sc.careers">
<div>
<h2>{[{job.title}]}</h2>
<span ng-if="job.super">{[{job.super}]}</span>
<div>
<h4>Role</h4>
<span ng-bind-html="job.role"></span>
</div>
<div class="col-md-4">
<h4>Required Skills</h4>
<span ng-bind-html="job.skills"></span>
</div>
<div class="col-md-4">
<h4>Media</h4>
Some other content \ media
</div>
</div>
<hr/>
<div class="join-us">
<button ng-click="sc.joinUs[$index] = true" ng-hide="sc.joinUs[$index]">Apply for this position</button>
<div ng-show="sc.joinUs[$index]">
<h4>Join Us</h4>
<span>Some random text</span>
<form>
<input type="hidden" name="job_id" value="{[{job.id}]}" />
<label for="email">E-Mail</label>
<input type="email" id="email" name="email" />
<label for="motivation">Cover Letter & CV link</label>
<textarea id="motivation" name="motivation"></textarea>
<label for="resume">Upload your resume</label>
<input type="file" name="resume">
<button type="submit">Send your application</button>
</form>
</div>
</div>
</div>
* most class names have been redacted to keep the code as relevant to the question as possible
I have not yet placed an ng-click -> submit directive. Also, my Angular is configured for the use of the "{[{" and "}]}" delimiters so as not to interfere with TWIG.
I have searched the internet for possible answers but they all pertain to Symfony generated forms (which include validation tokens). Other answers (such as this one) don't quite describe the Angular side of things or don't describe sending the entire form.
In the end, I'm not exactly sure how to approach this. If it's too complicated I'd even settle for not using AJAX at all and submitting directly to Symfony. (actually, the only reason I want to use AJAX is to make the site feel more "snappy").
For what it's worth I'm using the latest stable versions of PHP, Symfony and Angular.JS at the time of writing.
Update
So i managed to send the data back to the controller by using an Agular JS module that enables the use of ng-model on file inputs and by using FormData like so:
var formObject = new FormData;
formObject.append('email', self.careers[index].application.email);
formObject.append('motivation', self.careers[index].application.motivation);
formObject.append('resume', self.careers[index].application.file);
formObject.append('jobID', self.careers[index].id);
$http.post('/app_dev.php/jobs/apply', formObject, {
transformRequest: angular.identity,
headers: { 'Content-Type': undefined } // Allows angular to choose the proper Content-Type
})
The only problem left now is that Symfony does not recognize my form data as being valid. The controller action looks like this (for now).
public function applyAction(Request $request) {
$jobApplication = new JobApplications(); // This is the entity I use to store everything.
$form = $this->createFormBuilder($jobApplication)
->add('jobId')
->add('email')
->add('coverLetter')
->add('file')
->getForm();
$form->handleRequest($request);
$response = array (
'isValid' => $form->isValid(), // false
'isSubmitted' => $form->isSubmitted(), // false < that's why the form is invalid
'isErrors' => $form->getErrorsAsString() // empty array
);
if ($form->isValid()) { // is not valid so the following section is not complete
$em = $this->getDoctrine()->getManager();
$jobApplication->upload();
$em->persist($jobApplication);
$em->flush();
//return $this->redirect($this->generateUrl('idea_presentation_careers'));
}
return new JsonResponse($response);
}

Hot Towel Angular data-ng-show not working during validation

Hello anyone has anyone tried doing a validation using angularjs in a with Hot Towel template?
Basically i have a property in my angular controller which is binded(two way) in my view.
I just want to do a simple required validation, and then show a <span> element with the message.
here is my controller code
(function () {
'use strict';
var controllerId = 'login';
angular.module('app').controller(controllerId, ['$scope', 'common', 'userservice','$location', login]);
function login($scope, common, userservice, $location) {
var getLogFn = common.logger.getLogFn;
var log = getLogFn(controllerId);
var vm = this;
vm.title = 'Login';
//view model for credentials
vm.email = null;
activate();
function activate() {
common.activateController([], controllerId)
.then(function () { log('Activated Login View'); });
}
}
})();
and this is my view
<div data-ng-controller="login as vm">
<form name="loginform" id="loginform" novalidate data-ng-submit="loginuser()">
<fieldset>
<legend>Login</legend>
<p>
<label>Email</label>
<input type="email" data-ng-model="email" placeholder="Email" required />
<span data-ng-show="loginform.email.$error.required">*</span>
</p>
</fieldset>
</form>
</div>
i dont know what the problem is, but the <span> just wont show. am i missing something?
You have to set 'name' attribute to your input email :
<input type="email" name="email" data-ng-model="email" placeholder="Email" required />
For each field of your form, angularjs will set a value like: loginform[name-attribute].
For your information, your span will be visible only when required error is triggered (when your email is not empty, your span will be hidden).
[EDIT] See this fiddle: http://jsfiddle.net/k82at

Resources