Can parsley.js work with knockout.js? - validation

I just learned about parsley.js and I'm trying to add it's validation capabilities to my project that is already wired up with knockout.js bindings. Here is the markup:
<form id="form-add-gift" data-bind="submit: addGift" data-validate="parsley">
<table class="table table-striped">
<thead>
<tr>
<th>Gift</th><th>Description</th><th>Url</th><th></th>
</tr>
</thead>
<tfoot>
<tr>
<td><input id="txtName" name="txtName" type="text" data-required="true" data-bind="value: newGift().name" /></td>
<td><input id="txtDescription" name="txtDescription" type="text" data-bind="value: newGift().description" /></td>
<td><input id="txtUrl" name="txtUrl" type="text" data-type="url" data-bind="value: newGift().url" /></td>
<td><button id="btnAdd" name="btnAdd" class="btn" type="submit" data-bind="disable: newGift().name.length > 0">Add gift</button></td>
</tr>
</tfoot>
<tbody data-bind="foreach: gifts">
<tr>
<td id="tdName" data-bind="text: name"></td>
<td id="tdDescription" data-bind="text: description"></td>
<td id="tdUrl" data-bind="text: url"></td>
<td><a id="btnRemove" class="btn btn-danger" href="#" data-bind="disabled: $parent.isClaimed, click: $parent.removeGift">Remove</a></td>
</tr>
</tbody>
</table>
</form>
When I click the "Add gift" button, my knockout.js addGift() function fires and the parsley.js validation occurs afterward. Obviously this is incorrect. Is there any way to get parsley.js to play nice with the knockout.js bindings?

I don't think parsley.js meant to work directly with KnockoutJs, but this can't stop you from using them both nicely.
Quick look through the Documentation -> Javascript - > Form you can use this method:
$('#form').parsley('isValid');
Useful if you want to integrate the form validation process inside
custom functions, without triggering error messages.
UPDATE
you can try this too:
$( '#form' ).parsley( 'validate' );
Useful if you want to integrate the form validation process inside
custom functions.

Related

Form is empty when submitted using Thymeleaf

I want to fill a list of applications for a university in the frontend. Each entry is supposed to hold two buttons: one for accepting the application and one for rejecting it. I created one form for each submit-button each.
<div class="container">
<div class="row">
<div class="col-12">
<table class="table table-hover">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">Firstname</th>
<th scope="col">Lastname</th>
<th scope="col">Grade</th>
<th scope="col">NC</th>
<th scope="col">Course</th>
<th scope="col">Certificate</th>
<th scope="col">Recommendation</th>
<th scope="col">Decision</th>
</tr>
</thead>
<tbody>
<tr th:each="applicationOpen, rowStat: ${lstOpApplications}">
<th th:text="${rowStat.count}">1</th>
<td th:text="${applicationOpen.firstname}">firstname</td>
<td th:text="${applicationOpen.lastname}">lastname</td>
<td th:text="${applicationOpen.highschool_grade}">grade</td>
<td th:text="${applicationOpen.nc}">nc</td>
<td th:text="${applicationOpen.name}">coursename</td>
<td th:text="${applicationOpen.highschool_certificate}">certificate</td>
<td th:text="${applicationOpen.document}">recommendation</td>
<td>
<form action="#" th:action="#{/Bewerberubersicht}" th:object="${decisionForm}" method="post">
<input type="hidden" name="application_id" th:field="*{application_id}" value=${applicationOpen.id}"/>
<input type="hidden" name="decision" th:field="*{decision}" value=1/>
<button type="submit" class="btn btn-success"><i class="fas fa-edit"></i>Accept</button>
</form>
<form action="#" th:action="#{/Bewerberubersicht}" th:object="${decisionForm}" method="post">
<input type="hidden" name="application_id" th:field="*{application_id}" value=${applicationOpen.id}/>
<input type="hidden" name="decision" th:field="*{decision}" value=2/>
<button type="submit" class="btn btn-danger"><i class="fas fa-edit"></i>Reject</button>
</form>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
When I look in the Browser's Dev-Tools I can see that the content of the POST-request is
application_id: ""
decision: ""
When I replace value=${applicationOpen.id} with e.g. value=5 it is still empty. Hence, it should not be a problem with applicantOpen. Also, the list in the frontend is being filled just fine, so all of that should work. I first thought is is a problem with the DecisionForm class, but it seems my subsequent problems are caused by the issue described here.
The th:field attribute overrides content of name and value attributes. The decisionForm object seems to have empty fields so all forms have empty values.
Basically using th:object with th:field is convenient when you need to have your form prepopulated with values. It establishes initial state of your form, not the target. Using them makes sense for single form, not for multiple forms in loop with varying values.
In your case: please remove both th:object and th:field attributes. Instead use value="1" or value="2" for decision input, and th:value="${applicationOpen.id}" for application_id input.

