How to write a custom form helper template for dynamically generated content? - html

I have some sort of quiz system, where to user gets shown a question and several answer-options with radio-buttons.
But as I am using a helper for a inputRadioGroup that gets filled via a list, it does not look pretty anymore (like Twitter Bootstrap). The radiobuttons are inline, while they should be underneath each other. And actually I would like to change the icon to a prettier button.
This is how it looks at the moment:
Therefore I tried to write my own custom form helper, but keep getting stuck. I find it frustratingly hard to understand the documentation for this:
https://www.playframework.com/documentation/2.3.x/JavaFormHelpers
First I created a new template named myFieldConstructorTemplate.scala.html
#(elements: helper.FieldElements)
<div class="#if(elements.hasErrors) {error}">
<label for="#elements.id">#elements.label</label>
<div class="input">
#elements.input
<span class="errors">#elements.errors.mkString(", ")</span>
<span class="help">#elements.infos.mkString(", ")</span>
</div>
</div>
Saved it to the /views-folder. Then try to use it in my view class quiz.scala.html:
#import helper._
#import helper.twitterBootstrap._
#(questionList: List[Question], answerList: List[Answer], answerRadioForm: Form[Answer])
#helper.form(action = routes.Application.nextQuizPage(), 'id -> "answerRadioForm"){
#helper.inputRadioGroup(
answerRadioForm("Answer"),
options = answerList.map(answer => answer.answerID.toString -> answer.answerText),
'_label -> "Answer",
'_error -> answerRadioForm("answerID").error.map(_.withMessage("select answer")))
<button type="submit" class="btn btn-default" value="Send">
Next Question
</button>
}
#implicitField = #{ FieldConstructor(myFieldConstructorTemplate.f) }
#inputText(answerRadioForm("questionID"))
If I put this into my template, I get a not found: value implicitField-error.
So how can I manage to change the appearance of my radiobuttons to underneath and looking like Twitter Bootstrap?
[EDIT1]: I have changed the order of the imports to the suggested version:
#(questionList: List[Question], answerList: List[Answer], answerRadioForm: Form[Answer])
#import helper._
#implicitField = #{ FieldConstructor(myFieldConstructorTemplate.f) }
#import helper.twitterBootstrap._
I get this error then:
ambiguous implicit values:
both method implicitField of type => views.html.helper.FieldConstructor
and value twitterBootstrapField in package twitterBootstrap of type
=> views.html.helper.FieldConstructor match expected type
views.html.helper.FieldConstructor
I think this has to do with the way I import the answers into the radiobuttons?
[EDIT2]:
The order of the imports is now:
#(questionList: List[Question], answerList: List[Answer], answerRadioForm: Form[Answer])
#import models.Question
#import models.Answer
#import helper._
#implicitField = #{ FieldConstructor(myFieldConstructorTemplate.f) }
With this, the program compiles. BUT the radiobuttons still look the same. So I tried to change the design, which does not quite work. It looks now like all the radiobuttons are melted into a single one:
Here is my template class myFieldConstructorTemplate.scala.html:
#(elements: helper.FieldElements)
<div class="btn-group", data-toggle="buttons">
<label for="#elements.id">#elements.label</label>
<div class="input">
<label class="btn btn-primary">
#elements.input
<span class="errors">#elements.errors.mkString(", ")</span>
<span class="help">#elements.infos.mkString(", ")</span>
</label>
</div>
</div>
[EDIT3]: I have changed my class according to the last answer, but still the radiobuttons are melted into each other. So I want to point out that I am not fixated on using the inputRadioGroup from the playframework helper, if there is another solution that works the same and looks almost like bootstrap, I would gladly use that. It seems that changing the helper isnt that easy / intuitive. I appreciate any form of help!

