ASP.NET Core How to dynamically remove list element? - ajax

I want a user to be able to edit the order and delete some items. To do this, for each item I added a delete button (you can see it in the code below).
Partial view Views/Shared/EditorTemplates/Item.cshtml (Attribute data-id is hardcoded, because I have no idea how to pass the current OrderItem Id):
#model TimeTracker.Models.OrderItem
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Product" class="control-label"></label>
<input asp-for="Product" class="form-control" />
<span asp-validation-for="Product" class="text-danger"></span>
<input class="btn btn-default" type="button" id="btnDel" data-id="1" value="Remove" />
</div>
In ajax call comes the error in the console, which refers to the controller method. Here I am assuming I filled in the parameter data in ajax call incorrectly. I really don't know how to fill it in correctly in my case.
Failed to load resource: the server responded with a status of 405 ()
:5001/Orders/RemoveItem:1
Ajax call:
$("#btnDel").on('click', function () {
$.ajax({
async: true,
data: { order: $('#form').serialize(), orderItemId: $('#btnDel').data('id') },
type: "DELETE",
url: '/TaskTypes/RemoveItem',
success: function (partialView) {
console.log("partialView: " + partialView);
$('#itemsContainer').html(partialView);
}
});
});
Controller method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> RemoveItem([Bind("OrderItems")] Order order, int orderItemId)
{
order.OrderItems.RemoveAll(c => c.Id == orderItemId);
return PartialView("Items", order);
}

When you use ValidateAntiForgeryToken You have to pass RequestVerificationToken in your ajax header to validate your request.
Please visit https://learn.microsoft.com/en-us/aspnet/core/security/anti-request-forgery?view=aspnetcore-3.1 for more details about prevent cross site request.

Related

Spring Boot cannot render global error on multipart form submit

Currently I am trying to upload a CSV file containing records and this part is working fine.
However on submission of this form, if a data is not valid or missing, or if there is an import failure, I want to return a simple error message without refreshing the page.
Is it possible to return an error message on the same popup form, or is there any alternative way to do this?
Please find the code snippet below.
Form/Page:
<form id="uploadrecordform" method="POST" th:action="#{/import-record-file}" enctype="multipart/form-data">
<div class="form">
<h2>upload</h2>
<div class="form-element">
<label for="file">Upload record file</label>
<input type="file" name="file" class="form-control-file" id="file" accept=".csv" required>
</div>
<div class="form-element">
<button type="submit">
<p th:text="#{submit_text}"></p>
</button>
</div>
<div role="alert" th:if="${globalError}">
<strong>Error:</strong>
<span th:text="${globalError}"></span>
</div>
</div>
</form>
Note: on the page, there is an "add record" button and while clicking on the button, it opens the new form as a popup.
API sample code:
#RequestMapping(value = "/import-record-file", method = RequestMethod.POST)
#ResponseBody
public String importUserRecordCsvFile( #Valid #RequestParam("file") MultipartFile file, BindingResult result) {
final String username = principal.getName();
// validate file
if (file.isEmpty()) {
System.out.println("message Please select a CSV file to upload.");
ObjectError error = new ObjectError("globalError", "this is test error");
result.addError(error);
if (result.hasErrors()) {
return "errors/import-record-file";
}
}
return "empty";
}
I believe you are trying to make the controller validate the form and return the form back to the same pop up if there is an error.
You can do so using ajax. Let say your form is in a pop up and the pop up has the id popup.
<div id="popup">
<form>
<!-- form details -->
</form>
</div>
You can submit your form via ajax and have the result displayed in the pop up itself without refreshing the whole page.
The ajax function will do something as below,
$.ajax({
type: "POST",
data: formData,
url: url,
success: function (data) {
$('#popup').html(data); // data is always a simple html view
}
});
The endpoint handling the form submit (in your case, the endpoint /import-record-file) will function as follows, in case of error, the whole form is displayed back in the popup and in case of success, a simple html success message can be returned which will be displayed in the same popup.
So basically we are just overriding the content of the popup using ajax.

Browser-independent way to save text in a TextAreaFor

