Dynamic checkbox from list with Thymeleaf - checkbox

I try to save the values from dynamically created checkboxes:
<div>
<ul>
<li th:each="item, stat : *{users}">
<input type="checkbox" th:field="*{users[__${stat.index}__]}" th:value="${item}" />
<label th:text="${item}"></label>
</li>
</ul>
</div>
The controller provides the String items as follwing:
public List<String> getUsers() {
return users;
}
And the setter for the Strings is:
public void setUsers(final String[] users) {
for (final String string : users) {
System.out.println(string);
}
}
The values are correct shown in the html page. But when i click save button, and the setter is called, the values are empty. What can i do, where is the problem?
Any help would appreciate.

Please check out section about handlin multi-value checkboxes in Tutorial: Thymeleaf + Spring.
You should provide some model attribute (of type List<String>) containing all users possible to select. Let's call it selectableUsers.
Then it can collaborate with your form-backing bean (that one containing users) in a following manner:
<div>
<ul>
<li th:each="item : ${selectableUsers}">
<input type="checkbox" th:field="*{users}" th:value="${item}" />
<label th:for="${#ids.prev('users')}" th:text="${item}"></label>
</li>
</ul>
</div>
Note I think that getter and setter for a field should handle the same type, but they don't (getter returns List<String> however setter consumes String[])

What you are trying to do looks logical, but it does not work that way.
If you did not get it resolved you can do this instead:
In relevant method of your controller you can add list of titles for your checkboxes:
List<String> allUsers = Arrays.asList("abc","xyz"); // OR generate list dynamically
model.addAttribute("selectableUsers", allUsers);
Or add it to ModelAndView if that is what you are using.
Change your html to what was suggested by #Jakub Ch.
Change your getter and setter methods as follows:
private String users;
...
public String getUsers() {
return this.users;
}
public void setUsers(String users) {
this.users = users;
}
Then 'users' field will contain comma separated String values or their id numbers ( depending on how you set it up) indicating selected checkboxes. Then you can convert String values to array using code like below or if id numbers are stored get String values from your ArrayList.
public List<String> getStrings() {
return Arrays.asList(strings.split(","));
}
Hope it helps.

Related

Laravel - Display Array/String Object in view from database