Thymeleaf binding a selected value from a table

I'm having a table with multiple elements that are generated from a List and every table element from that list has a button.By clicking that button there is a post submit request which should bind the values from that table data element to a #ModelAttribute object in spring boot.
The problem is that i'm able to map the whole list but I want to bind only the table element where the button was pressed.
<div class="table-responsive">
<form th:action="#{/saveAd}" th:object="${object}" method="POST">
<table class="table table-sm table-hover">
<thead class="thead-dark">
<tr>
<th>Image</th>
<th>Title</th>
<!-- <th style="width: 16.66%">Link</th> -->
<th>Price</th>
<th>City</th>
</tr>
</thead>
<tbody id="myTable">
<th:block th:each="element, itemStat: *{lista}">
<tr
th:onclick="'javascript:rowClicked(\'' + *{lista[__${itemStat.index}__].url} + '\');'">
<input type="hidden" readonly="readonly"
th:name="?"
th:value="${element.getUrl()}" />
<td>
<input type="hidden" readonly="readonly" th:name="img" th:value="${element.getImg()}" />
<img th:src="*{lista[__${itemStat.index}__].img}" class="size" name="img" />
</td>
<td>
<input type="hidden" readonly="readonly" th:name="?" th:value="${element.getTitle()}" />
<span th:text="*{lista[__${itemStat.index}__].title}"></span>
</td>
<td>
<input type="hidden" readonly="readonly" th:name="?" th:value="${element.getPrice()}" />
<span th:text="*{lista[__${itemStat.index}__].price}"></span>
</td>
<td>
<input type="hidden" readonly="readonly" th:name="?" th:value="${element.getCity()}" />
<span th:text="*{lista[__${itemStat.index}__].city}"></span>
</td>
<td><input class="btn btn-danger" type="submit"
value="Save"></td>
</tr>
</th:block>
</tbody>
</table>
</form>
</div>
The Controller:
#RequestMapping(path = "/saveAd", method = RequestMethod.POST)
public String saveAd(#ModelAttribute("Value") ListCreationAutovitDto listCreationAutovitDto) {
return "home";
}
I have a hidden input type for every td which should map the values but i've tried naming it in different ways but i can't make it work.Are there any ways to bind only the values where the button was pressed?
Idea 1: Make each td contain a form pointing to your controller method. Then bind the values you want to pass to the model by using hidden input fields in the form. Each button would then do a submit for the form it is in.
Idea 2: If you're mapping json to your java objects in your app, you can construct the json request body in JavaScript to contain only the stuff you want for that request

How do you implement a generic CRUD grid using knockout.js?

I have a very nice grid with Create, Retrieve, Edit and Delete functionality. I am using knockout.js on the client and WebAPI on the back end.
I would like to extend this to a number of different objects. Essentially I have a List<T> where T is an MVC view model and the knockout view and view model should be able to work out what they should look like based on T.
Does anyone know of a simple way to display any viewmodel (with specific controls for editing based on the viewmodel itself - eg datepicker for dates, input for string, drop downs etc) in a grid format and have generic CRUD functions.
Below is an example of the exiting HTML I am using (for a specific object):
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Value Date</th>
<th>Position Date</th>
<th>Book</th>
<th>Currency</th>
<th>Currency Base</th>
<th>Amount1</th>
<th>Amount2</th>
<th>Position Type</th>
<th style="width: 100px; text-align:right;" />
</tr>
</thead>
<tbody data-bind=" template:{name:templateToUse, foreach: pagedList }"></tbody>
</table>
and the templates look like this:
<script id="itemsTmpl" type="text/html">
<tr>
<td data-bind="text: valueDate.formattedDate"></td>
<td data-bind="text: positionDate.formattedDate"></td>
<td data-bind="text: book"></td>
<td data-bind="text: currency"></td>
<td data-bind="text: currencyBase"></td>
<td data-bind="text: amount1"></td>
<td data-bind="text: amount2"></td>
<td data-bind="text: positionType"></td>
<td class="buttons">
<a class="btn" data-bind="click: $root.edit" href="#" title="edit"><i class="icon-pencil"></i></a>
<a class="btn" data-bind="click: $root.remove" href="#" title="remove"><i class="icon-trash"></i></a>
</td>
</tr>
</script>
<script id="editTmpl" type="text/html">
<tr>
<td><input data-bind="datepicker: valueDate.formattedDate, datepickerOptions: { dateFormat: 'yy/mm/dd' }"/></td>
<td><input data-bind="datepicker: positionDate.formattedDate, datepickerOptions: { dateFormat: 'yy/mm/dd' }"/></td>
<td><input data-bind="value: book"/></td>
<td><input data-bind="value: currency"/></td>
<td><input data-bind="value: currencyBase"/></td>
<td><input data-bind="value: amount1"/></td>
<td><input data-bind="value: amount2"/></td>
<td><input data-bind="value: positionType"/></td>
<td class="buttons">
<a class="btn btn-success" data-bind="click: $root.save" href="#" title="save"><i class="icon-ok"></i></a>
<a class="btn" data-bind="click: $root.cancel" href="#" title="cancel"><i class="icon-remove"></i></a>
</td>
</tr>
</script>
I would like the View as well as the View Model to be generic and not "hard-coded" as above. I am sure someone else must have done something like this.
One solution would be that:
AJAX call gets a list of JSON viewmodels
If list is empty then do not display anything
If list has items, pick first item and go through properties
Start constructing the view on the client by looping through proprties
Append the string as a DOM element to the main DIV
Wireup knockout
There are problems with this approach. What if a property is null for the first object but exists in another? If so we do not setup the element for it.
A better option is to use content negotiation to get a dedicated template:
So GET /api/customers will return customers but if you request text/knockout-template+html then you get back a knockout template as string and then you append to DIV and wireup knockout. So server can generate template using reflection or customise for some models.