Using ASP.NET MVC, I have a View that contains a TextAreaFor, where I want users to be able to type in some notes and save them on-the-fly, see notes that were saved there before (whether by them or some other user), as well as modify existing notes (like to add additional notes). Here's what I have....
The divs in the View:
<div class="form-group">
<label for="InternalNotes" class="control-label">Internal Notes</label>
#Html.TextAreaFor(w => w.InternalNotes, new { #class = "form-control" , #id="notes" }) #*this is editable*#
</div>
<div class="form-group">
<div class="col-xs-6">
<button type="button" id="savenotes" class="btn btn-default btn-primary"><span class="glyphicon glyphicon-floppy-disk"></span> Save Request Notes</button>
<div style="color:green; display:none" id="notessuccess">Notes successfully saved</div>
<div style="color:red; display:none" id="noteserror">Notes unable to be saved</div>
</div>
<div class="col-xs-6 text-right">
<button type="submit" id="deletereq" class="btn btn-default btn-primary" value="delete" name="submit"><span class="glyphicon glyphicon-remove"></span> Delete Request</button>
</div>
</div>
So the user could type something into the TextAreaFor, then hit the "savenotes" button, which should save them via Ajax. This is the jQuery for that:
$(document).ready(function () {
$("#savenotes").click(function () {
$("#notessuccess").hide();
$("#noteserror").hide();
var id = #Model.AccessRequestId;
var notes = document.getElementById("notes").textContent; //innerText;
$.ajax({
data: { 'id': id, 'notes': notes },
type: 'POST',
//contentType: "application/json; charset=utf-8",
url: '/Administration/SaveRequestNotes',
success: function (data) {
if (data.success == "ok") {
$("#notessuccess").fadeIn();
} else {
$("#noteserror").fadeIn();
}
},
fail: function (data) {
$("#noteserror").fadeIn();
}
});
});
});
The "innerText" is commented out because that's what I was originally using, but it was only working in Internet Explorer - another user is using Chrome, where he could see the other user's notes that were already there, but when he'd try to save notes in addition to theirs, it would blow it all out so the notes would be empty!
So I changed it to "textContent". That still works in Internet Explorer, but now in both Chrome and Firefox while it won't empty out existing notes, it still won't save new notes added. What is a browser-independent way I can make this work so everyone's notes will get properly saved whatever they are using?
Thank you!
You can use the jQuery val() method get the text user entered to the textarea
var notes = $("#notes").val();
alert(notes);
You might also consider using the Url.Action helper method to generate the correct relative path to your action method.
url: '#Url.Action("SaveRequestNotes","Administration")',

Play framework write Action with Ok(...) that doesn't load new page

