play scala tuple form with multiple case class mapping in views - scala

I have a form in controller Authentication.scala:
package controllers
import javax.inject._
import play.api._
import play.api.mvc._
import play.api.data._
import play.api.data.Forms._
import play.api.i18n.Messages.Implicits._
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import models.User
import models.UserProfile
/**
* This controller creates an `Action` to handle HTTP requests to the
* application's home page.
*/
#Singleton
class Authentication #Inject() extends Controller {
val registerForm = Form(
tuple(
"user" -> mapping(
"email" -> nonEmptyText,
"password" -> nonEmptyText
)(User.apply)(User.unapply),
"profile" -> mapping(
"firstname"->nonEmptyText,
"lastname"->nonEmptyText,
"gender" -> ignored(0)
)(UserProfile.apply)(UserProfile.unapply))
)
/**
* Create an Action to render an HTML page with a welcome message.
* The configuration in the `routes` file means that this method
* will be called when the application receives a `GET` request with
* a path of `/`.
*/
def login = Action {
Ok(views.html.login())
}
def loginSubmit = Action {
implicit request =>
// val maybeFoo = request.body.asFormUrlEncoded.get("password").lift(0) // returns an Option[String]
// val something = maybeFoo map {_.toString} getOrElse 0
// println(something)
Ok("Hello")
}
def register = Action{
Ok(views.html.register())
}
def registerSubmit = Action{
implicit request =>
registerForm.bindFromRequest.fold(
formWithErrors => {
// binding failure, you retrieve the form containing errors:
println(formWithErrors)
BadRequest(views.html.register())
},
userData => {
/* binding success, you get the actual value. */
Redirect(routes.Authentication.register())
}
)
}
def forgotPassword = Action{
Ok(views.html.forgot_password())
}
}
And I have a view register.scala.html:
#import b3.vertical.fieldConstructor
#(userForm: Form[tuple(Mapping, Mapping)])(implicit messages: Messages)
#main("Register") {
<!-- REGISTRATION FORM -->
<div class="text-center" style="padding:50px 0">
<div class="logo">register</div>
<!-- Main Form -->
<div class="login-form-1">
<form id="register-form" class="text-left" method="POST" action="#routes.Authentication.registerSubmit">
<div class="login-form-main-message"></div>
<div class="main-login-form">
<div class="login-group">
<div class="form-group">
<label for="reg_email" class="sr-only">Email</label>
<input type="text" class="form-control" id="reg_email" name="email" placeholder="email">
</div>
<div class="form-group">
<label for="reg_password" class="sr-only">Password</label>
<input type="password" class="form-control" id="reg_password" name="password" placeholder="password">
</div>
<div class="form-group">
<label for="password_confirm" class="sr-only">Password Confirm</label>
<input type="password" class="form-control" id="password_confirm" name="reg_password_confirm" placeholder="confirm password">
</div>
<div class="form-group">
<label for="firstname" class="sr-only">First Name</label>
<input type="text" class="form-control" id="firstname" name="firstname" placeholder="First Name">
</div>
<div class="form-group">
<label for="lastname" class="sr-only">Last Name</label>
<input type="text" class="form-control" id="lastname" name="lastname" placeholder="Last Name">
</div>
<div class="form-group login-group-checkbox">
<input type="radio" class="" name="gender" id="male" placeholder="username">
<label for="male">male</label>
<input type="radio" class="" name="gender" id="female" placeholder="username">
<label for="female">female</label>
</div>
<div class="form-group login-group-checkbox">
<input type="checkbox" class="" id="reg_agree" name="reg_agree">
<label for="reg_agree">i agree with terms</label>
</div>
</div>
<button type="submit" class="login-button"><i class="fa fa-chevron-right"></i></button>
</div>
<div class="etc-login-form">
<p>already have an account? login here</p>
</div>
</form>
</div>
<!-- end:Main Form -->
</div>
}
I know that I am not sending the form in the parameter for view.html.register(), but the question is how to implement the import in the views, with such a form like that? I am getting error all the time.
Help much appreciated. Thanks

