Thymeleaf dynamic form action url - forms

<thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version>
Springboot
How to construct expression for th:action "action url" in form? Url depends on "name" variable.
I'm trying like this, but it's not working:
<form method="get"
th:action="#{'/' + {baseUrl}(baseUrl=${#strings.isEmpty(name) ? '' : 'user/find/'})}">
<input type="text" th:value="${name}"/>
<button type="submit">Find</button>
<button type="button"
th:classappend="${#strings.isEmpty(name)}?'hidden':''"
onclick="clearSearch()">X</button>
</form>
and I tried this, doesn't work too:
<form th:with="baseUrl=(${#strings.isEmpty(name) ? '/' : '/user/find/'})"
th:action="#{${baseUrl}}" method="get">

Oh sorry. I realized Thymeleaf is not for dynamically handling input field.
I just write a JS code.
$(document).ready(function () {
var searchForm = $(this).find('#searchForm');
searchForm.on('click', '#searchButton', function (event) {
var form = $('#searchForm');
var name = $('#searchInputName').val();
var addUrl = (name === '') ? '/' : '/user/find/';
form.attr('action', addUrl);
});
});
after that I realized I can realize it simpler in my controller :)
#GetMapping("/user/find/")
public String getNextPage(Model model, Pageable pageable,
#RequestParam(name = "name") String name) {
if (name.isEmpty()) return "redirect:/";
....
}

You can write dynamic url like below
th:action="#{${#strings.isEmpty(name) ? '/' : '/user/find'}}"
If you want you can add dynamic value to your urls like below
th:action="#{${#strings.isEmpty(name) ? '/' : '/user/find' + name}}"

Related

Angular2 Dynamic form displaying incorrect ngModel data

Im generating a table and form to edit each field but when I try to edit it, the data displayed in the input fields is incorrect.
HTML
<form (ngSubmit)="onEditSubmit()">
<span>What the form gets</span>
<div *ngFor="let k of keys; let i = index" class="form-group row">
<span>{{k|uppercase}}</span>
<div class="col-md-9">
<input [(ngModel)]="updateModels[keys[i]]" type="text" name="text-input" class="form-control" placeholder="{{k}}" />
</div>
</div>
<button>Submit</button>
</form>
JS
public data: any = [{id: 1, code1: "VALUE1", code2: "VALUE2", code3: "VALUE3"},{id: 2, code1: "TEST1", code2: "TEST2", code3: "TEST3"}];
public keys: any = ["code1","code2","code3"];
public activeRoleId = '';
public updateModels: any = {};
public toEditData: any = "";
public editedData: any = "";
constructor() {
}
onEditSubmit() {
this.editedData = this.updateModels;
}
/*CLick on element actions*/
editElement(item): void {
for (let i = 0; i < this.keys.length; i++) {
this.updateModels[this.keys[i]] = item[this.keys[i]];
}
console.log(this.updateModels);
this.toEditData = this.updateModels;
this.activeRoleId = item.id;
}
Plunker
This error is caused because of you are using a form. If you'd remove the form tags, this would work as you want. BUT, since you are using a form, each name-attribute has to be unique, so that these fields are evaluated as separate form fields.
So the solution for your problem is quite simple, just assign an unique name to your fields, you can make use of the index, so change:
name="text-input"
for example to the following:
name="text-input{{i}}"
Then it works like a charm! :) Here's the forked
Plunker

FilterOperator.Contains Doesn't work correctly