Event delegation in Firefox after Ajax

Below is example html, script and php to submit and reload two forms via jquery ajax. In the real application, the same forms will not be reloaded but a series of forms will be loaded after each is submitted.
In chrome, IE and safari; clicking submit results in the intended ajax request. The server responds as intended. Results (showing post variables for conformation) and new forms are loaded into the page as intended. Those new forms can then be submitted and all is good.
In firefox; everything works as intended until you try to submit the forms that were loaded via ajax. They will not submit. The original forms do work. It is only the ones added via ajax that don't submit.
I am guessing that this is an "event delegation" issue with the way firefox updates the DOM after the ajax. I have read the documentation at http://api.jquery.com/on/ and other places but I am at a loss...
Any help or suggestions would be greatly appreciated! My goal is to get this functional in the latest versions of chrome, IE, safari and firefox.
html page with script
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Example Ajax Form Submit</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
</head>
<body>
<div id="results"></div>
<div id="forms">
<div>
<table width="100%" cellspacing="0" cellpadding="0" class="ui-widget">
<form>
<tr>
<td width="100"></td><td width="300"></td><td></td>
</tr>
<tr>
<td class=""colspan="3"><h1>Form 1</h1></td>
</tr>
<tr>
<td>Input 1A</td><td colspan="2"><input type="text" name="1A" /></td>
</tr>
<tr>
<td colspan="3"><input type="hidden" name="1B" value="Form 1 was submitted" /></td>
</tr>
<tr>
<td colspan="3"><button type="submit" >Click Me!</button><input type="submit" value="Submit Form 1" /></td>
</tr>
</form>
</table>
</div>
<div>
<table width="100%" cellspacing="0" cellpadding="0" class="ui-widget">
<form>
<tr>
<td width="100"></td><td width="300"></td><td></td>
</tr>
<tr>
<td class=""colspan="3"><h1>Form 2</h1></td>
</tr>
<tr>
<td>Input 2A</td><td colspan="2"><input type="text" name="2A" /></td>
</tr>
<tr>
<td colspan="3"><input type="hidden" name="2B" value="Form 2 was submitted" /></td>
</tr>
<tr>
<td colspan="3"><input type="submit" value="Submit Form 2" /></td>
</tr>
</form>
</table>
</div>
</div>
<script type="text/javascript">
$(document).ready(function(){
$("#forms").on("submit", "form", function(e) {
e.preventDefault();
$.ajax({
url: "testAjax.php",
type: "POST",
cache: false,
data: $(this).serializeArray(),
success: function(jsonData) {
htmlData = jQuery.parseJSON(jsonData);
$.each(htmlData, function(id, data){
$("#"+id).html(data);
});
},
error: function(jqXHR, textStatus, errorThrown){
$("#results").html("<p>Somthing went wrong!</p>");
}
});
});
});
</script>
</body>
</html>
testAjax.php
<?php
$htmlOutput['forms'] =
'<div>
<table width="100%" cellspacing="0" cellpadding="0" class="ui-widget">
<form id="login" method="post" enctype="multipart/form-data" target="_self" action="_self">
<tr>
<td width="100"></td><td width="300"></td><td></td>
</tr>
<tr>
<td class=""colspan="3"><h1>Form 1</h1></td>
</tr>
<tr>
<td>Input 1A</td><td colspan="2"><input type="text" name="1A" /></td>
</tr>
<tr>
<td colspan="3"><input type="hidden" name="1B" value="Form 1 was submitted" /></td>
</tr>
<tr>
<td colspan="3"><button type="submit" >Click Me!</button><input type="submit" value="Submit Form 1" /></td>
</tr>
</form>
</table>
</div>
<div>
<table width="100%" cellspacing="0" cellpadding="0" class="ui-widget">
<form>
<tr>
<td width="100"></td><td width="300"></td><td></td>
</tr>
<tr>
<td class=""colspan="3"><h1>Form 2</h1></td>
</tr>
<tr>
<td>Input 2A</td><td colspan="2"><input type="text" name="2A" /></td>
</tr>
<tr>
<td colspan="3"><input type="hidden" name="2B" value="Form 2 was submitted" /></td>
</tr>
<tr>
<td colspan="3"><input type="submit" value="Submit Form 2" /></td>
</tr>
</form>
</table>
</div>';
$htmlOutput['results'] =
'<p>Form 1:<br> 1A = ' . $_POST['1A'] . '<br>1B = ' . $_POST['1B'] . '</p>' .
'<p>Form 2:<br> 2A = ' . $_POST['2A'] . '<br>2B = ' . $_POST['2B'] . '</p>';
echo json_encode($htmlOutput);
?>
Open Firebug DOM inspector and verify that your form indeed loaded. I am pretty sure you are not allowed to nest form element directly under table element, and FF is likely simply dropping it.
In general issues like this one are always due to broken HTML. Verify that your HTML is correct. Verify that loaded DOM is what you expect it to be.
Here is how your HTML is being parsed. As expected form has been mangled.

