Formatting phone numbers in Groovy/Grails - grails

I have been trying to format phone numbers in a grails app by using the following taglib
package rewards
class MasksTagLib {
static defaultEncodeAs = [taglib:'html']
//static encodeAsForTags = [tagName: [taglib:'html'], otherTagName:
[taglib:'none']]
def phone334 = { attrs ->
String phone = attrs.phone
def formatted = "("+phone.substring(0,3)+") "+pho ne.substring(3,6)+"-"+phone.substring(6)
out << formatted
}
}
In addition I have used the import statement to use the taglib in my views, however the format is not changing the display in the index.gsp for the CustomerController
This works in the following view, but I can't get it to work elsewhere the view is profile.gsp
<%# page import="rewards.Customer" %>
<!DOCTYPE html>
<html>
<head>
<meta name="layout" content="main">
<g:set var="entityName" value="${message(code: 'customer.label', default: 'Customer')}" />
<title>Customer Profile</title>
</head>
<body>
<div id="edit-customer" class="content scaffold-edit" role="main">
<h1>Customer Profile</h1>
<g:if test="${flash.message}">
<div class="message" role="status">${flash.message}</div>
</g:if>
<g:hasErrors bean="${customerInstance}">
<ul class="errors" role="alert">
<g:eachError bean="${customerInstance}" var="error">
<li <g:if test="${error in org.springframework.validation.FieldError}">data-field-id="${error.field}"</g:if>><g:message error="${error}"/></li>
</g:eachError>
</ul>
</g:hasErrors>
<g:form url="[resource:customerInstance, action:'updateProfile']" method="PUT" >
<g:hiddenField name="version" value="${customerInstance?.version}" />
<fieldset class="buttons">
<g:actionSubmit class="save" action="updateProfile" value="${message(code: 'default.button.update.label', default: 'Update')}" />
</fieldset>
<fieldset class="form">
<div class="fieldcontain ${hasErrors(bean: customerInstance, field: 'firstName', 'error')} ">
<label for="firstName">
<g:message code="customer.firstName.label" default="First Name" />
</label>
<g:textField name="firstName" value="${customerInstance?.firstName}"/>
</div>
<div class="fieldcontain ${hasErrors(bean: customerInstance, field: 'lastName', 'error')} ">
<label for="lastName">
<g:message code="customer.lastName.label" default="Last Name" />
</label>
<g:textField name="lastName" value="${customerInstance?.lastName}"/>
</div>
<div class="fieldcontain ${hasErrors(bean: customerInstance, field: 'phone', 'error')} required">
<span id="phone-label" class="property-label"><g:message code="customer.phone.label" default="Phone" /></span>
<span class="property-value" aria-labelledby="phone-label"><g:phone334 phone="${customerInstance?.phone}"/></span>
</div>
<div class="fieldcontain ${hasErrors(bean: customerInstance, field: 'email', 'error')} ">
<label for="email">
<g:message code="customer.email.label" default="Email" />
</label>
<g:textField name="email" value="${customerInstance?.email}"/>
</div>
<div class="fieldcontain ${hasErrors(bean: customerInstance, field: 'totalPoints', 'error')} required">
<span id="totalPoints-label" class="property-label"><g:message code="customer.totalPoints.label" default="Total Points" /></span>
<span class="property-value" aria-labelledby="totalPoints-label"><g:fieldValue bean="${customerInstance}" field="totalPoints"/></span>
</div>
</fieldset>
</g:form>
</div>
<div id="list-award" class="content scaffold-list" role="main">
<g:if test="${flash.message}">
<div class="message" role="status">${flash.message}</div>
</g:if>
<table>
<thead>
<tr>
<g:sortableColumn property="type" title="${message(code: 'award.type.label', default: 'Type')}" />
<g:sortableColumn property="awardDate" title="${message(code: 'award.checkinDate.label', default: 'Award Date')}" />
<th><g:message code="award.customer.label" default="Phone" /></th>
<g:sortableColumn property="points" title="${message(code: 'award.points.label', default: 'Points')}" />
</tr>
</thead>
<tbody>
<g:each in="${customerInstance.awards}" status="i" var="checkinInstance">
<tr class="${(i % 2) == 0 ? 'even' : 'odd'}">
<td>${fieldValue(bean: checkinInstance, field: "type")}</td>
<td>${fieldValue(bean: checkinInstance, field: "awardDate")}</td>
<td><g:phone334 phone="${customerInstance?.phone}"/></td>
<td>${fieldValue(bean: checkinInstance, field: "points")}</td>
</tr>
</g:each>
</tbody>
</table>
</div>
</body>
I would appreciate any help I can get.
Here is the Controller. It is called CustomerController.groovy and it is displaying the list using "Index". How can I apply the phone format to this list
package rewards
class CustomerController {
static scaffold = true
def calculationsService
def lookup() {
// def customerInstance = Customer.list(sort: "lastName", order:
"desc", max: 5, offset: 5)
// dynamic queries
// def customerInstance = Customer.findAllByLastName("Foster")
// def customerInstance = Customer.findAllByTotalPoints(5, [sort: "lastName", order: "desc"])
// def customerInstance = Customer.findAllByPhone(params.id) // for one row return findBy if rows > 1 only first
// def customerInstance = Customer.findAllByLastNameLike("H%") // Case Sensitive
// def customerInstance = Customer.findAllByLastNameIlike("b%") // Case Insensitive
// def customerInstance = Customer.findAllByTotalPointsGreaterThanEquals(3, [sort: "totalPoints"])
// def customerInstance = Customer.findAllByTotalPointsBetween(2, 4, [sort: "totalPoints"])
def customerInstance = Customer.findAllByFirstNameIlikeAndTotalPointsGreaterThanEquals("b%", 3)
[customerInstanceList: customerInstance]
}
def customerLookup(Customer lookupInstance) {
// Query customer by Phone number - service
// If no result, - controller
// create a new customer - service
// create welcome message - service
// add award reward - service
// save customer - service
// send welcome to kiosk - controller
// if customer found - controller
// calculate total ponts - service
// create welcome message - service
// add award reward - service
// save customer - controller
// send welcome to kiosk - controller
def (customerInstance, welcomeMessage) = calculationsService.processCheckin(lookupInstance)
render(view: "checkin", model:[customerInstance: customerInstance, welcomeMessage: welcomeMessage])
}
def index() {
params.max = 10
[customerInstanceList: Customer.list(params), customerInstanceCount: Customer.count()]
}
def create() {
[customerInstance: new Customer()]
}
def save(Customer customerInstance) {
customerInstance.save()
redirect(action: "show", id: customerInstance.id)
}
def show(Long id) {
def customerInstance = Customer.get(id)
customerInstance = calculationsService.getTotalPoints(customerInstance)
[customerInstance: customerInstance]
}
def edit(Long id) {
def customerInstance = Customer.get(id)
[customerInstance: customerInstance]
}
def update(Long id) {
def customerInstance = Customer.get(id)
customerInstance.properties = params
customerInstance.save()
redirect(action: "show", id: customerInstance.id)
}
def delete(Long id) {
def customerInstance = Customer.get(id)
customerInstance.delete()
redirect(action: "index")
}
def profile() {
def customerInstance = Customer.findByPhone(params.id)
[customerInstance: customerInstance]
}
def updateProfile(Customer customerInstance) {
customerInstance.save()
render(view: "profile", model:[customerInstance: customerInstance])
}
def checkin() {}
}
Customer List Display

