mirror of
https://github.com/st-tu-dresden-praktikum/swt23w23
synced 2024-07-19 21:04:36 +02:00
Add form validator to users package
Closes #35 Closes #59 Closes #64 Closes #71 Co-authored-by: Simon Bruder <simon.bruder@mailbox.tu-dresden.de>
This commit is contained in:
parent
b72fa87445
commit
d7c4482200
|
@ -285,7 +285,8 @@ image:models/design/user.svg[class design diagram - User]
|
|||
|UserManagement |A class that manages the UserRepository.
|
||||
|UserRepository |An extension of 'CrudRepository' to store Users.
|
||||
|User |A class that allows a person to associate system data with themselves.
|
||||
|RegistrationForm |A Form to cache a user input that was made while registration.
|
||||
|UserForm |A Form to cache a user input that was made during registration or updating the profile.
|
||||
|FormValidator |A validator that validates the UserForm.
|
||||
|===
|
||||
|
||||
=== Staff
|
||||
|
|
|
@ -38,7 +38,9 @@ UserController -> RegistrationForm : getPassword()
|
|||
UserController <-- RegistrationForm : String
|
||||
UserController -> RegistrationForm : getAddress()
|
||||
UserController <-- RegistrationForm : String
|
||||
UserController -> UserManagement : createCustomer(String,String,String)
|
||||
UserController -> RegistrationForm : getFullName()
|
||||
UserController <-- RegistrationForm : String
|
||||
UserController -> UserManagement : createCustomer(String,String,String,String)
|
||||
activate UserManagement
|
||||
UserManagement -> UserRepository : "save(User:customer)"
|
||||
activate User_customer
|
||||
|
@ -55,7 +57,7 @@ deactivate Spring
|
|||
|
||||
== Disable Customer by Customer ==
|
||||
|
||||
Spring -> UserController : disableUser()
|
||||
Spring -> UserController : disableUser(UserAccount:userAccount)
|
||||
activate User_customer
|
||||
activate Spring
|
||||
activate UserController
|
||||
|
@ -88,9 +90,15 @@ UserManagement -> UserRepository : findAll()
|
|||
activate UserRepository
|
||||
UserManagement <-- UserRepository : Streamble
|
||||
deactivate UserRepository
|
||||
UserController <-- UserManagement : Optional<User:user>
|
||||
UserController <-- UserManagement : Optional<User>
|
||||
UserController -> User : getUsername()
|
||||
UserController <-- User : String
|
||||
UserController -> User : getFullName()
|
||||
UserController <-- User : String
|
||||
UserController -> User : getAddress()
|
||||
UserController <-- User : String
|
||||
deactivate UserManagement
|
||||
UserController -> Spring : model.addAttribute("user",User:user)
|
||||
UserController -> Spring : model.addAttribute("profileForm",ProfileForm)
|
||||
UserController <-- Spring : Model
|
||||
Spring <-- UserController : "profile"
|
||||
deactivate Spring
|
||||
|
@ -99,7 +107,7 @@ deactivate User
|
|||
|
||||
== Edit Profile ==
|
||||
|
||||
Spring -> UserController : editProfile(UserAccount:LoggedIn,String:password,String:address,String:username)
|
||||
Spring -> UserController : editProfile(UserAccount:LoggedIn,ProfileForm:form,Erros:result,Model:model)
|
||||
activate User
|
||||
activate Spring
|
||||
activate UserController
|
||||
|
@ -111,23 +119,20 @@ UserManagement <-- UserRepository : Streamble
|
|||
deactivate UserRepository
|
||||
UserController <-- UserManagement : Optional<User:user>
|
||||
deactivate UserManagement
|
||||
UserController -> User : [!String:username.isBlank()] setUsername(String:username)
|
||||
UserController -> User : setUsername(String:username)
|
||||
UserController <-- User : boolean
|
||||
UserController -> User : [!String:address.isBlank()] setAddress(String:address)
|
||||
UserController -> User : setAddress(String:address)
|
||||
UserController <-- User : boolean
|
||||
UserController -> User : [!String:password.isBlank()] setPassword(String:password)
|
||||
UserController -> User : [!form.getUsername().equals(user.getUsername())] setPassword(String:password)
|
||||
UserController <-- User : boolean
|
||||
UserController -> UserManagement : save(User:LoggedIn)
|
||||
UserManagement -> UserRepository : "save(User:LoggedIn)"
|
||||
activate UserManagement
|
||||
UserManagement -> UserRepository : save(User:LoggedIn)
|
||||
activate UserRepository
|
||||
UserManagement <-- UserRepository : User
|
||||
deactivate UserRepository
|
||||
UserController <-- UserManagement : User
|
||||
activate UserManagement
|
||||
UserController <-- UserManagement : User
|
||||
deactivate UserManagement
|
||||
Spring <-- UserController : [!username.isBlank()] "redirect:/profile"
|
||||
Spring <-- UserController : "redirect:/profile"
|
||||
deactivate Spring
|
||||
deactivate UserController
|
||||
deactivate User
|
||||
|
|
BIN
src/main/asciidoc/models/design/seq_users.svg
(Stored with Git LFS)
BIN
src/main/asciidoc/models/design/seq_users.svg
(Stored with Git LFS)
Binary file not shown.
|
@ -9,55 +9,68 @@ package Salespoint {
|
|||
class UserAccount
|
||||
class AbstractAggregateRoot
|
||||
class Role
|
||||
class Errors
|
||||
}
|
||||
|
||||
package Spring {
|
||||
class CrudRepository
|
||||
class Streamable
|
||||
class WebDataBinder
|
||||
class Errors
|
||||
interface Validator
|
||||
}
|
||||
|
||||
package catering.users {
|
||||
class User {
|
||||
- address : String
|
||||
+ User(userAccount : UserAccount, address : String)
|
||||
- fullName : String
|
||||
+ User(userAccount : UserAccount, address : String, fullName : String)
|
||||
+ User()
|
||||
+ getAddress() : String
|
||||
+ setAddress(address:String) : String
|
||||
+ getFullName() : String
|
||||
+ setFullName(fullName:String) : String
|
||||
+ getUsername() : String
|
||||
+ setUsername(username:String) : boolean
|
||||
+ getUserAccount() : UserAccount
|
||||
+ getId() : UserIdentifier
|
||||
+ isEnabled() : boolean
|
||||
+ hasRole(role:String) : boolean
|
||||
+ equals(obj:Object) : boolean
|
||||
+ hashCode() : int
|
||||
}
|
||||
User ..> Role
|
||||
User ..> WebDataBinder
|
||||
User --> UserAccount : "-userAccount"
|
||||
User --|> AbstractAggregateRoot : <<extends>>
|
||||
|
||||
class UserController {
|
||||
+ UserController(userManagement: UserManagement)
|
||||
- initProfileBinder(binder : WebDataBinder) : void
|
||||
- initRegistrationBinder(binder : WebDataBinder) : void
|
||||
+ unauthorized() : String
|
||||
+ register() : String
|
||||
+ register(form:RegistrationForm,result:Erros) : String
|
||||
+ register(form:RegistrationForm,result:Errors) : String
|
||||
+ loginPage() : String
|
||||
+ viewProfile(model:Model,userAccount:UserAccount) : String
|
||||
+ editProfile(userAccount:UserAccount,username:String,password:String,address:String) : String
|
||||
+ editProfile(userAccount:UserAccount,form:ProfileForm,result:Errors,model:Model) : String
|
||||
+ disableUser(userAccount:UserAccount) : String
|
||||
+ getCustomer(model:Model) : String
|
||||
+ removeCustomer(user:User,model:Model) : String
|
||||
+ removeCustomer(user:User) : String
|
||||
+ editCustomer(user:User,model:Model) : String
|
||||
+ updateCustomer(user:User,username:String,address:String,model:Model)
|
||||
+ updateCustomer(user:User,username:String,address:String)
|
||||
}
|
||||
UserController --> UserManagement : "-userManagement"
|
||||
UserController ..> User
|
||||
UserController ..> UserAccount
|
||||
UserController ..> Role
|
||||
UserController ..> RegistrationForm
|
||||
UserController ..> UserForm
|
||||
UserController ..> FormValidator
|
||||
UserController ..> WebDataBinder
|
||||
|
||||
class UserManagement {
|
||||
+ UserManagement(users:UserRepository,userAccounts:UserAccountManagement)
|
||||
+ createCustomer(name:String, password:String, address:String) : User
|
||||
+ createAdmin(name:String, password:String, address:String) : User
|
||||
+ createAdmin(name:String, password:String, address:String,fullName:String) : User
|
||||
+ save(user:User) : User
|
||||
+ getUsers() : UserRepository
|
||||
+ disableUserAccount(userAccount:UserAccount) : void
|
||||
|
@ -79,15 +92,22 @@ package catering.users {
|
|||
UserRepository --|> CrudRepository : <<extends>>
|
||||
UserRepository ..|> Streamable
|
||||
UserRepository o-- User
|
||||
class RegistrationForm {
|
||||
class UserForm {
|
||||
- username: String
|
||||
- password: String
|
||||
- address: String
|
||||
+ RegistrationForm(username:String,password:String,address:String)
|
||||
- password: String
|
||||
+ UserForm(username:String,address:String,fullName:String,password:String)
|
||||
+ getUsername() : String
|
||||
+ getPassword() : String
|
||||
+ getAddress() : String
|
||||
+ validate(errors:Erros) : void
|
||||
+ getFullName() : String
|
||||
+ getPassword() : String
|
||||
}
|
||||
class FormValidator {
|
||||
supports(c:Class<?>) : boolean
|
||||
validate(o:Object,e:Errors) : void
|
||||
}
|
||||
FormValidator ..> Validator
|
||||
FormValidator --> Errors
|
||||
}
|
||||
@enduml
|
||||
|
|
BIN
src/main/asciidoc/models/design/user.svg
(Stored with Git LFS)
BIN
src/main/asciidoc/models/design/user.svg
(Stored with Git LFS)
Binary file not shown.
25
src/main/java/catering/users/FormValidator.java
Normal file
25
src/main/java/catering/users/FormValidator.java
Normal file
|
@ -0,0 +1,25 @@
|
|||
package catering.users;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.ValidationUtils;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
@Component
|
||||
public class FormValidator<F extends UserForm> implements Validator {
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> c) {
|
||||
return UserForm.class.isAssignableFrom(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void validate(Object o, Errors e) {
|
||||
F form = (F) o;
|
||||
ValidationUtils.rejectIfEmptyOrWhitespace(e, "username", "username must not be empty");
|
||||
ValidationUtils.rejectIfEmptyOrWhitespace(e, "address", "address must not be empty");
|
||||
ValidationUtils.rejectIfEmptyOrWhitespace(e, "fullName", "fullName must not be empty");
|
||||
form.validatePassword(e);
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package catering.users;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import java.util.Optional;
|
||||
import org.springframework.validation.Errors;
|
||||
|
||||
public class ProfileForm {
|
||||
|
||||
private final @NotEmpty String username, address, fullName;
|
||||
private final Optional<String> password;
|
||||
|
||||
public ProfileForm(String username, Optional<String> password, String address, String fullName) {
|
||||
this.username = username;
|
||||
this.address = address;
|
||||
this.fullName = fullName;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public Optional<String> getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public void validate(Errors errors) {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package catering.users;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import org.springframework.validation.Errors;
|
||||
|
||||
public class RegistrationForm {
|
||||
|
||||
private final @NotEmpty String password, username, address, fullName;
|
||||
|
||||
public RegistrationForm(String username, String password, String address, String fullName) {
|
||||
this.username = username;
|
||||
this.address = address;
|
||||
this.fullName = fullName;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public void validate(Errors errors) {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,14 +1,17 @@
|
|||
package catering.users;
|
||||
|
||||
import static catering.users.UserForm.RegistrationForm;
|
||||
import static catering.users.UserForm.ProfileForm;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.Errors;
|
||||
|
||||
import org.salespointframework.useraccount.Role;
|
||||
import org.salespointframework.useraccount.UserAccount;
|
||||
import org.salespointframework.useraccount.web.LoggedIn;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
@ -24,13 +27,26 @@ public class UserController {
|
|||
this.userManagement = userManagerment;
|
||||
};
|
||||
|
||||
// the binder associates the form validator with the corresponding form
|
||||
// as a result, it overrides every validator annotation
|
||||
@InitBinder("profileForm")
|
||||
private void initProfileBinder(WebDataBinder binder) {
|
||||
binder.setValidator(new FormValidator<ProfileForm>());
|
||||
}
|
||||
|
||||
@InitBinder("registrationForm")
|
||||
private void initRegistrationBinder(WebDataBinder binder) {
|
||||
binder.setValidator(new FormValidator<RegistrationForm>());
|
||||
}
|
||||
|
||||
@GetMapping("/unauthorized")
|
||||
String unauthorized(){
|
||||
return "unauthorized";
|
||||
}
|
||||
|
||||
@GetMapping("/register")
|
||||
String register() {
|
||||
String register(Model model) {
|
||||
model.addAttribute("registrationForm", RegistrationForm.empty());
|
||||
return "register";
|
||||
}
|
||||
|
||||
|
@ -39,9 +55,6 @@ public class UserController {
|
|||
if (result.hasErrors()){
|
||||
return "register";
|
||||
}
|
||||
if (form.getPassword().chars().anyMatch(Character::isISOControl)) {
|
||||
return "register";
|
||||
}
|
||||
userManagement.createCustomer(form.getUsername(),form.getAddress(),form.getPassword(),form.getFullName());
|
||||
return "redirect:/login";
|
||||
}
|
||||
|
@ -58,14 +71,18 @@ public class UserController {
|
|||
return "redirect:/";
|
||||
}
|
||||
User user = userManagement.getUserByAccount(userAccount).get();
|
||||
model.addAttribute("user", user);
|
||||
model.addAttribute("profileForm", new ProfileForm(user.getUsername(), user.getAddress(), user.getFullName(), ""));
|
||||
return "profile";
|
||||
}
|
||||
|
||||
@PostMapping("/profile")
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
public String editProfile(@LoggedIn UserAccount userAccount, @Valid ProfileForm form) {
|
||||
public String editProfile(@LoggedIn UserAccount userAccount, @Valid ProfileForm form, Errors result, Model model) {
|
||||
String redirect = "redirect:/logout";
|
||||
|
||||
if (result.hasErrors()){
|
||||
return "profile";
|
||||
}
|
||||
User user = userManagement.getUserByAccount(userAccount).get();
|
||||
|
||||
if (form.getUsername().equals(user.getUsername())) {
|
||||
|
@ -74,10 +91,8 @@ public class UserController {
|
|||
user.setUsername(form.getUsername());
|
||||
user.setFullName(form.getFullName());
|
||||
user.setAddress(form.getAddress());
|
||||
if (!form.getPassword().get().isEmpty()) {
|
||||
if (form.getPassword().get().chars().anyMatch(Character::isISOControl)) {
|
||||
userManagement.setPassword(form.getPassword().get(), user.getUserAccount());
|
||||
}
|
||||
if (!form.getPassword().isEmpty()) {
|
||||
userManagement.setPassword(form.getPassword(), user.getUserAccount());
|
||||
}
|
||||
userManagement.save(user);
|
||||
|
||||
|
|
97
src/main/java/catering/users/UserForm.java
Normal file
97
src/main/java/catering/users/UserForm.java
Normal file
|
@ -0,0 +1,97 @@
|
|||
package catering.users;
|
||||
|
||||
import org.springframework.validation.ValidationUtils;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.validation.Errors;
|
||||
|
||||
/**
|
||||
* An abstract class to hold form input for {@link User} data.
|
||||
*
|
||||
* It can be extended to validate input for different use cases.
|
||||
*/
|
||||
public abstract class UserForm {
|
||||
|
||||
private final String username, address, fullName, password;
|
||||
|
||||
public UserForm(String username, String address, String fullName, String password) {
|
||||
this.username = username;
|
||||
this.address = address;
|
||||
this.fullName = fullName;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template Method for validating the password.
|
||||
*
|
||||
* @param e {@link Errors} from {@link FormValidator}
|
||||
*/
|
||||
public void validatePassword(Errors e) {
|
||||
validatePasswordGeneric(e);
|
||||
Optional.ofNullable(e.getFieldValue("password")).map(s -> (String) s).ifPresent(s -> {
|
||||
if (s.chars().anyMatch(Character::isISOControl)) {
|
||||
e.rejectValue("password", "password must only contain printable characters");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Primitive Operation for validating the password depending on the subclass.
|
||||
*
|
||||
* @param e {@link Errors} from {@link FormValidator}
|
||||
*/
|
||||
public abstract void validatePasswordGeneric(Errors e);
|
||||
|
||||
/**
|
||||
* An extension of {@link UserForm} to validate input during registration.
|
||||
*
|
||||
* It requires the password to be not empty.
|
||||
*/
|
||||
public static class RegistrationForm extends UserForm {
|
||||
RegistrationForm(String username, String address, String fullName, String password) {
|
||||
super(username, address, fullName, password);
|
||||
}
|
||||
|
||||
public static RegistrationForm empty() {
|
||||
return new UserForm.RegistrationForm("", "", "", "");
|
||||
}
|
||||
|
||||
public void validatePasswordGeneric(Errors e) {
|
||||
ValidationUtils.rejectIfEmptyOrWhitespace(e, "password", "password must not be empty");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An extension of {@link UserForm} to validate input during profile editing.
|
||||
*
|
||||
* The password can be empty.
|
||||
*/
|
||||
public static class ProfileForm extends UserForm {
|
||||
ProfileForm(String username, String address, String fullName, String password) {
|
||||
super(username, address, fullName, password);
|
||||
}
|
||||
|
||||
public void validatePasswordGeneric(Errors e) {
|
||||
if (e.getFieldValue("password") == null) {
|
||||
e.rejectValue("password", "password must not be null");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -56,4 +56,8 @@ public class UserManagement {
|
|||
public Optional<User> getUserByAccount(UserAccount userAccount) {
|
||||
return users.findAll().stream().filter(u -> u.getUserAccount().equals(userAccount)).findFirst();
|
||||
}
|
||||
|
||||
public Optional<User> getUserByName(String username) {
|
||||
return userAccounts.findByUsername(username).flatMap(ua -> getUserByAccount(ua));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,32 +8,34 @@
|
|||
<body>
|
||||
<div layout:fragment="content">
|
||||
<div class="mb-4">
|
||||
<form th:action="@{/profile}" method="post">
|
||||
<form th:object="${profileForm}" th:action="@{/profile}" method="post">
|
||||
<h2>Authentifizierung</h2>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="username">Nutzername</label>
|
||||
<input class="form-control" name="username" th:value="${user.username}" type="text">
|
||||
<input class="form-control" th:field="*{username}" th:errorclass="is-invalid" type="text" required>
|
||||
<div th:if="${#fields.hasErrors('username')}" class="invalid-feedback">Ungültiger Nutzername</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="password" th:text="Passwort"></label>
|
||||
<input class="form-control" name="password" type="password">
|
||||
<input class="form-control" th:field="*{password}" th:errorclass="is-invalid" type="password">
|
||||
<div th:if="${#fields.hasErrors('password')}" class="invalid-feedback">Ungültiges Passwort</div>
|
||||
</div>
|
||||
|
||||
<h2>Nutzerinformationen</h2>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="fullName">Name</label>
|
||||
<input class="form-control" name="fullName" th:value="${user.fullName}" type="text">
|
||||
<input class="form-control" th:field="*{fullName}" th:errorclass="is-invalid" type="text" required>
|
||||
<div th:if="${#fields.hasErrors('fullName')}" class="invalid-feedback">Ungültiger Name</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="address">Adresse</label>
|
||||
<textarea class="form-control" name="address" th:text="${user.address}" rows="3" required></textarea>
|
||||
<textarea class="form-control" th:field="*{address}" th:errorclass="is-invalid" th:placeholder="*{address}" rows="3" required></textarea>
|
||||
<div th:if="${#fields.hasErrors('address')}" class="invalid-feedback">Ungültige Addresse</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" type="submit">Bearbeiten</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div th:if="${user.hasRole('CUSTOMER')}" class="card">
|
||||
<div sec:authorize="hasRole('CUSTOMER')" class="card">
|
||||
<div class="card-header text-danger">Danger Zone</div>
|
||||
<div class="card-body">
|
||||
<a th:href="@{/profile/disable}">
|
||||
|
|
|
@ -5,22 +5,26 @@
|
|||
layout:decorate="~{layout.html(title='Registrierung')}">
|
||||
|
||||
<div layout:fragment="content">
|
||||
<form method="post" th:action="@{/register}">
|
||||
<form th:object="${registrationForm}" method="post" th:action="@{/register}">
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="username">Nutzername</label>
|
||||
<input class="form-control" name="username" type="text" required>
|
||||
<input class="form-control" th:field="*{username}" th:errorclass="is-invalid" type="text" required>
|
||||
<div th:if="${#fields.hasErrors('username')}" class="invalid-feedback">Ungültiger Nutzername</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="password">Passwort</label>
|
||||
<input class="form-control" name="password" type="password" required>
|
||||
<input class="form-control" th:field="*{password}" th:errorclass="is-invalid" type="password" required>
|
||||
<div th:if="${#fields.hasErrors('password')}" class="invalid-feedback">Ungültiges Passwort</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="fullName">Name</label>
|
||||
<input class="form-control" name="fullName" type="text" required>
|
||||
<input class="form-control" th:field="*{fullName}" th:errorclass="is-invalid" type="text" required>
|
||||
<div th:if="${#fields.hasErrors('fullName')}" class="invalid-feedback">Ungültiger Name</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="address">Adresse</label>
|
||||
<textarea class="form-control" name="address" rows="3" required></textarea>
|
||||
<textarea class="form-control" th:field="*{address}" th:errorclass="is-invalid" rows="3" required></textarea>
|
||||
<div th:if="${#fields.hasErrors('address')}" class="invalid-feedback">Ungültige Addresse</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Registrieren</button>
|
||||
</form>
|
||||
|
|
Loading…
Reference in a new issue