How to display the similar information in every view?

I would like to display the information about user's registration state. It should be displayed on every view, but i consider, that writting this code for about 20 times is a bad idea. By the way, this is the code, it works, i guarantee that:
View/RegistrationInfo:
<table width="95%" border="0" cols="2">
<tr>
<td width="50%" height="124"><h2><strong>Simple Blog </strong></h2></td>
<td width="50%" height="124">
#if (System.Web.HttpContext.Current.User.Identity.IsAuthenticated)
{
using (Html.BeginForm("LogOff", "Account"))
{
<table width="95%" height="86" border="0">
<tr>
<td width="50%" valign="middle">Hello, #System.Web.HttpContext.Current.User.Identity.Name</td>
<td width="50%" valign="middle" align = "right"><input type="submit" value="Exit" /></td>
</tr>
</table>
}
}
else
{
using (Html.BeginForm("LogIn", "Home"))
{
<table width="95%" height="86" border="0">
<tr>
<td width="45%" valign="bottom">Login:</td>
<td width="45%" valign="bottom">Password:</td>
<td width="10%"></td>
</tr>
<tr>
<td width="45%">
<p>
<input type="text" name="login" />
</p>
</td>
<td width="45%">
<p>
<input type="password" name="password" />
</p>
</td>
<td width="10%" align="left">
<input type="submit" value="Enter" />
</td>
</tr>
<tr>
<td width="45%" valign="top">
#Html.ActionLink("Register", "Register", "Account")
</td>
</tr>
</table>
}
}
</td>
</tr>
</table>
<hr align="left" width="100%" size="2" />
As you can see, this view doesn't require to be strongly typed... and i don't know, should I modify any controllers (because, all information is stored in HTML Context), and how. I tried to call
#Html.RenderAction("RegistrationInfo", "Home");
or
#Html.RenderPartial("RegistrationInfo", "Home");
and every time i receive compile error with message about invalid parametres
While Darin's answer perfectly covers your question, I would point out that registration information seems like it's part of your layout more (master page for the .aspx-inclined) than an actual action.
So what I would do (and indeed, do do) is put it either in the header part of your layout in the top right or under your side menu or something, and let the layout handle all the details of rendering it (with a conditional around it to hide it when you're not logged in, so you don't see it on the login screen).
The following should work:
#Html.Partial("~/Views/Home/RegistrationInfo.cshtml")
Or if you are inside a view of the Home controller simply:
#Html.Partial("RegistrationInfo")
Or if you put this partial in ~/Views/Shared/RegistrationInfo.cshtml you will be able to reference with the shorthand syntax.

Resources