If you want to customize your index view, your best bet in this case is to stop using the scaffolded page, yes. (There are alternatives, but they're overly complicated for this situation, and don't offer you nearly as much control over the resulting view.)
In general, it's not a good idea long-term to have half-scaffolded content. By this, I mean either scaffolded controllers, or scaffolded views, but not the other side. Long term, one or the other is likely to change, and your pages will break.

Related

No such property: propertyName for class: org.grails.orm.hibernate.cfg.HibernatePersistentEntity

im new to grails, and i want to style my f:table in the index.gsp, i created a file _table.gsp, when i execute i get this error :
No such property: propertyName for class: org.grails.orm.hibernate.cfg.HibernatePersistentEntity
index.gsp
<!DOCTYPE html>
<html>
<head>
<meta name="layout" content="main" />
<g:set var="entityName" value="${message(code: 'user.label', default: 'User')}" />
<title><g:message code="default.list.label" args="[entityName]" /></title>
</head>
<body>
<div class="breadcrumbs">
<div class="col-sm-3">
<g:message code="default.link.skip.label" default="Skip to content…"/>
</div>
<div class="col-sm-3">
<a class="home" href="${createLink(uri: '/')}"><g:message code="default.home.label"/></a>
</div>
<div class="col-sm-3">
<g:link class="create" action="create"><g:message code="default.new.label" args="[entityName]" /></g:link>
</div>
</div>
<br>
<br>
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<strong class="card-title"><g:message code="default.list.label" args="[entityName]" /></strong>
</div>
<div class="card-body">
<div id="pay-invoice">
<div class="card-body">
<div id="list-user" class="content scaffold-list" role="main">
<g:if test="${flash.message}">
<div class="message" role="status">${flash.message}</div>
</g:if>
<f:table collection="${userList}"/>
<div class="pagination">
<g:paginate total="${userCount ?: 0}" />
</div>
</div>
</div>
</div>
</div>
</div> <!-- .card -->
</div>
</div>
</body>
</html>
_table.gsp
<table class="table stripped-table">
<thead>
<tr>
<g:each in="${domainClass}" var="p" status="i">
<g:set var="propTitle">${domainClass.propertyName}.${p.name}.label</g:set>
<g:sortableColumn property="${p.name}" title="${message(code: propTitle, default: p.naturalName)}" />
</g:each>
</tr>
</thead>
<tbody>
<g:each in="${collection}" var="bean" status="i">
<tr class="${(i % 2) == 0 ? 'even' : 'odd'}">
<g:each in="${domainProperties}" var="p" status="j">
<g:if test="${j==0}">
<td><g:link method="GET" resource="${bean}"><f:display bean="${bean}" property="${p.name}" displayStyle="${displayStyle?:'table'}" /></g:link></td>
</g:if>
<g:else>
<td><f:display bean="${bean}" property="${p.name}" displayStyle="${displayStyle?:'table'}" /></td>
</g:else>
</g:each>
</tr>
</g:each>
</tbody>
</table>
User.groovy
class User {
transient securiteService
String username
String password
String nom
String prenom
String email
String tel
static hasMany = [roles : Role]
static constraints = {
username blank: false, unique: true
password blank: false
nom nullable: true
prenom nullable: true
email email:true, nullable:true
tel nullable:true, maxSize:20, matches:/[\+]{0,1}[0-9\s]{3,15}/
}
static mapping = {
password column: '`password`'
sort nom: "asc"
affectations sort : "dateAffectation", order:"desc"
intervention sort : "responsable", order:"desc"
}
}
usercontroller.groovy :
package mylicence
import grails.validation.ValidationException
import static org.springframework.http.HttpStatus.*
import java.security.MessageDigest
class UserController {
UserService userService
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond userService.list(params), model:[userCount: userService.count()]
}
def show(Long id) {
respond userService.get(id)
}
def create() {
respond new User(username: params.username, password: params.password, nom: params.nom, prenom: params.prenom, email: params.email, tel: params.tel)
}
def save(User user) {
if (user == null) {
notFound()
return
}
try {
userService.save(user)
} catch (ValidationException e) {
respond user.errors, view:'create'
return
}
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.created.message', args: [message(code: 'user.label', default: 'User'), user.id])
redirect user
}
'*' { respond user, [status: CREATED] }
}
}
def edit(Long id) {
respond userService.get(id)
}
def update(User user) {
if (user == null) {
notFound()
return
}
try {
userService.save(user)
} catch (ValidationException e) {
respond user.errors, view:'edit'
return
}
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.updated.message', args: [message(code: 'user.label', default: 'User'), user.id])
redirect user
}
'*'{ respond user, [status: OK] }
}
}
def delete(Long id) {
if (id == null) {
notFound()
return
}
userService.delete(id)
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.deleted.message', args: [message(code: 'user.label', default: 'User'), id])
redirect action:"index", method:"GET"
}
'*'{ render status: NO_CONTENT }
}
}
protected void notFound() {
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'user.label', default: 'User'), params.id])
redirect action: "index", method: "GET"
}
'*'{ render status: NOT_FOUND }
}
}
def login() {
}
def handlelogin = {
def hashPassd = params.password
// Find the username
def user = User.findByUsernameAndPassword(params.username, hashPassd)
if (!user) {
flash.message = "User not found for userName: ${params.username}"
redirect(action:'login')
return
} else {
session.user = user
redirect(controller:'user')
}
}
def logout = {
//log.info 'logout'
if(session.user) {
session.user = null
session.invalidate()
redirect(controller:'user', action: 'login')
}
else {
redirect(controller:'user', action: 'login')
}
}
}
i searched every where with no solution, im using grails 3.3.4, groovy 2.4.14, JVM 1.8.0_161
had the same problem here's my solution:
<%# page import="grails.util.GrailsNameUtils" %>
<table >
<thead>
<tr>
<g:each in="${domainProperties}" var="p" status="i">
<g:set var="propTitle">${domainClass.decapitalizedName}.${p.name}.label</g:set>
<g:sortableColumn property="${p.name}" title="${message(code: propTitle, default: grails.util.GrailsNameUtils.getNaturalName(p.name))}" />
</g:each>
</tr>
</thead>
<tbody>
<g:each in="${collection}" var="bean" status="i">
<tr class="${(i % 2) == 0 ? 'even' : 'odd'}">
<g:each in="${domainProperties}" var="p" status="j">
<g:if test="${j==0}">
<td><g:link method="GET" resource="${bean}"><f:display bean="${bean}" property="${p.name}" displayStyle="${displayStyle?:'table'}" /></g:link></td>
</g:if>
<g:else>
<td><f:display bean="${bean}" property="${p.name}" displayStyle="${displayStyle?:'table'}" /></td>
</g:else>
</g:each>
</tr>
</g:each>
</tbody>
</table>
I had to import the grails.util.GrailsNameUtils library to display the name in natural format.