The Twitter Bootstrap structure needs to be accurate.
Wrap the btn-group class around the inputRadioGroup helper like so:
<div class="btn-group" data-toggle="buttons">
#helper.inputRadioGroup(
answerRadioForm("Answer"),
options = answerList.map(answer => answer.answerID.toString -> answer.answerText),
'_label -> "Answer",
'_error -> answerRadioForm("answerID").error.map(_.withMessage("select answer")))
</div>
Then replace the template with:
#(elements: helper.FieldElements)
<label class="btn btn-primary">
#elements.input #elements.label
</label>
<span class="errors">#elements.errors.mkString(", ")</span>
<span class="help">#elements.infos.mkString(", ")</span>
In general, perhaps it'd be of interest another way of doing it. When you use fooForm("myField"...), you can use fooForm("myField[#i]"...) in a for loop where #i is a counter going from 0 to however many inputs there are. Then you can yourself sketch out the full HTML instead of using implicit values.
By the way, the documentation with the Scala version about all this has lots more information than the Java version. See here. It has more information on inputRadioGroup than the Java version of the documentation but still very useful reading for better understanding of all this.
Some Play Bootstrap plugin has code available on GitHub that is also useful reading especially as it uses implicit values also.
UPDATE
Here's a couple of GitHub projects showing how to achieve this:
with Scala version of Play: https://github.com/bjfletcher/play-bootstrap-radio-group
with Java version of Play: https://github.com/bjfletcher/play-java-bootstrap-radio-group
Screenshot of the result:

Move the implicitField-definition to the top of the file:
#(questionList: List[Question], answerList: List[Answer], answerRadioForm: Form[Answer])
#import helper._
#implicitField = #{ FieldConstructor(myFieldConstructorTemplate.f) }
#import helper.twitterBootstrap._
#helper.form(action = routes.Application.nextQuizPage(), 'id -> "answerRadioForm"){
#helper.inputRadioGroup(
answerRadioForm("Answer"),
options = answerList.map(answer => answer.answerID.toString -> answer.answerText),
'_label -> "Answer",
'_error -> answerRadioForm("answerID").error.map(_.withMessage("select answer")))
<button type="submit" class="btn btn-default" value="Send">
Next Question
</button>
}
#inputText(answerRadioForm("questionID"))
This makes sure the implicit value is available where it's needed.

Always keep params on top of template and remove the following import statement #import helper.twitterBootstrap._ this will conflict with your own field constructor.
#(questionList: List[Question], answerList: List[Answer], answerRadioForm: Form[Answer])
#import helper._
#implicitField = #{ FieldConstructor(myFieldConstructorTemplate.f) }
Hope it resolves your issue.

Related

MeteorJS: How to get id to load from collection