I have a model which contains site info.
If I do a search on the beginning of the "SearchString" (using FilterOperator.Contains), the input list is populated fine, but if I search on a string not at the beginning of the string, nothing is returned.
e.g ... SearchString= "100 My Town"
An entry of '100' works.
An entry of 'My' doesn't !
No error is displayed either.
Controller :
handleSuggest: function (oEvent) {
var sTerm = oEvent.getParameter("suggestValue");
var aFilters = [];
if (sTerm) {
aFilters.push(new Filter("SearchString", sap.ui.model.FilterOperator.Contains, sTerm ));
}
oEvent.getSource().getBinding("suggestionItems").filter(aFilters);
},
View :
<Input
id="shopInput"
type="Text"
placeholder="Enter Shop Number ..."
showSuggestion="true"
suggest="handleSuggest"
suggestionItems="{/SiteSet}"
change="validateInput"
liveChange="liveChange"
valueStateText="Shop Number must be valid"
suggestionItemSelected="suggestionItemSelected">
<suggestionItems>
<core:Item text="{SearchString}" />
</suggestionItems>
Any Ideas?
I've now found the solution from right under my nose in the input.suggestions.custom section of https://sapui5.netweaver.ondemand.com/sdk/explored.html#/entity/sap.m.Input/samples
handleSuggest : function(oEvent) {
var view = this.getView();
var sTerm = oEvent.getParameter("suggestValue")
view.byId("shopInput").setFilterFunction(function(sTerm, oItem) {
// A case-insensitive 'string contains' style filter
return oItem.getText().match(new RegExp(sTerm, "i"));
});
},
I'd an other problem with FilterOperator.Contains.
My scenarios involves:
oData V2
Backend Java using Olingo
Frontend in Apache
I'd putted in my controller this Filter:
aFilter.push(new Filter("description", FilterOperator.Contains, `'${fDescription}'`));
But the filter didn't work.
Then, I'd investigated the problem and identify that FilterOperator.Contains, in SAPUI5 framework doesn't build the query parameter right.
Because the src/sap/ui/model/odata/ODataUtils.js package on ODataUtils._createFilterSegment function doesn't included the suffix "eq true":
case "Contains":
return "substringof(" + oValue1 + "," + sPath + ")";
Searching in odata.org (https://www.odata.org/documentation/odata-version-2-0/uri-conventions/) and conforming de sample:
$filter=substringof('Alfreds', CompanyName) eq true
To fix the problem I'd changed the controller to use FilterOperator.EQ in this way:
aFilter.push(new Filter(`substringof('${fDescription}',description)`, FilterOperator.EQ, true));
As far as I can see you are missing to set the ID in the Suggestion item, which enables you to search for it? Your Filter goes on SiteId, but your Suggestion item is only binded to SearchString.
This should help:
<suggestionItems>
<core:Item key="{SiteId}" text="{SearchString}" />
</suggestionItems>

Zend_Form with Ajax/json

I'm a bit lost using Zend_Form with Ajax. I have a form in a class extending Zend_Form called from my controller, that way :
GoodAddGroup.php
class Default_Form_GoodAddGroup extends Zend_Form {
(...)
public function init()
{
$this->setMethod('post');
$this->setAction("process-add-group");
$this->setName("addgroupgood");
// Load Elements class
require "Form/Elements.php";
$magElements = new Elements();
// Category
$categoryElement = $magElements->getCategorySelectField();
$categoryElement->setDecorators($this->elementDecorators);
// Barcode
$barcodeElement = $magElements->getGoodBarcodeTextField();
$barcodeElement->setDecorators($this->elementDecorators);
(...)
// Add elements to the form
$this->addElements(array(
$categoryElement,
//$codeElement,
$barcodeElement,
$serialElement,
$warehouseElement,
$submitButtonElement
));
$this->setDecorators($this->formDecorators);
}
}
In GoodsController.php
private function getAddGroupForm()
{
return new Default_Form_GoodAddGroup();
}
public function addGroupAction()
{
// Initialize the form for the view.
$this->view->form = $this->getAddGroupForm();
}
public function processAddGroupAction()
{
$form = $this->getAddGroupForm();
(...)
if ($_POST)
{
if ($form->isValid($_POST))
{
// Do things
} else {
$this->view->form = $form;
}
}
}
Basically, the form has a category select field, when selecting a category, a second "code" selector is added filled with the items related to this category. When the page with the form is displayed (http://myapp/goods/add-group), everything works fine, the ajax call does its job, the second select field is added and well fed, but as you can see, the form processing is done with the processAddGroupAction(), this method get the instance of the form to get its values and to re-display it in case of problem. But that way, my "new" select field doesn't exist anymore, so i can never validate the form.
It's my first attempt using ajax/json with Zend, i think i need help at this poind.
Thank you.
EDIT : added the view code as requested
<script>
$(function(){
$("#cats").change(function(){
getSelectBox(this);
});
$("#code").parent().parent().css('display', 'none');
getSelectBox($("#cats"));
});
function getSelectBox(element)
{
if($(element).val() != '')
{
$("#code").parent().parent().css('display', 'block');
if ($('#code').length <= 0) {
$("#cats").after('<select name="code" id="code" style="margin-left:10px"></select>');
}
$.getJSON("/goods/json-get-codes-from-category", {id: $(element).val(), ajax: "true"}, function(j){
console.log(j);
var options = "";
jQuery.each(j, function(i, val) {
options += '<option value="' + i + '">' + i + val + '</option>';
});
$("#code").html(options);
});
}
}
</script>
<?php echo $this->form; ?>
You can add the select element 'code' in the form, but don't display it in the view (it will be created from the js). So when the form is posted the 'code' will also be validated since it is in the $_POST.
After post you have to display the select box without $("#cats").change({..}). You can accomplish it by spiting the js code into functions
<script>
$(function(){
$("#cats").change(function(){
getSelectBox(this);
});
getSelectBox($("#cats"));
});
function getSelectBox(element)
{
if($(element).val() != '')
{
if ($('#code').length <= 0) {
$("#cats").after('<select name="code" id="code" style="margin-left:10px"></select>');
}
$.getJSON("/goods/json-get-codes-from-category", {id: $(element).val(), ajax: "true"}, function(j){
console.log(j);
var options = "";
jQuery.each(j, function(i, val) {
options += '<option value="' + i + '">' + i + val + '</option>';
});
$("#code").html(options);
});
}
}
</script>
Hope this helps
Ok, i found the solution! I post it in case it can be useful for other people.
I knew what was the problem, but unable to know how to achieve it.
At form processing time, the new select element does not exist for the action as it has been added in the view with javascript. To fix this, we need to inform the action about the new field, to do so, we first need to override the method “isValid” of Zend_Form in the form class :
public function isValid($values)
{
$values = $this->_modifyElements($values);
return parent::isValid($values);
}
Then create a method "_modifyElements" that will modify the form by adding the new element :
protected function _modifyElements($values)
{
// Search for codes
$dbu = new DbUtils();
$codes = $dbu->getCodesFromCategory($values['cats']);
// Remove the current code element
$this->removeElement('code');
// Create a new element
$codeElement = new Zend_Form_Element_Select('code', array());
$codeElement->setLabel(_('Code :'));
$codeElement->setRequired(true);
$codeElement->addMultiOptions($codes);
$codeElement->setValue($values['code']);
$codeElement->setDecorators($this->elementDecorators);
$this->addElement($codeElement);
return $values;
}
We have to override the method "populate" too :
public function populate(array $values)
{
$values = $this->_modifyElements($values);
return parent::populate($values);
}
And voilà. It works for me ;>
All the credits about this go to Mohamed : http://jamandcheese-on-phptoast.com/2009/12/13/on-fly-elements-in-zend_form/

Dynamically add textareas client-side to a form in lift

I've got a form based on the sample in http://simply.liftweb.net/index-4.2.html#toc-Section-4.2 and I was wondering if there's a way of having a button on the page that would add a textarea each time it's clicked, and then in the lift code get that as an array of strings.
What I'm picturing is something like this:
<form class="lift:OnSubmit?form=post">
Name: <input name="name"><br>
Age: <input name="age" value="0"><br>
<span id="somecomments"></span>
<input type="button" onclick="$('#somecomments').append($('<textarea cols=80 rows=10 name=comments>'))" value="Add Comment"/>
<input type="submit" value="Submit">
</form>
//in scala:
object OnSubmit {
def render = {
var name = ""
var age = 0
var comments = List("")​
def process() {
S.notice("Name: "+name)
S.notice("Age: "+age)
S.notice(comments)
S.redirectTo("/")
}
}​
"name=name" #> SHtml.onSubmit(name = _) &
"name=age" #> SHtml.onSubmit(s => asInt(s).foreach(age = _)) &
"name=comments" #> SHtml.onSubmit(comments = _) &
"type=submit" #> SHtml.onSubmitUnit(process)
}
}
But I get the compile error that the comments field is a String so I can't assign it to a List with "name=comments" #> SHtml.onSubmit(comments = _)
What's the best way to make this code work?
for prepending:
"name=comments" #> SHtml.onSubmit(comments ::= _)
for appending:
"name=comments" #> SHtml.onSubmit(comments :+= _)
Here I describe how you can add any number of fields (I have a textarea and a "related" numeric field.
You add them using jQuery and then Lift gets all the data as a json object.
Dynamically adding fields to a Lift application

ASP.NET MVC 2 RC: How to use the EditorFor to render correct name attributes for a List<>?

In the MVC RC 2 docs, we find:
Expression-based helpers that render input elements generate correct name attributes when the expression contains an array or collection index. For example, the value of the name attribute rendered by Html.EditorFor(m => m.Orders[i]) for the first order in a list would be Orders[0].
Anyone care to link an example of the C# view code (using a List where the result can bind back to the Model upon post)?
Just as a reference, I use the following code to verify the model binds correctly round trip. It simply shows view that allows change, then displays a view with the edited data upon form submission.
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
var myStudents = new List<Student>();
myStudents.Add(new Student { Name = "Harry" });
myStudents.Add(new Student { Name = "Tom" });
myStudents.Add(new Student { Name = "Richard" });
var myClass = new Classroom {Students = myStudents};
return View(myClass); // EditorFor()
}
[HttpPost]
public ActionResult Index( Classroom myClass)
{
return View("IndexPost", myClass); // DisplayFor()
}
This code:
<% for (int count = 0; count < Model.Students.Count; count++ )
{ %><%=
Html.EditorFor(m => m.Students[count]) %><%
}
%>
Rendered this output:
<input class="text-box single-line" id="Students_0__Name" name="Students[0].Name" type="text" value="Harry" />
<input class="text-box single-line" id="Students_1__Name" name="Students[1].Name" type="text" value="Tom" />
<input class="text-box single-line" id="Students_2__Name" name="Students[2].Name" type="text" value="Richard" />
And when I posted the content, the display was this (because I have a Student.ascx):
<table>
<tr><td><span>Harry</span> </td></tr>
<tr><td><span>Tom</span> </td></tr>
<tr><td><span>Richard</span> </td></tr>
</table>
But that's it (I think). Next question is how to get rid of those name="" tags.