Yesod: Custom divs and forms - forms

How to make Yesod generate complicated forms like this:
<form class="form-horizontal">
<div class="form-group">
<label for="text1" class="control-label col-lg-4">
Normal Input Field
</label>
<div class="col-lg-8">
<input type="text" id="text1" placeholder="Email" class="form-control">
</div>
</div>
<!-- /.form-group -->
<div class="form-group">
<label for="pass1" class="control-label col-lg-4">
Password Field
</label>
<div class="col-lg-8">
<input class="form-control" type="password" id="pass1" data-original-title="Please use your secure password" data-placement="top" />
</div>
</div>
</form>
I know that Yesod can create a generic form but is it possible to do the following things:
Wrap a div around a textbox
Wrap the label and input textbox also in another div.
Does Yesod allow to do these stuff for generating highly customized forms ?

Yesod provides the ability of defining custom fields which is very well explained in their documentation.
Also, I define two custom fields for the above problem:
textBoxField :: Text -> Field Handler Text
textBoxField label = Field
{ fieldParse = \rawVals _ ->
case rawVals of
[a] -> return $ Right $ Just a
[] -> return $ Right Nothing
, fieldView = \idAttr nameAttr otherAttrs eResult isReq ->
[whamlet|
<div class="form-group">
<label for=#{idAttr} class="control-label col-lg-4">#{label}
<div class="col-lg-8">
<input id=#{idAttr} name=#{nameAttr} *{otherAttrs}
type="text" class="form-control">
|]
, fieldEnctype = UrlEncoded
}
cPasswordField :: Text -> Field Handler Text
cPasswordField label = Field
{ fieldParse = \rawVals _ ->
case rawVals of
[a] -> return $ Right $ Just a
[] -> return $ Right Nothing
, fieldView = \idAttr nameAttr otherAttrs eResult isReq ->
[whamlet|
<div class="form-group">
<label for=#{idAttr} class="control-label col-lg-4">#{label}
<div class="col-lg-8">
<input id=#{idAttr} name=#{nameAttr} *{otherAttrs}
type="password" class="form-control"
data-original-title="Please use your secure password" data-placement="top">
|]
, fieldEnctype = UrlEncoded
}
These functions can be later utilized to build up the actual form. The entire working code is here.

Have you looked into runInputPost and ireq? Else look at the form chapter in the yesod book.
But, to give you an example, you can create exactly the form you want, and then just use something like this in the POST request,
-- Handling the blog article form
postAdminNewArticleR :: Handler Html
postAdminNewArticleR = do
title <- runInputPost $ ireq textField "form-title-field"
htmlContent <- runInputPost $ ireq htmlField "form-htmlcontent-field"
-- insert into the database or something
For the form handler itself, you'd probably do,
-- The form page for posting a new blog article
getAdminNewArticleR :: Handler Html
getAdminNewArticleR = do
formroute <- return $ AdminNewArticleR
defaultLayout $ do
-- ...
and lastly, the hamlet for the form page would look like,
<form method=post action=#{formroute}>
<input name="form-title-field">
<textarea name="form-htmlcontent-field">
This gives you 100% control of the forms.

Related

Nested Angular Form Groups - Form must reflect HTML structure

Say I have the following formGroup structure:
userGroup = {
name,
surname,
address: {
firstLine,
secondLine
}
}
This forces me to write HTML similar to the following:
<form [formGroup]="userGroup">
<input formControlName="name">
<input formControlName="surname">
<div formGroupName="address">
<input formControlName="firstLine">
<input formControlName="secondLine">
</div>
</form>
Let's say, just for the sake of the example, that you are constrained to write HTML that looks like this:
<form [formGroup]="userGroup">
<input formControlName="name">
<input formControlName="surname">
<div formGroupName="address">
<input formControlName="firstLine">
</div>
<hr>
<div formGroupName="someOtherGroup">
<input id="problemSecondLine" formControlName="???.secondLine">
</div>
</form>
Is there a way, to force the field with id=problemSecondLine to be under userGroup -> address -> secondLine, even though, visually, I have no option but to place it under this particular DIV?
I guess I can manually update via ngModel - but I'm trying to find the cleanest way possible.
You can use formControl directive instead of formControlName
<input id="problemSecondLine" [formControl]="userGroup.get('address.secondLine')">
Plunker Example

