Updating input field by leaving - laravel

Is it possible with Laravel livewire to change a field in the database when leaving the input field?

You can use wire:model.lazy to achieve an action when leaving (loosing focus) on an input field.
<div>
<input type="text" class="form-control" wire:model.lazy="billingRate">
</div>
class Rate extends Component
{
public $billingRate;
public function updatedBillingRate(){
dd($this->billingRate); // don't forget to use $this to access class property
// persist to database here
}
}
In the above example, whenever billingRate value changes, it will trigger the updatedBillingRate() method.
Please checkout a similar thred on laracast here (https://laracasts.com/discuss/channels/livewire/livewire-how-to-action-the-value-of-an-input-without-a-button)

In my own perspective, you can implement JavaScript event listener when leaving the input field, an ajax request will fire-up to update the database.

Related

Pass data from Thymeleaf template to springboot controller

I have simple web application written using Springboot and Thymeleaf templates. Report controller receives the data from form and builds the TestPlanReportResponse object which is added as model attribute like this:
#PostMapping("/report")
public String homeSubmit(#ModelAttribute HomeFormInput homeFormInput, Model model, Errors errors) {
final TestPlanReportResponse response = new TestPlanReportResponse(homeFormInput);
model.addAttribute("allData", response);
return "charts";
}
I can work with that data in "charts" thymeleaf template and show the data I need, but I need to send exactly the same object back to controller when button is clicked, but i getting TestPlanReportResponse
object as parameter with nulls set.
#PostMapping("/report/send")
public String sendReport(#ModelAttribute TestPlanReportResponse reportData, Model model) {
//reportData contains just nulls
}
Here is how my button is set in charts template:
<form action="#" th:action="#{/report/send}" th:object="${allData}" method="post">
<button type="submit">Send the report</button>
</form>
So my question is how to send the object back from thymeleaf template? Should i create a hidden input and put there the "allData" object just to send it back? It looks for me like dirty hack. What would be the appropriate way to pass data back? I want to have this app stateless so don't to store the data on a server side.
When I used to work with Spring and Thymeleaf and form, we had the same issue, passing the data back and forth between a form, the template, and different controllers.
And what you suggest is what we did, we used hidden input as dirty as it may look,it was the standard suggested answer, we did not find anything better.
You need to create an input, with a type a value and link it to a field, like this:
<form action="#" th:action="#{/report/send}" th:object="${allData}" method="post">
<input type="hidden" th:value="*{allDataValue1}" th:field="*{allDataField1}" />
//Do this for all your attributes/values that you wish to pass to the controller
<button class="btn btn-info btn-lg btn-block" type="submit">Send the report</button>
</form>
Though, i found this answer, you can try looking into this thread

retain model values in case of error and showing same thymeleaf template

As per my understanding, model attributes are associated with every request and they can not survive multiple requests, until we add them as flashAttributes.
I have a simple controller method which shows a couple of options to user to select from. However, those options are being attached to thymeleaf template using model attributes.
<div class="input-group mb-3" th:each="ingredient : ${recipes.ingredients}">
<div class="input-group-prepend">
<div class="input-group-text">
<input aria-label="Checkbox for following text input" name="ingredient"
th:value="${ingredient.name}" type="checkbox">
</div>
<input aria-label="Text input with checkbox" class="form-control" disabled
th:value="${ingredient.name + ' ' + ingredient.price + 'Rs.'}"
type="text">
</div>
assume "recipes" as model attribute here, which was injected to modelMap inside the controller.
when bean validation fails, below line exectutes.
if (errors.hasErrors()) return "selectItem";
and selectItem template is re-rendered, but whatever model attributes I have set inside previous controller vanishes.
I have solved this using a #ModelAttribute method inside the same controller to set model attributes for every HTTP requests for the specific controller(until it is not in controllerAdvice for global effect).
I am being confused if I am on right way || is there any elegant way to achieve this.
Setting Model attribute for every request is kind of overhead, when I want them to be available for handful of request mappings.
When you say:
selectItem template is re-rendered, but whatever model attributes I
have set inside previous controller vanishes.
You mean that when the page reloads due to validation errors, your model attributes are no longer existing and Thymeleaf probably returns an error, because it cannot find them, correct?
If this is the case, then you have to manually prepare the same model attributes within the if statement (i.e. adding them to your MapModel):
if (errors.hasErrors()) {
map.addAttribute("recipes", recipes);
return "selectItem";
}
Alternatively, if you need this model attribute also on other pages in your controller, you can reduce code duplication by declaring a method with the ModelAttribute annotation, which will add this attribute to all models in your controller:
#ModelAttribute("recipes")
public Recipes loadRecipes() {
// get list of Recipes
return list;
}

How to pass innerHTML content to th:field in thymeleaf?

I am creating a Spring Boot e-commerce website, and I have a span tag containing the total price so far in cart, and update with JavaScript behind the scenes:
<form method="POST" th:object="${chargeRequest}">
<span class="total">
Total: <span class="total-price">$0</span>
</span>
</form>
and I pass a model attribute model.addAttribute("chargeRequest", new ChargeRequest()); to this page, and ChargeRequest class is defined as:
public class ChargeRequest {
private int amount;
// constructor, setter/getter
// ...
}
The question is: how can update amount in chargeRequest, and pass it back to the controller using Thymeleaf?
th:field is only valid for <input>, <select>, <textarea>, I cannot directly put it to <span> tag
I have tried to pass 2 attributes:
model.addAttribute("amount", new String("$0"));
model.addAttribute("chargeRequest", new ChargeRequest());
<span class="total">
Total: <span class="total-price" th:text="${amount}">$0</span>
<input type="hidden" th:value="${amount}, id="amount", name="amount">
</span>
But I don't know how to update ${amount} such that amount in chargeReqeust can be updated automatically?
Well unfortunately for you, you won't be able to do that with a simple controller. You will need a #ControllerAdvice that will serve as a global controller to be able to update the cart whenever someone adds a product into their cart or remove products from their cart.
In other word, you need a GLOBAL CONTROLLER. This global controller should check the SESSION attribute to allow you to change the amount of total cart and update it every time a change occurs. For example, check this out below.
#ControllerAdvice
public class GlobalCartController{
#Autowired
private HttpSession session;
#ModelAttribute("cartModel")
public CartModel getCartTotal(){
if(session.getAttribute("cart")==null){
//Here you create cart attribute for the session
// then
session.setAttribute("cartModel", cartObjectWithUpdatedTotal);
}
return (CartObjectWithUpdatedTotal) session.getAttribute("cartModel");
}
Note that global controller will be always checked at every time at all routes and don't require a routing, this allows you to use the HttpSession autowired class which allows you to
identify a user across more than one-page request or visit a Web site and to store information about that user.
Cheers

Custom constraint validation error doesn't display next to field in Symfony2

I am using FOSUserBundle in my Symfony2 project. I added 'birthday' field in User entity, because it is required in registration form. I also added a proper field (type=birthday) to the registration form. I have to check if age of a user is above 18. I prepared my own Constraint for this following this tutorial. Everything works perfectly, but error message is attached to form not to field, and I want error message next to field. Instead I get it above the whole form. Every other error in form is displayed next to a proper field. Does anybody know how to force constraint to be attached to the field, not to the form?
EDIT:
Twig code fragment which renders this particular field:
<div class="grid_8" style="margin-bottom:20px;">
<div class="grid_2 alpha">{{ form_label(form.date_of_birth)}}</div>
<div class="grid_4">{{ form_widget(form.date_of_birth)}}</div>
<div class="grid_2 omega">{{ form_errors(form.date_of_birth)}}</div>
</div>
At the begining of the form I also have:
<div class="grid_8">
{{form_errors(form)}}
</div>
If you attach callback to form – not to particular field, you should set property path a bit different way:
public function isFormValid($data, ExecutionContext $context)
{
if ( $condition == false ) {
$context->addViolationAt('[date_of_birth].date_of_birth', 'You are too young!');
}
}
You can add the violation to one particular field with addViolationAtSubPath() instead of the classic addViolation() used in the validator. You can have a look at the method definition here.
An example
Lets take the example of a validator that need to validate that the username property is alphanumeric:
class ContainsAlphanumericValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint)
{
if (!preg_match('/^[a-zA-Za0-9]+$/', $value, $matches)) {
$this->context->addViolationAtSubPath('username',$constraint->message, array('%string%' => $value));
}
}
}
Edit
You can also take a look at addViolationAtPath() and addViolationAt().