When you create a form using tuple(mapping(...), mapping(...)) the type of the form is a tuple of the mapping's types. So in this case you want the type of the view's form parameter to be Form[(User, UserProfile)].
#(userForm: Form[(User, UserProfile)])(implicit messages: Messages)
#main("Register") {
... HTML ...
}

Related

Thymeleaf form not submitting to Spring boot controller

I am trying to submit a form to Spring boot controller
This is the thymeleaf part:
<form th:action="#{/change_password}" method="post">
<div class="row">
<div class="col-md-9 register-right">
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">
<h3 class="register-heading">Change password</h3>
<div class="row register-form">
<div class="col-md-6">
<div class="form-group">
<input type="email" th:name="email" id="email" class="form-control" placeholder="Email *" >
<span th:if="${notPresent}" class="alert alert-info" style="color:red; width: 100% !important; border: none; background-color: transparent !important;">This email does not exist!</span>
</div>
<div class="form-group">
<input type="password" th:name="password" id="password" class="form-control" placeholder="Password *" >
</div>
<div class="form-group">
<input type="password" th:name="confirmPassword" id="confirmPassword" class="form-control" placeholder="Confirm *" >
</div>
</div>
<div class="col-md-6">
<input type="submit" class="btnRegister" style="background-color: #ffa600 !important;" value="Change"/>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
This is the Spring boot controller method:
#PostMapping("/change_password")
public String changeUserPassword(HttpServletRequest request, Model model) {
String path = "";
User u = userService.findByEmail(request.getParameter("email"));
if(u == null || u.getActive() == 0) {
model.addAttribute("notPresent", true);
path = "redirect:/forgot_password";
} else {
u.setPassword(bCryptPasswordEncoder.encode(request.getParameter("password")));
userService.updateUser(u);
sendEmail(u.getEmail(), u.getFirstname());
path = "redirect:/login";
}
return path;
}
I don't get any errors so I am not sure what's wrong.
To submit a form you can use an object userPassword define in a Class like this:
package ....
imports ....
#Data #AllArgsConstructor #NoArgsConstructor #ToString
public class UserPassword {
private Integer id;
private String password;
... other fields you need
}
You add this object to the controller displaying the page with :
model.addAttribute("UserPassword", userPassword);
Then you add this object to your form:
<form th:action="#{/change_password}" th:object="${userPassword}" method="post">
You need to bind data to this object in your html page (here I bind password from userPassword to the input tag):
<input type="password" th:field="*{password}" class="form-control" placeholder="Password *" >
And Finnaly I retrieve the object in the controller:
#PostMapping("/change_password")
public String changeUserPassword(Model model, #ModelAttribute UserPassword userPassword) {
.....
}
You can find a complete tutorial on part 7 from https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html

Angular2 ngForm not working

I am trying to do a login form component but I cannot read the form data.
When I try to write username on the console, 'undefined' writes.
Everything seems usual but form data does not come to component.
Below is the html code:
<form (ngSubmit)="onSubmit(myForm)"
#myForm="ngForm"
class="form-signin">
<div class="form-group">
<h2 class="form-signin-heading">Please sign in</h2>
<input type="text"
id="inputUsername"
name="inputUsername"
class="form-control"
placeholder="User Name"
required>
<input type="password"
id="inputPassword"
name="inputPassword"
class="form-control"
placeholder="Password" >
</div>
<button class="btn btn-lg btn-primary btn-block"
type="submit">Sign in</button>
</form>
Component ts:
#Component({
selector: 'signin',
templateUrl: './signin.component.html',
encapsulation: ViewEncapsulation.None
})
export class SigninComponent implements OnInit{
constructor(){}
ngOnInit(){ }
onSubmit(form: NgForm){
console.log(form.value.inputUsername);
}
}
Thanks in advance.
You need to add the ngModel directive to each of the form fields. This registers the form fields to your form.
<input type="text"
id="inputUsername"
name="inputUsername"
class="form-control"
placeholder="User Name"
ngModel
required>
add FormsModule in appmodule.ts
import { FormsModule } from '#angular/forms';
imports: [
FormsModule,
]