Forms testing Angular 4

I have done a small Web App in Angular 4 and I'm now trying to test it.
Here is what I would like to test :
<form class="navbar-form navbar-left" (ngSubmit)="onSubmit(f)" #f="ngForm">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search" ngModel name="name" pattern=".{2,}" required #name="ngModel">
</div>
<button type="submit" class="btn btn-default" [disabled]="!f.valid">
<span class="glyphicon glyphicon-search" aria-hidden="true"></span>
</button>
<span class="help-block help" *ngIf="!name.valid && name.touched ">Please enter at least two characters</span>
</form>
At first, I would just like to test if the form is really invalid (and then I cannot click on the submit button) if thye word entered in the input is less than 2 characters.
Here is the test code I have written
beforeEach(() => {
fixture = TestBed.createComponent(NavbarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should disable submit if word<2',()=>{
const compiled = fixture.nativeElement;
const input = fixture.debugElement.query(By.css('input'));
input.nativeElement.value='b';
input.nativeElement.dispatchEvent(new Event('input'));
fixture.detectChanges();
//console.log(compiled.querySelector('input').textContent);
//console.log(compiled.querySelector('.ng-valid'));
expect(compiled.querySelector('.ng-valid')).toBe(null);
});
The textContent of the input is modified properly but the form is still valid. However, when I use the webapplication, I can't submit the form is the word is less than two characters.
I feel like the word in the input doesn't really "reach" the form in the test. I've tried a lot of different things so any help would be great !

How to define custom scala input helper for checkbox

I am trying to build an input helper for Play Framework in Scala.
I would like to have my inputs of type radio and checkbox to be printed differently than the standard text or password input types ?
Here is my custom helper:
#(elements: helper.FieldElements)
<div class="form-group">
<label for="#elements.id" class="col-lg-2 control-label">#elements.label</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="#elements.id" />
</div>
</div>
But this prints an text input instead of checkbox input. Which is strange behaviour.
I've read the documentation, but couldn't find anything about it.
Here is how I import this helper to view:
#implicitFieldConstructor = #{ FieldConstructor(generic.views.html.FormHelpers.twitterBootstrapInputSupraClub.render) }
#checkboxFieldConstructor = #{ FieldConstructor(generic.views.html.FormHelpers.twitterBootstrapInputSupraClubCheckbox.render) }
And this is how I call helpers to build input (generates inputs) in view:
#inputText(
accountRegistrationForm("email"),
'_label -> "email"
)
#inputPassword(
accountRegistrationForm("password"),
'_label -> password
)
#inputRadioGroup(
accountRegistrationForm("regulationAcceptance"),
options = Seq("true"->"Yes"),
'_label -> "I agree with regulation",
'type -> "radio"
)(handler = checkboxFieldConstructor, implicitly[Lang])
I might be missing something here but as far as I can see, you seem to me missing the right input type.
In your helper, try changing <input type="text"... to <input type="radio"...
You can pass the input type dynamically, too. e.g.
#(elements: helper.FieldElements, inputType: String)
<div class="form-group">
<label for="#elements.id" class="col-lg-2 control-label">#elements.label</label>
<div class="col-lg-10">
<input type="#inputType" class="form-control" id="#elements.id" />
</div>
</div>
EDIT:
I have something similar to this for radio buttons:
given that my userDetailForm contains canLogOn:
"company" -> nonEmptyText,
"canLogon" -> boolean,
"canTrade" -> boolean,
I created a function in views: radioSet(fieldName: String)
#radioSet(fieldName: String) = {
<label for="#fieldName">Can Logon</label>
<input name=#fieldName id=#fieldName type="radio" value = "true"
#if(userDetailForm(fieldName).value.getOrElse("false").equals("true"))
{checked}>
}
Then I call it when I need it:
#radioSet("canLogon")
And I get: http://imgur.com/RG60wxV (sorry I cant post images yet)