How to pass both model value & text value to controller in mvc?

I want to pass two values from view to controller . i.e., #Model.idText and value from textbox. here is my code:
#using HTML.BeginForm("SaveData","Profile",FormMethod.Post)
{
#Model.idText
<input type="text" name="textValue"/>
<input type="submit" name="btnSubmit"/>
}
But problem is if i use "Url.ActionLink() i can get #Model.idText . By post action i can get textbox value using FormCollection . But i need to get both of this value either post or ActionLink
using ajax you can achieve this :
don't use form & declare your attributes like this in tags:
#Model.idText
<input type="text" id="textValue"/>
<input type="submit" id="btnSubmit"/>
jquery:
$(function (e) {
// Insert
$("#btnSubmit").click(function () {
$.ajax({
url: "some url path",
type: 'POST',
data: { textField: $('#textValue').val(), idField: '#Model.idText' },
success: function (result) {
//some code if success
},
error: function () {
//some code if failed
}
});
return false;
});
});
Hope this will be helpful.
#using HTML.BeginForm("SaveData","Profile",FormMethod.Post)
{
#Html.Hidden("idText", Model.idText)
#Html.TextBox("textValue")
<input type="submit" value="Submit"/>
}
In your controller
public ActionResult SaveData(String idText, String textValue)
{
return null;
}
I'm not sure which part you are struggling with - submitting multiple values to your controller, or getting model binding to work so that values that you have submitted appear as parameters to your action. If you give more details on what you want to achieve I'll amend my answer accordingly.
You could use a hidden field in your form - e.g.
#Html.Hidden("idText", Model.idText)
Create a rule in global.asax and than compile your your with params using
#Html.ActionLink("My text", Action, Controller, new { id = Model.IdText, text =Model.TextValue})
Be sure to encode the textvalue, because it may contains invalid chars
Essentially, you want to engage the ModelBinder to do this for you. To do that, you need to write your action in your controller with parameters that match the data you want to pass to it. So, to start with, Iridio's suggestion is correct, although not the full story. Your view should look like:
#using HTML.BeginForm("SaveData","Profile",FormMethod.Post)
{
#Html.ActionLink("My text", MyOtherAction, MaybeMyOtherController, new { id = Model.IdText}) // along the lines of dommer's suggestion...
<input type="text" name="textValue"/>
<input type="submit" name="btnSubmit"/>
#Html.Hidden("idText", Model.idText)
}
Note that I have added the #Html.Hidden helper to add a hidden input field for that value into your field. That way, the model binder will be able to find this datum. Note that the Html.Hidden helper is placed WITHIN your form, so that this data will posted to the server when the submit button is clicked.
Also note that I have added dommer's suggestion for the action link and replaced your code. From your question it is hard to see if this is how you are thinking of passing the data to the controller, or if this is simply another bit of functionality in your code. You could do this either way: have a form, or just have the actionlink. What doesn't make sense is to do it both ways, unless the action link is intended to go somewhere else...??! Always good to help us help you by being explicit in your question and samples. Where I think dommer's answer is wrong is that you haven't stated that TextValue is passed to the view as part of the Model. It would seem that what you want is that TextValue is entered by the user into the view, as opposed to being passed in with the model. Unlike idText that IS passed in with the Model.
Anyway, now, you need to set up the other end, ie, give your action the necessary
[HttpPost]
public ActionResult SaveData(int idText, string textValue) // assuming idText is an int
{
// whatever you have to do, whatever you have to return...
}
#dommer doesn't seem to have read your code. However, his suggestion for using the Html.ActionLink helper to create the link in your code is a good one. You should use that, not the code you have.
Recapping:
As you are using a form, you are going to use that form to POST the user's input to the server. To get the idText value that is passed into the View with the Model, you need to use the Html.Hidden htmlhelper. This must go within the form, so that it is also POSTed to the server.
To wire the form post to your action method, you need to give your action parameters that the ModelBinder can match to the values POSTed by the form. You do this by using the datatype of each parameter and a matching name.
You could also have a complex type, eg, public class MyTextClass, that has two public properties:
public class MyTextClass
{
public int idText{get;set}
public string TextValue{get;set;}
}
And then in your controller action you could have:
public ActionResult SaveData(MyTextClass myText)
{
// do whatever
}
The model binder will now be able to match up the posted values to the public properties of myText and all will be well in Denmark.
HTH.
PS: You also need to read a decent book on MVC. It seems you are flying a bit blind.
Another nit pick would be to question the name of your action, SaveData. That sounds more like a repository method. When naming your actions, think like a user: she has simply filled in a form, she has no concept of saving data. So the action should be Create, or Edit, or InformationRequest, or something more illustrative. Save Data says NOTHING about what data is being saved. it could be credit card details, or the users name and telephone...

Resources