templateUrl giving undefined when referring to class properties

While following the tutorial for forms at https://angular.io/docs/ts/latest/guide/forms.html
I keep getting undefined errors whenever referring to any of the properties that are defined in the .ts file as long as I use templateUrl and a seperate .html
When I take the same html and place in the .ts file inside `` in template everything works fine.
So my question is: Does the templateURl file need to define scope somehow? The tutorial doesn't seem to cover anything about it and I assumed that it would get access to the component variables that called it.
import { Component } from '#angular/core';
import { NgForm } from '#angular/forms';
import { Guitar } from './guitar';
#Component({
selector: 'guitar-form',
// template: `
// <div class="container">
// <h1>Guitar Form</h1>
// <form>
// <div class="form-group">
// <label for="brand">Brand</label>
// <select class="form-control" required
// [(ngModel)]="testmodel.brand" name="brand">
// <option *ngFor="let b of brands" [value]="b">{{b}}</option>
// </select>
// TODO: remove this: {{testmodel.brand}}
// </div>
// <div class="form-group">
// <label for="model">Model</label>
// <input type="text" class="form-control" required
// [(ngModel)]="testmodel.model" name="model">
// TODO: remove this: {{testmodel.model}}
// </div>
// <div class="form-group">
// <label for="color">Color</label>
// <input type="text" class="form-control"
// [(ngModel)]="testmodel.color" name="color">
// TODO: remove this: {{testmodel.color}}
// </div>
// <button type="submit" class="btn btn-default">Submit</button>
// </form>
// </div>
// `
templateUrl: 'app/guitar-form.component.html'
})
export class GuitarFormComponent {
brands = ['Fender', 'Gibson', 'Guild', 'Jackson', 'Epiphone', 'Charvel'];
testmodel = new Guitar(69, this.brands[0], 'Stratocaster', 'Black');
submitted = false;
onSubmit() { this.submitted = true; }
// TODO: remove this when we're done
get diagnostic() { return JSON.stringify(this.model); }
}
Funny, the undefined disappeared when I commented it out so I could demonstrate but ironically the diagnostic is no longer working when placed in the html and this was the one aspect that was working earlier. I have verified connectivity on an individual basis...
<div class="container">
<h1>Guitar Form</h1>
<form>
{{ diagnostic }}
<div class="form-group">
<label for="brand">Brand</label>
<select class="form-control" required
[(ngModel)]="testmodel.brand" name="brand">
<option *ngFor="let b of brands" [value]="b">{{b}}</option>
</select>
{{ testmodel.brand }}
</div>
<div class="form-group">
<label for="model">Model</label>
<input type="text" class="form-control" required
[(ngModel)]="testmodel.model" name="model">
TODO: remove this: {{testmodel.model}} -->
</div>
<div class="form-group">
<label for="color">Color</label>
<input type="text" class="form-control"
[(ngModel)]="testmodel.color" name="color">
{{ testmodel.color }}
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
I think you need to add
moduleId: module.id,
to the #Component({...}) decorator.
See also Angular2 - What is the meanings of module.id in component?

Pass radio button values to component as Form data on Form Submit in Angular2

Html template below
<form class="form" #pubForm="ngForm">
<div class="form-group">
<label for="name">Publisher Name:</label>
<input type="text" #name id="name" ngControl="name" #name="ngForm" class="form-control" placeholder="Enter Name" style="width:50%;" required maxlength="50">
<div *ngIf="name.touched && name.errors">
<div class="alert alert-danger" *ngIf="name.errors.required" style="width:50%;">
Name is Required (Maxlength is 50 characters)
</div>
</div>
</div>
<div class="form-group">
<label for="status">Status:</label>
<label class="radio-inline">
<input type="radio" name="options" (click)="model.options = 'active'" [checked]="'active' === model.options">Active
</label>
<label class="radio-inline">
<input type="radio" name="options" (click)="model.options = 'inactive'" [checked]="'inactive' === model.options">Inactive
</label>
</div>
<div class="form-group">
<button type="submit" class="btn btn-default" (click)="onSubmit(pubForm.value)">Submit</button>
<button type="cancel" class="btn btn-default">Cancel</button>
</div>
</form>
Component code below
model = { options: 'active' };
onSubmit(form:any) : void {
form.status = this.model.options;
console.log(form);
}
On console log now I get object on form submit but I want to pass radio button value as name input box value is passed automatically on form submit in my form above. How to do pass radio button selection as form data ? The input name above is being passed automatically on click of Submit.