How can I pass some values from gsp to controller in a g:form?

I know there are some questions like mine, but I'm hoping to get an explanation of how to do it in Grails.
What I want to do is pass values from a gsp datepicker and textfield to the controller when upon Submit button press.
My thanks in advance
GSP:
<g:form action="generateReport">
<ol>
<li class="fieldcontain">
<div class="fieldcontain required">
<label for="date" style="padding-left: 210px;">
<g:message code="Data" default="Data"/>
<span class="required-indicator">*</span>
<g:datePicker id="data" name="datapicker" precision="day"></g:datePicker>
</label>
</div>
</li>
<li class="fieldcontain">
<div class="fieldcontain required">
<label for="date" style="padding-left: 195px;">
<g:message code="Time" default="Time"/>
<g:textField name="minutes"/>
</label>
</div>
</li>
</ol>
<div class="content scaffold-show" style="padding-left: 50px;">
<g:submitButton value="search" name="button"/>
</div>
</g:form>
Controller:
def generateReport(){
log.info("Button pressed")
redirect(action: "list")
}
I just found the answer:
the view:
<g:textField name="name" />
the controller:
Controller:
class SomeController {
def someAction() {
def name = params.name
// do something with name
}
}

How to prevent data input for being erase in Grails with reCaptcha

I have gsp with two textfield for firstname-lastname and reCaptcha. What I want is for every wrong captcha code the user's input for firstname and last name won't be erased.
snippet for controller:
***captcha_code****
if (result) {
def person = new Person(params)
person.save()
render "Success!"
} else {
flash.message = message(code: 'forgotPassword.captcha.wrong')
redirect(controller:'person', action:'form')
}
snipper for form.gsp
***captcha_code_here***
<g:form controller="person" action="save">
<label>First Name: </label>
<g:textField name="firstName"/><br/>
<label>Last Name: </label>
<g:textField name="lastName"/><br/>
<g:if test="${flash.message}">
<div class="message" role="status" style="font-size: medium;color: green;">${flash.message}</div>
</g:if>
***captcha_code_here***
<g:actionSubmit value="Save"/>
To repopulate the fields you can use the same flash scope you're using for the message. On error, add the first and last name to the flash scope, and then in your GSP use those values when they are available:
PersonController
class PersonController {
def save() {
...
if(/* recaptcha failed */) {
flash.firstName = params.firstName
flash.lastName = params.lastName
}
...
}
}
GSP
<label>First Name: </label>
<g:textField name="firstName" value="${flash.firstName ?: ''}"/><br/>
<label>Last Name: </label>
<g:textField name="lastName" value="${flash.lastName ?: ''}"/><br/>
In Controller Action, send back fields that you want to be repopulated.