I'm trying to load an array (with simple text) and trying to load it up on the template whenever it is called. How do I get the ID from that specific item to get the array that I stored in it?
HTML Template:
<template name="commentMarker">
<div id="viewMarker">
<h3 id="markerTitle">{{markerName}}</h3>
<h6 id="markerCategory">{{markerCategory}}</h6>
<br>
<fieldset>
<legend>Description</legend>
<p>{{markerDescription}}</p>
</fieldset>
<form id="commentForm">
<fieldset>
<legend>Comments</legend>
<input type="text" id="markerId" name="idForComment" value={{markerId}}>
<textarea rows="3" cols="19" name="comment" id="commentArea" placeholder="Insert your comment here..."></textarea>
{{#each comments}}
<p id="oneComment">{{this}}</p>
{{/each}}
</fieldset>
<input type="submit" value="Comment" class="commentButton">
<input type="submit" value="Close" class="exitButton">
</form>
</div>
</template>
JS:
Template.commentMarker.helpers({
comments(){
alert(template.find("#markerId").value);
if(commentArray.length===0) return;
else return commentArray;
}});
This is where I insert the comment into the collection's item and it's working fine
Template.commentMarker.events({
'click .commentButton': function(e, template){
e.preventDefault();
var id = template.find("#markerId").value;
var comment = template.find("#commentArea").value;
Points.update(id, { $push: { comments: comment }});
commentArray = Points.findOne(id).comments;
template.find("#commentArea").value = ' ';
}
I tried with commentArray as a global variable which still is. But I'm at loss how I can get the Id from that specific item, I even put it's Id (with hidden display) in the form to actually be able to insert the comment. But it doesn't help me with showing the comments because I cannot seem to get to this field in the Template.helpers ...
Not entirely sure what you are trying to do. It's almost like as if you are displaying the comments right after you updated in to the collection. It looks like you are doing this entirely on local and not a online collection.
However, storing it as a session would work...or reactive var. Might not be the best solution tho. Basically replace commentArray = Points.findOne(id).comments; with:
Session.set('comments', Points.findOne(id).comments)
Then to get it out in helpers:
let commentArray = Session.get('comments')
It's not safe to use it all the time tho for sensitive data. Also try catch the findOne(id).comments because it does produce errors if it happen to not find it.
NOTE: If you are going to use Meteor.Methods, you cannot use Session. You have to return the id and find it in your helpers.

Remove object from array typescript(Angular 2)

I just try to delete object from array in typescript, in angular 2.4.0, let me show the code, its my html file:
button type="submit" (click)="addAnotherLanguague()" >Add non native languague</button>
<li *ngFor="let languague of listOfLanguagues;">
<div class="form-item form-item--text">
<label class="label invisible">Years studied</label>
<input type="number" min="0" [(ngModel)]="languague.yearsStudied" name="years" placeholder="Years studied"/>
</div>
<button type="submit" (click)="removeLanguague(languague)" >Remove</button> // here you can see use of method
</li>
And there is component.ts
(...)
this.listOfLanguagues = new Array <LanguagueInformationData>();
}
addAnotherLanguague(){
this.listOfLanguagues.push(new LanguagueInformationData);
}
removeLanguague(languague){
this.listOfLanguagues.slice(this.listOfLanguagues.indexOf(languague), 1);
}
(...)
Adding works well, but I tried everything to remove and still dont know how to transfer that languague's reference, I dont want to use .pop, because I want to remove exactly this languague below which is button.
Can you help me?
[edit]
I got problem again with this code, because every time I try to add new languague(push) it clears my data on classes existing in array, do you know what can cause it ?
<li *ngFor="let languague of listOfLanguagues; let i = index">
<button type="submit" (click)="removeLanguague(languague, i)" >Remove</button>
removeLanguague(languague, index){
this.listOfLanguagues.splice(index, 1);
}
You have to use splice and not slice
this.listOfLanguagues.splice(this.listOfLanguagues.indexOf(languague), 1);
slice returns a section of an array, and splice removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements

Form with select and text options

I have an HTML form that needs to collect information entered into a text box as well as options that are chosen from a set of dropdown menus. To give a little context, I am creating virtual machines that can be configured by the user on a web page. They must enter a name (arbitrary) and a hostmachine in two separate boxes in addition to selecting options from three different dropdown menus. Because I am working with clusters, there could be as many as 99 "rows" of dropdown menus representing different system configurations that will be a part of the cluster.
Is it possible (if so, advisable?) to have both the text fields and the dropdowns contained in one form? If not, how do I make sure that the submit button sends all the data to my Django server for processing as I need all of this information to ultimately come to the same place.
I currently have them in different forms, but just ignore this for now as it doesn't do anything at the moment. Also don't worry about the lack of dropdowns present in this code as the addSelect() JS function is fully functional. Just know that each added node is given a unique name (node1, node2, etc.) and goes into the div "nodes".
<body><b>Virtual Cluster Initialization</b><br></br>
<div id="container">
<div id="general">
<form method="POST" id="naming">Cluster name:<br>
<input type="text" name="cluster_name">
<br>
Host Machine:<br>
<input type="text" name="host_machine">
</form>
</div>
<form method="POST" id="node_config"></form>
<div id="nodes" form="node_config"></div>
<div id=node1">
<select name="node_type" id="node_type">Node Type</option>
(two options go here)
<select name="issp_version" id="issp_version>ISSP Version</option>
(7 or so options go here)
<select name="os" id="os">Operating System </option>
(about 20 options)
<button id="add" onclick="addSelect('nodes');">+</button>
</div>
<br></br><input type="submit"></input>
</body>
EDIT1: Added the an example dropdown for clarity. Would it be better to NOT make a new div for each node? I did this initially because it seemed like a good way to keep each node's configuration separate. Like I said, there could be up to 99 nodes, each with three dropdown menus.
Not really sure if I understand what you're asking. Showing us the code after your drop downs are added would help. Syntax wise, this wont work. Inputs should be inside forms and div doesn't have a form property.
Put everything into one form if you want it to all be in one post. If your dynamically adding new form elements you can use an array as element names.
How about something like this?
<script>
var nodeID = 0;
function addSelect() {
var html = "<div id='node_" + nodeID + "'>";
html += "<select name='node_type[" + nodeID + "]' id='node_type'><option>example</option></select>";
html += "<select name='issp_version[" + nodeID + "]' id='issp_version'><option>ISSP Version</option></select>";
html += "<select name='os[" + nodeID + "]' id='os'><option>Operating System </option></select>";
html += "</div>";
document.getElementById('nodes').innerHTML += html;
nodeID++;
}
</script>
<div style="margin-bottom:20px;"><b>Virtual Cluster Initialization</b>
</div>
<form>
<div id="container">
<div id="general">
<div>Cluster name:</div>
<div>
<input name="cluster_name" type="text">
</div>
<div>Host Machine:</div>
<div>
<input name="host_machine" type="text">
</div>
</div>
<div id="nodes">
<div>Nodes</div>
<div id="node_0">
<select name="node_type[0]" id="node_type"><option>example</option></select>
<select name="issp_version[0]" id="issp_version"><option>ISSP Version</option></select>
<select name="os[0]" id="os"><option>Operating System </option></select>
</div>
<div id="node_1">
<select name="node_type[1]" id="node_type"><option>example</option></select>
<select name="issp_version[1]" id="issp_version"><option>ISSP Version</option></select>
<select name="os[1]" id="os"><option>Operating System </option></select>
</div>
</div>
<div>
<button type="button" id="add" onclick="addSelect();">+</button>
</div>
</div>
<div>
<input type="submit">
</div>
</form>
Here is a JSfiddle to help you visualize what this does:
https://jsfiddle.net/fdss08w9/2/
Example of how you might use this in Django:
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = NameForm(request.POST)
# check whether it's valid:
if form.is_valid():
# Get the number of nodes we added
for id, node_type in enumerate(form.cleaned_data['node_type']):
issp_version = form.cleaned_data['issp_version'][id]
os = form.cleaned_data['os'][id]
#do stuff with node_type, issp_version, os

jQuery live filter/search

I'm building an icon library where the user on the front end (submitting a form) can select an icon. I managed to get everything working as far as the selection process. Now, the final product will have over 400 icons, and i wanted to add a search (ajax, i guess) or autocomplete input where the user can type a couple of letters and it filter's out those icons.
They search will be filtering out some with a class that has the prefix "icon-", so the search term would be whatever is after that prefix. (i.e: icon-TWIITER, icon-FACEBOOK, etc).
I started on jsFiddle here: http://jsfiddle.net/yQMvh/28/
an example would be something like this :
http://anthonybush.com/projects/jquery_fast_live_filter/demo/
http://cheeaun.github.io/jquery.livefilter/
I'm trying to stay away from jQuery plugins and try and figure this out before i resort to that. I'm using wordpress as the backend of the website.
as soon as the user types, it's already sorting out the icons that pertain to the input value.
My HTML Markup:
<div class="iconDisplay">Display's selected icon</div>
<span id="selectedIcon" class="selected-icon" style="display:none"></span>
<button id="selectIconButton">Select Icon</button>
<div id="iconSelector" class="icon-list">
<div id="iconSearch">
<label for="icon-search">Search Icon: </label>
<input type="text" name="icon-search" value="">
</div>
<span class="icon-icon1"></span>
<span class="icon-icon2"></span>
<span class="icon-icon3"></span>
<span class="icon-icon4"></span>
<span class="icon-icon5"></span>
<span class="icon-icon6"></span>
<span class="icon-icon7"></span>
<span class="icon-icon8"></span>
</div>
JS:
var iconVal = $(".icon_field").val();
$('#selectedIcon').addClass(iconVal);
$("#selectIconButton").click(function () {
$("#iconSelector").fadeToggle();
});
$("#iconSelector span").click(function () {
selectIcon($(this));
});
function selectIcon(e) {
var selection = e.attr('class');
$(".icon_field").val(selection);
$("#iconSelector").hide();
$('#selectedIcon').removeClass();
$('#selectedIcon').addClass(selection).show();
return;
}
Made an update on your jsfiddle jsfiddle.net/yQMvh/30 Just enter the classname e.g. icon-icon1 and all icons without icon-icon1 gets hidden

How to Get Model Data from Partial View?

I am creating a site in which I utilize partial views to display various bits of data about a single Model. Here is a bit of the HTML. (Note, all of these are contained within a single form and the Index page that these partials are rendered in is strongly typed to the main model. The main model contains various lists of data.)
<div id="tab1"><% Html.RenderPartial("Tab1", Model); %></div>
<div id="tab2"><% Html.RenderPartial("Tab2", Model.AnItemList1.FirstOrDefault<AnItemList1>()); %></div>
<div id="tab3"><% Html.RenderPartial("Tab3", Model.AnItemList2.FirstOrDefault()); %></div>
Here is ONE of the partial views headers (for 'tab2'):
<%# Language="C#" Inherits="System.Web.Mvc.ViewUserControl<AnItem1>" %>
The pages display correctly. The issue is that, when I enter data into the various parts of the partial pages and then submit the entire form (via POST), the data is not making it back to my data store (MSSQL) - but this only happens for any of the list items (that are contained within the Model). The first partial page does properly have its data set within the data store.
What am I doing wrong here? Should I only be passing the model to Html.RenderPartial and then get the specific model I need on the partial page? Should I pass the entire list and then get the first (right now, I only care about the first item in the list - that will EVENTUALLY change, but not any time soon).
Suggestions or thoughts appreciated.
Update: Here is how I accessing the properties on the partial views.
<div class="data-group">
<%: Html.CheckBoxFor(model => model.Property1) %>
<%: Html.LabelFor(model => model.Property1) %>
</div>
Update 2: Per request...
Controller Action (ScenarioController):
public ActionResult Index(int id = 0)
{
if (id == 0)
{
SavedScenario scenario = new SavedScenario();
scenario.AnItemList1.Add(new AnItem1());
scenario.AnItemList2.Add(new AnItem2());
return View("Index", scenario);
}
else
{
SavedScenario scenario = repository.GetScenario(id);
if (scenario == null)
return View("NotFound");
else
return View("Index", scenario);
}
}
[HttpPost]
public ActionResult Index(SavedScenario scenario)
{
if (ModelState.IsValid && TryUpdateModel(scenario, "SaveScenario"))
{
repository.Add(scenario);
repository.Save();
}
return View(scenario);
}
Rendered HTML (I can only include parts of it - this is a small sample of what is in the form):
<form action="/Scenario" id="form0" method="post">
<!-- This is the one that works - the basic Scenario. Top level. -->
<fieldset>
<legend>Scenario Information</legend>
<div class="data-group">
<div class="editor-label">
<label for="ScenarioName">Scenario Name</label>
</div>
<div class="option1">
<input class="wide" id="ScenarioName" name="ScenarioName" type="text" value="" />
</div>
<div class="validation">
<div><span class="field-validation-valid" id="ScenarioName_validationMessage"></span></div>
</div>
</div>
</fieldset>
<!-- This does not work or get submitted (as far as I can tell). -->
<div id="tab2">
<fieldset>
<legend>Tab2</legend>
<div class="data-group">
<input id="Property1" name="Property1" type="checkbox" value="true" /><input name="Property1" type="hidden" value="false" />
<label for="Property1" />
</div>
</div>
</fieldset>
</form>
My apologies for having to keep this so generic.
Hard to guess from this much code. However you should make sure that all properties of your models have the same prefix when they are posted back to the server
Edit: form field names should match property names of your model to correctly bind all values. You have two fields with the same name that you can bind in following way
[HttpPost]
public ActionResult Index(SavedScenario scenario, List<bool> Property1)
{
// here you can do with values coming in property1
if (ModelState.IsValid && TryUpdateModel(scenario, "SaveScenario"))
{
repository.Add(scenario);
repository.Save();
}
return View(scenario);
}
It might be issue with naming the fields on your partial forms. Try naming the fields on your partial views by prefixing it with the name of the Model passed into it...like 'AnItemList1.name' instead of just 'name'..I am just guessing here though...but that's what I did sometimes to fix the problem when I was getting values as null..