Foundation 5 & Abide: a custom validator for a set of checkboxes?

I would like to create a validator for abide for a set of checkboxes.
Let's consider a set of 5 checkboxes. The user is asked to check 3 max, and at least 1.
So, here is my work-in-progress code:
<div data-abide-validator='checkboxes' data-abide-validator-values='1,3'>
<input type="checkbox"/>
<input type="checkbox"/>
<input type="checkbox"/>
<input type="checkbox"/>
<input type="checkbox"/>
</div>
<script>
$(document).foundation({
validators: {
checkboxes: function(el, required, parent) {
var countC = el.find(':checked').length;
alert(countC);
}
}
});
</script>
At this point, I just try to count the checked inputs. But it seems I can't even trigger the validator... I think I could manage to code my validation stuff if only I could figure out how to trigger it.
Indeed I didn't find many examples of the custom validator, and the official doc did not help me much.
Your HTML markup is not really "correct" for abide. You should be attaching the data-abide-validator attribute to the inputs, not the parent div. Additionally, you need some better markup so abide's default error display can work (and some better use of foundation's grid system to lay it out). I would point you toward the Abide Validation Page on Zurb's site for some examples of form markup.
I've taken the liberty of restructuring your markup to be something that is more becoming of a foundation layout:
<form action="/echo/html/" method="POST" data-abide>
<div class="row">
<div class="small-12 columns checkbox-group" data-abide-validator-limit="1,3">
<label>Check some boxes</label>
<small class="error">You have checked an invalid number of boxes.</small>
<ul class="small-block-grid-3">
<li>
<label>
<input type="checkbox" data-abide-validator="checkbox_limit" value="1" /> 1
</label>
</li>
<li>
<label>
<input type="checkbox" data-abide-validator="checkbox_limit" value="2" /> 2
</label>
</li>
<li>
<label>
<input type="checkbox" data-abide-validator="checkbox_limit" value="3" /> 3
</label>
</li>
<li>
<label>
<input type="checkbox" data-abide-validator="checkbox_limit" value="4" /> 4
</label>
</li>
<li>
<label>
<input type="checkbox" data-abide-validator="checkbox_limit" value="5" /> 5
</label>
</li>
</ul>
</div>
</div>
<div class="row">
<div class="small-12 columns">
<button type="submit">Submit</button>
</div>
</div>
</form>
As to your JS code. It's not correct either. You need to address the abide -> validators namespace of the options, not just validators. I've rewritten your JS code to not only do that, but give the desired effect you wanted:
$(document).foundation({
abide: {
validators: {
checkbox_limit: function(el, required, parent) {
var group = parent.closest( '.checkbox-group' );
var limit = group.attr('data-abide-validator-limit').split(',');
var countC = group.find(':checked').length;
if( countC >= limit[0] && countC <= limit[1] ) {
group.find('small.error').hide();
//return true so abide can clear any invalid flags on this element
return true;
} else {
group.find('small.error').css({display:'block'});
//return false and let abide do its thing to make sure the form doesn't submit
return false;
}
}
}
}
});
In order to check adjacent elements when doing custom validation, you need to have something to target. The el variable in the validation function will be the DOM element of the input/field that is being validated. The required variable will tell you if the field is flagged as being required or not (boolean). The parent variable will be set to the "parent" of the field. I say "parent" because although the label tag is technically the parent of the input element, abide is smart enough to realize that the label is part of the field's element structure and skip over it to the li element instead.
From there, you need a way to identify a common parent. So I added the checkbox-group class to whatever element I decided to make the "parent" of all the checkboxes in the group. This is not a Foundation or Abide "magic" class, but rather something of my own creation for use in the validation function.
From there, you can easily trace the few lines of the validation function to see the workflow: Find the group container object, parse the limits off the container's data-abide-validator-limits attribute, count the number of checked inputs in the container, check if the number checked is between the limits, display/hide the error message and return true/false so abide knows if the field validated or not.
I've got a working Fiddle of it if you care to check it out yourself ;) Hopefully this was informative for you, and I wish you the best of luck playing with the awesome that is Foundation!