Grails Parent Child Form not saving child data

I am trying to create a parent child form with Author and Book domain classes. The view works fine and lets me enter books when I creating a new Author. However, when I add a new author and the books and check the database (dbconsole), I see a new record in the Author table but no records are added to the Book table. Can you please let me know what I am missing or doing wrong here?
Here are my domain classes:
AUTHOR:
package bookauthor1tomany
import org.apache.common.collections.list.*
import org.apache.commons.collections.ListUtils.*
class Author {
static constraints = {
}
String name
String category
List<Book> books = new ArrayList<>()
static hasMany = [ books:Book ]
static mapping = {
books cascade:"all-delete-orphan"
}
def getExpandableBookList() {
return LazyList.decorate(books, FactoryUtils.instantiateFactory(Book.class))
}
String toString(){
return "${name}" - "${category}"
}
}
BOOK
package bookauthor1tomany
class Book {
static constraints = {
}
String title
boolean _deleted
static transients = [ '_deleted' ]
static belongsTo = [ author:Author ]
def String toString() {
return title
}
}
AuthorController
I haven't changed anything with the controller. This is the default generated save method for the Author controller.
#Transactional
def save(Author authorInstance) {
if (authorInstance == null) {
notFound()
return
}
if (authorInstance.hasErrors()) {
respond authorInstance.errors, view:'create'
return
}
authorInstance.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.created.message', args: [message(code: 'author.label', default: 'Author'), authorInstance.id])
redirect authorInstance
}
'*' { respond authorInstance, [status: CREATED] }
}
}
GSPs
create.gsp
<!DOCTYPE html>
<html>
<head>
<meta name="layout" content="main">
<g:set var="entityName" value="${message(code: 'author.label', default: 'Author')}" />
<title><g:message code="default.create.label" args="[entityName]" /></title>
</head>
<body>
<g:message code="default.link.skip.label" default="Skip to content…"/>
<div class="nav" role="navigation">
<ul>
<li><a class="home" href="${createLink(uri: '/')}"><g:message code="default.home.label"/></a></li>
<li><g:link class="list" action="index"><g:message code="default.list.label" args="[entityName]" /></g:link></li>
</ul>
</div>
<div id="create-author" class="content scaffold-create" role="main">
<h1><g:message code="default.create.label" args="[entityName]" /></h1>
<g:if test="${flash.message}">
<div class="message" role="status">${flash.message}</div>
</g:if>
<g:hasErrors bean="${authorInstance}">
<ul class="errors" role="alert">
<g:eachError bean="${authorInstance}" var="error">
<li <g:if test="${error in org.springframework.validation.FieldError}">data-field-id="${error.field}"</g:if>><g:message error="${error}"/></li>
</g:eachError>
</ul>
</g:hasErrors>
<g:form url="[resource:authorInstance, action:'save']" >
<fieldset class="form">
<g:render template="authortemp"/>
</fieldset>
<fieldset class="buttons">
<g:submitButton name="create" class="save" value="${message(code: 'default.button.create.label', default: 'Create')}" />
</fieldset>
</g:form>
</div>
</body>
</html>
_form.gsp
<%# page import="BookAuthor1ToMany" %>
<div class="fieldcontain ${hasErrors(bean: authorInstance, field: 'books', 'error')} ">
<label for="books">
<g:message code="author.books.label" default="Books" />
</label>
<ul class="one-to-many">
<g:each in="${authorInstance?.books?}" var="b">
<li><g:link controller="book" action="show" id="${b.id}">${b?.encodeAsHTML()}</g:link></li>
</g:each>
<li class="add">
<g:link controller="book" action="create" params="['author.id': authorInstance?.id]">${message(code: 'default.add.label', args: [message(code: 'book.label', default: 'Book')])}</g:link>
</li>
</ul>
</div>
<div class="fieldcontain ${hasErrors(bean: authorInstance, field: 'name', 'error')} required">
<label for="name">
<g:message code="author.name.label" default="Name" />
<span class="required-indicator">*</span>
</label>
<g:textField name="name" required="" value="${authorInstance?.name}"/>
</div>
_authortemp.gsp
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name"><label for="name">Name:</label></td>
<td valign="top" class="value ${hasErrors(bean:authorInstance,field:'name','errors')}">
<input type="text" id="name" name="name" value="${fieldValue(bean:authorInstance,field:'name')}"/>
</td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="category">Category:</label></td>
<td valign="top" class="value ${hasErrors(bean:authorInstance,field:'category','errors')}">
<input type="text" id="category" name="category" value="${fieldValue(bean:authorInstance,field:'category')}"/>
</td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="books">Books:</label></td>
<td valign="top" class="value ${hasErrors(bean:authorInstance,field:'books','errors')}">
<g:render template="books" model="['authorInstance':authorInstance]" />
</td>
</tr>
</tbody>
</table>
</div>
_books.gsp
<script type="text/javascript">
var childCount = ${authorInstance?.books.size()} + 0;
function addChild() {
var htmlId = "book" + childCount;
var deleteIcon = "${resource(dir:'images/skin', file:'database_delete.png')}";
var templateHtml = "<div id='" + htmlId + "' name='" + htmlId + "'>\n";
templateHtml += "<input type='text' id='expandableBookList[" + childCount + "].title' name='expandableBookList[" + childCount + "].title' />\n";
templateHtml += "<span onClick='$(\"#" + htmlId + "\").remove();'><img src='" + deleteIcon + "' /></span>\n";
templateHtml += "</div>\n";
$("#childList").append(templateHtml);
childCount++;
}
</script>
<div id="childList">
<g:each var="book" in="${authorInstance.books}" status="i">
<g:render template='book' model="['book':book,'i':i]"/>
</g:each>
</div>
<input type="button" value="Add Book" onclick="addChild();" />
_book.gsp
<div id="book${i}">
<g:hiddenField name='expandableBookList[${i}].id' value='${book.id}'/>
<g:textField name='expandableBookList[${i}].title' value='${book.title}'/>
<input type="hidden" name='expandableBookList[${i}]._deleted' id='expandableBookList[${i}]._deleted' value='false'/>
<span onClick="$('#expandableBookList\\[${i}\\]\\._deleted').val('true'); $('#expandableBookList${i}').hide()">Delete</span>
</div>
You don't need the
List<Book> books = new ArrayList<>()
in your domain. The hasMany will give you a books collection by default. That might be causing some issues. Also, have you debugged in your controller to make sure that the books collection is populated when the save occurs.

grails g:field error not showing

I have this setup, but the validation is not working
index.gsp:
<g:form name="loginForm" autocomplete="off" controller="company" action ="save">
<table >
<tr>
<td><g:field type="text" name="company" required="true" value="${c?.companyName}" /></td>
</tr>
</table>
Controller:
def index = {
def c =new Company()
//c=params
return [c:c]
}
def save ={}
You need to check for errors using hasErrors method in your form:
<div class="fieldcontain ${hasErrors(bean: yourBean, field: 'yourField', 'error')} required">
<label for="yourField">
<g:message code="yourBean.yourField.label" default="yourField" />
<span class="required-indicator">*</span>
</label>
<g:textField name="content" required="" value="${yourBean?.yourField}"/>
</div>
Grails hasErrors documenttaion.
And check in your controller (save action) if the validation has passed using:
if (!yourBean.save(flush: true)) {
render(view: "create", model: [yourBean: yourBean])
return
}

Resources