Play framework 2.4.x. A button is pressed on my home page that executes some code via Ajax, and returns its results beneath the button without loading a new page. The results wait for a user to input some text in a field and press "submit". Those results Look like this:
<li class="item">
<div>
<h3>Email: </h3>
<a>#email.tail.init</a>
<h3>Name: </h3>
<a>#name</a>
</div>
<div>
<h3>Linkedin: </h3>
<form class="linkedinForm" action="#routes.Application.createLinkedin" method="POST">
<input type="number" class="id" name="id" value="#id" readonly>
<input type="text" class="email" name="email" value="#email" />
<input type="text" class="emailsecondary" name="emailsecondary" value="" />
<input type="text" class="name" name="email" value="#name" />
<input type="text" class="linkedin" name="linkedin" value="" />
<input type="submit" value="submit" class="hideme"/>
</form>
</div>
<div>
<form action="#routes.Application.delete(id)" method="POST">
<input type="submit" value="delete" />
</form>
</div>
</li>
Along with some jquery that slides up a li after submission:
$(document).ready(function(){
$(".hideme").click(function(){
$(this).closest('li.item').slideUp();
});
});
However, since a form POST goes inside an Action that must a return an Ok(...) or Redirect(...) I can't get the page to not reload or redirect. Right now my Action looks like this (which doesn't compile):
newLinkedinForm.bindFromRequest.fold(
errors => {
Ok("didnt work" +errors)
},
linkedin => {
addLinkedin(linkedin.id, linkedin.url, linkedin.email, linkedin.emailsecondary, linkedin.name)
if (checkURL(linkedin.url)) {
linkedinParse ! Linkedin(linkedin.id, linkedin.url, linkedin.email, linkedin.emailsecondary, linkedin.name)
Ok(views.html.index)
}else{
Ok(views.html.index)
}
}
)
Is it possible to return Ok(...) without redirecting or reloading? If not how would you do a form POST while staying on the same page?
EDIT: Here is my attempt at handling form submission with jquery so far:
$(document).ready(function(){
$(".linkedinForm").submit(function( event ) {
var formData = {
'id' : $('input[name=id]').val(),
'name' : $('input[name=name]').val(),
'email' : $('input[name=email']).val(),
'emailsecondary' : $('input[name=emailsecondary]').val(),
'url' : $('input[name=url]').val()
};
jsRoutes.controllers.Application.createLinkedin.ajax({
type :'POST',
data : formData
})
.done(function(data) {
console.log(data);
});
.fail(function(data) {
console.log(data);
});
event.preventDefault();
};
});
This is an issue with the browser's behavior on form submission, not any of Play's doing. You can get around it by changing the behavior of the form when the user clicks submit.
You will first want to attach a listener to the form's submission. You can use jQuery for this. Then, in that handler, post the data yourself and call .preventDefault() on the event. Since your javascript is now in charge of the POST, you can process the data yourself and update your page's HTML rather than reloading the page.
What you need is use ajax to submit a form, check this: Submitting HTML form using Jquery AJAX
In your case, you can get the form object via var form = $(this), and then start a ajax with data from the form by form.serialize()
$.ajax({
type: form.attr('method'),
url: form.attr('action'),
data: form.serialize(),
success: function (data) {
alert('ok');
}
});
In order to accomplish this task, i had to use play's javascriptRouting
This question's answer helped a lot.
I'm not experienced with jquery so writing that correctly was difficult. For those that find this, here is my final jquery that worked:
$(document).ready(function(){
$("div#results").on("click", ".hideme", function(event) {
var $form = $(this).closest("form");
var id = $form.find("input[name='id']").val();
var name = $form.find("input[name='name']").val();
var email = $form.find("input[name='email']").val();
var emailsecondary = $form.find("input[name='emailsecondary']").val();
var url = $form.find("input[name='url']").val();
$.ajax(jsRoutes.controllers.Application.createLinkedin(id, name, email, emailsecondary, url))
.done(function(data) {
console.log(data);
$form.closest('li.item').slideUp()
})
.fail(function(data) {
console.log(data);
});
});
});
Note that my submit button was class="hideme", the div that gets filled with results from the DB was div#results and the forms were contained within li's that were class="item". So what this jquery is doing is attaching a listener to the static div that is always there:
<div id="results">
It waits for an element with class="hideme" to get clicked. When it gets clicked it grabs the data from the closest form element then sends that data to my controller via ajax. If the send is successful, it takes that form, looks for the closest li and does a .slideUp()
Hope this helps

angularUI select dropdown displays data only after entering a character

I am using AngularJS v1.2.15 and angular-ui / ui-select. My select HTML is:
<div class="form-group col-md-3">
<div class="input-group select2-bootstrap-append">
<ui-select ng-model="modelOwner.selected" theme="select2" class="form-control">
<match placeholder="Select Owner">{{$select.selected.name}}</match>
<choices repeat="item in owner | filter: $select.search">
<span ng-bind-html="item.name | highlight: $select.search"></span>
</choices>
</ui-select>
<span class="input-group-btn">
<button ng-click="modelOwner.selected = undefined" class="btn btn-danger">
<span class="glyphicon glyphicon-trash"></span>
</button>
</span>
</div>
</div>
My call in controller is:
$scope.modelOwner = {};
OwnersFactory.query({}, function (data) {
$scope.owner = data;
});
My service code:
bootstrapApp.factory('OwnersFactory', function ($http,$state,serviceUrl,$resource,$log) {
return $resource(serviceUrl + 'owner/:id', {}, {
show: { method: 'GET', params: {}, isArray: false }
})
});
Now, in my form i can view the values only after entering at least a single character. I want this select dropdown to display values just by clicking on the dropdown (not by entering any character.)
Possible Solution: if i could load my state only after all the AJAX calls have been made.
Please help me out here.
if you are using ui-router you can use resolve on each state, so that the call is resolved before initializing the controller
https://github.com/angular-ui/ui-router/wiki#resolve

how do I add an ajax response to my form without adding a new js file?

I have this simple submission form and I want to "ajaxify" it so the user isn't redirected to this page thanks.php after submission. I want the content from thanks.php to respond and show inside the div.
What jquery code will plug right into this to show the response.
<div ><form method="post" action="http://domain.com/thanks.php">
<input type="text" placeholder="Enter Email" name="email" id="email" >
<input type="submit" name="submit" value="Submit" ></form></div>
I would give 'form' and 'div' a class or id so it is not so generic, but this should work.
$("form").submit(function (){
$.ajax({
url: "http://{url}/thanks.php",
type: 'POST',
data: {email: $("#email").val()},
success: function ( data ) {
$("div").html(data);
}
});
return false;
});

Resources