multiple verify method on form tuple

I'm quite new to play and scala. I'm working on form and validations. But I couldn't figure out to get all errors from multiple verification on form.
My form tuple looks like;
val companyMapping = Forms.tuple(
"name" -> nonEmptyText,
"email" -> email,
"password" -> nonEmptyText(8),
"re-password" ->nonEmptyText(8)).verifying(
// Add an additional constraint: both passwords must match
"Passwords don't match", data => {
data._3 == data._4 }
).verifying(
// Second constraint
"Test error", data => {
false }
)
In the view I print global errors and errors, it looks like;
#println(companyForm.globalError)
#println(companyForm.errors)
and output;
Some(FormError(,Passwords don't match,WrappedArray()))
List(FormError(,Passwords don't match,WrappedArray()), FormError(,Test error,WrappedArray()))
At this stage I have absolutely no idea about how to print both of the errors. I'm showing errors separately for the each input and show global errors at the end.
But if passwords match I can see test constraint in the global errors. Other than it only shows password match constraint.
Here is the view part;
#helper.form(action = routes.Login.register) {
<div class="row">
<span class="label">Name</span>
<input type="text" name="name" placeholder="Company Name" value="#companyForm("name").value" >
#if(!companyForm.errors("name").isEmpty){
<span class="error">#Messages(companyForm.errors("name")(0).message,"Company name")</span>
}
</div>
<div class="row">
<span class="label">Email</span>
<input type="text" name="email" placeholder="Email" value="#companyForm("email").value" >
#if(!companyForm.errors("email").isEmpty){
<span class="error">#Messages(companyForm.errors("email")(0).message,companyForm.errors("email")(0).key)</span>
}
</div>
<div class="row">
<span class="label">Password</span>
<input type="password" name="password" placeholder="Password" value="#companyForm("password").value" >
#if(!companyForm.errors("password").isEmpty){
<span class="error">#Messages(companyForm.errors("password")(0).message,8)</span>
}
</div>
<div class="row">
<span class="label">Re-type Password</span>
<input type="password" name="re-password" placeholder="Re-type your password" value="#companyForm("re-password").value" >
#if(!companyForm.errors("re-password").isEmpty){
<span class="error">#Messages(companyForm.errors("re-password")(0).message,8)</span>
}
</div>
#println(companyForm.globalError)
#println(companyForm.errors)
<div class="row">
<span class="label"><button type="submit">Save</button></span>
#companyForm.globalError.map { error =>
<span class="error">#error.message</span>
}
</div>
}
Maybe I'm just confused about those error types. So please can you explain it detailed.
In the re-password section of your template, you currently test if !companyForm.errors("re-password").isEmpty but then only show the message for companyForm.errors("re-password")(0), i.e. the first error only. Even if you have multiple errors.
You have to iterate over companyForm.errors("re-password") to print something for each error.
You can for example output a <span class="error">... for each error, using a for comprehension:
<div class="row">
<span class="label">Re-type Password</span>
<input type="password" name="re-password" placeholder="Re-type your password" value="#companyForm("re-password").value" >
#for (error <- companyForm.errors("re-password")) {
<span class="error">#Messages(error.message,8)</span>
}
</div>
See the play doc for Scala templates for other useful syntax to use in templates.