Data are stored as ["item_1", "item_2"] in database like shown below.
I want to display those data in view blade properly.
Product Model
protected $fillable = ['name', 'prod_id'];
public function models() {
return $this->hasMany(Model::class, 'prod_id');
}
Model Model
protected $fillable = ['model', 'prod_id'];
protected $cat = ['model'=>'array'];
public function model()
{
return $this->belongsTo(Product::class, 'prod_id');
}
Controller - store method
public function create (Request $request, Product $product){
$models = new Model;
{
$model = json_encode(request('models'));
$items->models = $model;
$product->models()->save($models);
}
}
Controller show method
public function show(request $id){
$product = Product::findorfail($id);
$models = Model::with(['model'])->where('prod_id', $product->id)->get();
return view ('show', compact('product', 'models'));
Create View
<input type="checkbox" name="model[]" value="Samsung">
<input type="checkbox" name="model[]" value="Nokia">
<input type="checkbox" name="model[]" value="Apple">
<button>Add Model</button>
I tried show view:
#foreach($models as $model)
{{ json_decode($model->models) }}
#endforeach
It throws
htmlspecialchars() expects parameter 1 to be string, array given
What am I missing.
PS: MySQL does not support json column, so I saved as text column.
you need to do someting like this.
Model Model
protected $fillable = ['models', 'prod_id']; // screenshot says that the field name is "models"
protected $cast = ['models' => 'array']; // the property is $cast no $cat
public function product()
{
return $this->belongsTo(Product::class, 'prod_id');
}
ModelController - store method
public function store (Request $request){
$product = Model::create([
'models' => json_encode($request->models),
'prod_id' => $request->prod_id
]);
return redirect()->back()->with('success', 'created!');
}
public function show(Request $id){
$model = Model::findOrFail($id)->with('product');
return view ('model.show', compact('model'));
}
ProductController show method
public function show(request $id){
$product = Product::findOrFail($id)->with('models'); // the method name is findOrFail() no findorfail
// $models = Model::with(['model'])->where('prod_id', $product->id)->get();
return view ('show', compact('product'));
}
Into the show View
#foreach($product->models as $models)
#foreach(json_decode($models->models) as $model)
{{ $model }}
#endforeach
#endforeach
Your Models Model confuses me a little bit. You seem to have a field name model that's the same as a relationship method name. That means whenever you include that relation, it'd functionally override that property with data from the related table. (I say 'functionally' because you're using dynamic properties, whereas it is actually possible to explicitly tell Eloquent whether you want an attribute or relation without making it guess.)
That said, your $model->models property could be coming back as an array for one of two reasons. The first is that it may be accidentally referring to a relational data-set and not the JSON string you were expecting. The second is you've corrected the protected $cat = ['model'=>'array']; to read protected $cast = ['models'=>'array'];, and it's stepping on your toes now. By casting it to an array, it may be getting automatically get interpreted back into one before you call json_decode on it.
Either way, I'd dd($model->models) to see what it is first.
You need to change your foreach like this:
#foreach($models->models as $model)
{{ json_decode($model) }}
#endforeach
because Your array is like this
{"id":18,"prod_id":22,"models":{"id":22,"user_id":1}}
In here the $models is getting only id and prod_id models is still array so your foreach should be #foreach($models->models as $model)
Sample Code is here:
$arr = '{"id":18,"prod_id":22,"models":{"id":22,"user_id":1}}';
echo '<pre>';
foreach (json_decode($arr->models) as $str){
echo $str;
}

Filter card list with Razor View

Im trying to make a filter like in AngularJS when u use:
ng-repeat="u in users | filter:searchBar">
And your input filter looks like
<input type="text" id="searchBar" placeholder="start typing" ng-model="searchBar">
But the things its that im working on MVC with Razor View and I do not know how to approach this filter.
The list of cards is made with a foreachlike this:
#foreach{ var item in Models){
<div class="card">
<div class="card-container">
some content
</div>
</div>
}
Any ideas?
You can do the filtering with ajax. Here is a server side filtering solution.
First, you should move the code which renders the result to a partial view. Let's say you created a partial view called CustomerList.cshtml. Move the list code to that.
#model IEnumerable<Customer>
#foreach (var item in Model)
{
<div class="card">
<div class="card-container">
#item.Name
</div>
</div>
}
Now in your main view, you can call this partial view and pass the data to it. Wrap the call to the partial view in a container div. Add a input element for user to enter the search key. Assuming your main view is also strongly typed to IEnumerable<Customer>
#model IEnumerable<Customer>
<input type="text" id="search" data-url="#Url.Action("Index")" />
<div id="div-items">
#Html.Partial("CustomerList",Model)
</div>
Now we need to have some javascript code which listen to the keyup event on the search input, read the value of it and make an ajax call to the server where it uses the search key and get the filtered set of data, pass that to the same partial view and return the partial view result.
You can use jQuery $.get method
$(document).ready(function () {
$("#search").keyup(function() {
var v = $(this).val();
$.get($(this).data("url"), { searchKey: v }).done(function(res) {
$("#div-items").html(res);
});
});
});
Now make sure your server action method returns the filtered data like this
public ActionResult Index(string serchKey="")
{
var items = db.Customers.AsQueryable();
if (!String.IsNullOrEmpty(searchKey))
{
items = items.Where(a => a.Name.StartsWith(searchKey));
}
var t = items.ToList();
if (Request.IsAjaxRequest())
{
return PartialView("CustomerList",t );
}
return View(t);
}
Another option is to do client side filtering. on the items. But if i am going that direction, i would choose a client side MVC framework like angular to do that for me

Unable to access merge field values inside visualforce component in angularJs

View:
<div ng-controller = "ClPortalRegistrationController">
<div ng-repeat="(key, value) in ObjectApiFieldsetMap">
{{key}} {{value}} //this is printing correct result
<c:FieldSetComponent objectAPIName="'{{key}}'" fieldSet="'{{value}}'" cid="'{{key}}'"
sectionTitle="Section 1" columns="2" textAlign="center"></c:FieldSetComponent>
</div>
</div>
Controller:
$scope.ObjectApiFieldsetMap = {
Applications__c: "Application_FieldSet_One",
clcommon__Collateral__c: "Collateral_FieldSet_One"
};
Now when I'm trying to access {{key}},{{value}} inside c:FieldSetComponent ,its only passing string as {{key}} and {{value}} and not the converted result. How can I access values stored inside key, value inside component?
Posting the solution which I implemented as a work around.
Turns out that you cannot access angular merge field values inside visualforce components. So instead of manipulating(segregating input into key-value pair) values inside angular controller, I have to push the logic to apex controller.
<apex:component controller="RegistrationController" access="global">
<apex:repeat value="{!ObjectApiFieldsetMap}" var="apiName">
<c:FieldSetComponent objectAPIName="{!apiName}" fieldSet="{!ObjectApiFieldsetMap[apiName]}"
cid="{!apiName}{!ObjectApiFieldsetMap[apiName]}"
columns="1" textAlign="center">
</c:FieldSetComponent>
</apex:repeat>
</apex:component>
And in my apex controller i.e RegistrationController , I have set the logic to segregate key values from a map input which I'm using inside visualforce component
global class RegistrationController {
global Map<String,String> ObjectApiFieldsetMap {
get {
ObjectApiFieldsetMap = arrangeApiFieldSetsByOrder();
return ObjectApiFieldsetMap;
}
set;
}
global Map<String,String> arrangeApiFieldSetsByOrder() {
Map<String,String> ObjectApiFieldsetMap = new Map<String,String>();
/* logic for segregation */
return ObjectApiFieldsetMap;
}
}

Spring MVC, Angular AngularJS drop-down list - problems on persist ManyToOne

Problem: I have a Purchase Order table and Country table which primary key is a foreign key in my PO table. On the front end I have a form (angularJS) used for creation of a new PO with a bunch of fields and about 9 drop-down list fields form where user can select whatever info is required for the PO.
For the sake of the example I will use the Country drop-down. (All drop-downs are getting populated from the DB).
The problem I have is with the persistence. After submitting the form, if I use the debugger and I check the Purchase Order object that is being passed to the service all my drop downs objects are null, except the other normal input fields which belong to the PO table.
I think the problem is Angular, If I check the network tab in my chrome browser under developer tools I see the country being passed as countryData:2. Thing is I am new to Angular so I am bit confused at this stage on how I should handle this as data is coming from the drop-down list and how I should return the country object. Here http://tinypic.com/r/2rd9pxl/8 I put a screenshot of my network tab from the browser which show the from Data that is being posted.
Populate the country drop-down list
<div class="control-group">
<label class="control-label">*Country</label>
<div class="input-append">
<div data-ng-init="getCountryDataFromServer()">
<b>Person Data:</b> <select id="countryData" ng-model="contact.countryData">
<option value="">-- Select Countries --</option>
<option data-ng-repeat="country in countries" value="{{country.countryId}}">{{country.countryname}}</option>
</select><br>
</div>
</div>
<div class="input-append">
<label>
<span class="alert alert-error"
ng-show="displayValidationError && newContactForm.countryData.$error.required">
<spring:message code="required"/>
</span>
</label>
</div>
</div>
Model
#Entity
#Table(name = "PURCHASEORDER",schema = "POTOOL")
#Audited
public class PurchaseOrder {
#Id
#GeneratedValue
private int id;
private String tower;
private Double revenue;
//other properties of the PO
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "country_id")
private Country country;
//Other ManyToOne relationships
//Getter and setters
Controller
#RequestMapping(method = RequestMethod.POST, produces = "application/json")
public ResponseEntity<?> create(#ModelAttribute("contact") PurchaseOrder purchaseOrder,
#RequestParam(required = false) String searchFor,
#RequestParam(required = false, defaultValue = DEFAULT_PAGE_DISPLAYED_TO_USER) int page,
Locale locale) {
purchaseOrderServiceImpl.save(purchaseOrder);
if (isSearchActivated(searchFor)) {
return search(searchFor, page, locale, "message.create.success");
}
return createListAllResponse(page, locale, "message.create.success");
}
Service
public void save(PurchaseOrder purchaseOrder) {
purchaseOrderRepository.save(purchaseOrder);
}
Repository
public interface PurchaseOrderRepository extends
PagingAndSortingRepository<PurchaseOrder, Integer> {
}
I don't think there is any issue because of angular since you have defined ng-model="contact.countryData" for dropdown and option has value from value="{{country.countryId}}". When you submit value country.countryId is going to server which is absolutely fine. I think you should have conversion mechanism which will convert data from UI to Hibernate Entity(i.e. PurchaseOrder). Generally DTO(Data transfer Object) is used to get data from UI and using setter and getter we set to Hibernate entity and latter you will save that entity.
This question might help you.
DTO to Entity And Entity to DTO
When you are working with HTML(not with JSP), it better to use #RequestBody at place of #ModelAttribute to get complete body of data. Please refer the following links:
Link 1
Link 2

How to bind form to model in NancyFX

I'm new to NancyFX and trying to simply bind a posted form to my model.
In the module when trying to access the posted values I run following statement:
string email = this.Context.Request.Form["Email"];
Debug.WriteLine(email);
Result is:
"Nancy.DynamicDictionaryValue" instead of posted value
Can anybody tell me what newbie mistake I'm doing:
The form looks like:
<form method="post" action="account">
<input type="text" id="Email" />
<input type="password" id="Password" />
<input type="submit" value="Create" />
</form>
the routing in Module contructor:
Post["/"] = parameters => CreateAccount(parameters);
The dynamic dictionary returns a dynamic value, if you cast it to a string (implicitly or explicitly) you'll get what you want, or just use the build in model binder https://github.com/NancyFx/Nancy/wiki/Model-binding
Just adding to the correct answer above in the hope it is useful to nancy-newbies like me.
Because the Nancy Form and Query are of type dynamic you can access the values using the name of the form or query-string param (see terms and max in the example code). I use a simple base class just to make the syntax terser throughout the rest of my modules.
Note: The ExpandoObject Model in the base class is there so I can just throw values at my view-model and not have to worry about cluttering things up with strongly typed data-transfer classes (this also helps prevent exposing any secret domain instance data).
public class SearchModule : _BaseModule
{
public SearchModule(ISearchService searchService)
{
Get["/search"] = _ =>
{
if (!Query.terms.HasValue) return HttpStatusCode.BadRequest;
var terms = (string) Query.terms;
var max = (Query.max.HasValue) ? (int) Query.max : 3;
Model.SearchResults = searchService.GetResults(max, terms);
...
};
}
}
public class _BaseModule : NancyModule
{
protected dynamic Model = new ExpandoObject();
public dynamic Query { get { return Request.Query; } }
public dynamic Form { get { return Request.Form; } }
}

Resources