Angular 2: 'ngFormModel' since it isn't a known native property

My error is
EXCEPTION: Error: Uncaught (in promise): Template parse errors:
Can't bind to 'ngFormModel' since it isn't a known native property ("
<h3 class = "head">MY PROFILE</h3>
<form [ERROR ->][ngFormModel]="form" (ngSubmit)="onSubmit(form.value)">
<div class="row">
"): a#3:7
There is no directive with "exportAs" set to "ngForm" ("stname</label>
<input type="text" id="facebook" class="form-control" ngControl="firstname" [ERROR ->]#firstname="ngForm" >
</div>
"): a#9:85
There is no directive with "exportAs" set to "ngForm" ("/label>
<input type="text" id="facebook" class="form-control col-xs-3" ngControl="lastname" [ERROR ->]#lastname="ngForm" >
</div>
My template,
<h3 class="head">MY PROFILE</h3>
<form [ngFormModel]="form" (ngSubmit)="onSubmit(form.value)">
<div class="row">
<div class="form-group">
<label class="formHeading">firstname</label>
<input type="text" id="facebook" class="form-control" ngControl="firstname" #firstname="ngForm">
</div>
<div *ngIf="firstname.touched">
<div *ngIf="!firstname.valid" class="alert alert-danger">
<strong>First name is required</strong>
</div>
</div>
<div class="form-group">
<label class="formHeading">lastname</label>
<input type="text" id="facebook" class="form-control col-xs-3" ngControl="lastname" #lastname="ngForm">
</div>
<div *ngIf="lastname.touched">
<div *ngIf="!lastname.valid" class="alert alert-danger">
<strong>Last name is required</strong>
</div>
</div>
<div class="form-group">
<label class="formHeading">Profilename</label>
<input type="text" id="facebook" class="form-control col-xs-3" ngControl="profilename" #profilename="ngForm">
</div>
<div class="form-group">
<label class="formHeading">Phone</label>
<input type="text" id="facebook" class="form-control col-xs-3" ngControl="phone" #phone="ngForm">
</div>
<div *ngIf="phone.touched">
<div *ngIf="!phone.valid" class="alert alert-danger">
<strong>Phone number is required</strong>
</div>
</div>
<label class="formHeading">Image</label>
<input type="file" name="fileupload" ngControl="phone">
<div class="form-row btn">
<button type="submit" class="btn btn-primary " [disabled]="!form.valid">Save</button>
</div>
</div>
</form>
My Component
import {Component} from '#angular/core';
import { Http, Response, Headers} from '#angular/http';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
import {contentHeaders} from '../headers/headers';
import {FORM_DIRECTIVES} from '#angular/forms';
import {Router, ROUTER_DIRECTIVES} from '#angular/router';
import {Control, FormBuilder, ControlGroup, Validators} from '#angular/common';
#Component({
templateUrl: './components/profile/profile.html',
directives: [ROUTER_DIRECTIVES, FORM_DIRECTIVES],
})
export class Profile {
http: Http;
form: ControlGroup;
constructor(fbld: FormBuilder, http: Http, public router: Router) {
this.http = http;
this.form = fbld.group({
firstname: ['', Validators.required],
lastname: ['', Validators.required],
profilename: ['', Validators.required],
image: [''],
phone: [''],
});
}
onSubmit(form: any) {
console.log(form);
let body = JSON.stringify(form);
var headers = new Headers();
this.http.post('http://localhost/angular/index.php/profile/addprofile', body, {
headers: headers
})
.subscribe(
response => {
if (response.json().error_code == 0) {
alert('added successfully');
this.router.navigate(['/demo/professional']);
} else {
alert('fail');
}
});
}
}
The problem is that you're still importing from common and especially using the instructions of the old forms.
Import correctly the components for new forms:
import {FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES} from '#angular/forms';
import {FormBuilder, FormGroup, Validators} from '#angular/forms';
And correct the component:
#Component({
...
directives: [FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES]
})
export class AppComponent {
form: FormGroup;
constructor(fbld: FormBuilder) {
this.form = fbld.group({
...
});
}
...
}
Then correct the view: ngFormModel has been replaced by formGroup, and use formControl for your fields:
<form [formGroup]="form" (ngSubmit)="onSubmit(form.value)">
<div class="row">
<div class="form-group">
<label class="formHeading">firstname</label>
<input type="text" id="facebook" class="form-control" [formControl]="form.controls['firstname']" >
</div>
<div *ngIf ="form.controls['firstname'].touched">
<div *ngIf ="!form.controls['firstname'].valid" class = "alert alert-danger">
<strong>First name is required</strong>
</div>
</div>
...
<div class="form-row btn">
<button type="submit" class="btn btn-primary" [disabled]="!form.valid">Save</button>
</div>
</div>
</form>
Edit. From Angular 2.0.0-rc.5, is necessary to remove the directives from the component and import the form modules in AppModule:
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
#NgModule({
imports: [
...
FormsModule,
ReactiveFormsModule
],
...
bootstrap: [AppComponent]
})
export class AppModule { }
If you use a shared module, do not forget to export them:
#NgModule({
imports: [
...
FormsModule,
ReactiveFormsModule
],
exports: [
...
FormsModule,
ReactiveFormsModule
]
})
export class SharedModule { }
I had the same issue. What I did to solve it:
remove the tag, and add (click)-function to the button
checked my backend: there was an error in some special event... fixed it
Now it doesn't fire twice anymore.
So double check this! You never know...
Just import the following statement in ts,
import {FORM_DIRECTIVES, FormBuilder, Validators, REACTIVE_FORM_DIRECTIVES} from '#angular/forms';
directives: [CORE_DIRECTIVES, ROUTER_DIRECTIVES, FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES],
Make the following changes in templates,
<h3 class = "head">MY PROFILE</h3>
<form [ngFormModel]="form" (ngSubmit)="onSubmit(form.value)">
<div class="row">
<div class="form-group">
<label class="formHeading">firstname</label>
<input type="text" id="facebook" class="form-control" [formControl]="form.controls['firstname']">
</div>
<div *ngIf ="firstname.touched">
<div *ngIf ="!firstname.valid" class = "alert alert-danger">
<strong>First name is required</strong>
</div>
</div>
<div class="form-group">
<label class="formHeading">lastname</label>
<input type="text" id="facebook" class="form-control col-xs-3" [formControl]="form.controls['lastname']">
</div>
<div *ngIf ="lastname.touched" >
<div *ngIf = "!lastname.valid" class = "alert alert-danger">
<strong>Last name is required</strong>
</div>
</div>
<div class="form-group">
<label class="formHeading">Profilename</label>
<input type="text" id="facebook" class="form-control col-xs-3" [formControl]="form.controls['profilename']" >
</div>
<div class="form-group">
<label class="formHeading">Phone</label>
<input type="text" id="facebook" class="form-control col-xs-3" [formControl]="form.controls['phone']">
</div>
<div *ngIf ="phone.touched" >
<div *ngIf = "!phone.valid" class = "alert alert-danger">
<strong>Phone number is required</strong>
</div>
</div>
<div class="form-row btn">
<button type="submit" class="btn btn-primary " [disabled]="!form.valid">Save</button>
</div>
Finally do this in your bootstrapping,
import {provideForms, disableDeprecatedForms} from '#angular/forms';
bootstrap(MyDemoApp, [
provideForms(),
disableDeprecatedForms()]);