Editing a viewmodel's member via button without submit - asp.net

I'm using Asp Net Core 3.1 and am working on developing admin controls to approve and delete submitted images that are awaiting approval. The functionality that I am developing and am stuck on is as follows: I have created a grid of images waiting approval (using a loop in razor) and would like to click a button to "approve" that image via the logic I have written in my controller. How would I pass that data to the controller without refreshing the page?
View Model
public class ImageManagerViewModel
{
public List<ListingImages> Images;
public List<Tuple<long, string>> ListingNames;
}
Controller
public class AdminController : Controller
{
public ActionResult ApproveImage(int listingID, long imageID, bool isFeatured)
{
....
}
}
Client-side
#foreach (ListingImages row in Model.Images)
{
....
<div class="d-flex card-footer">
<a a class="btn btn-success btn-space"
href="#Url.Action("ApproveImage", "Admin", new { listingID = row.ListingId, imageID = row.ImageId, isFeatured = false})" role="button">Approve</a>
}

As VDWWD described, you wanna use ajax to achieve this behavior.
I made a quick sample for your code (I didn't have the ability to test it atm though).
Your loop (you can also use hidden input fields to track the ids of every single item):
#foreach (ListingImages row in Model.Images)
{
...
<span class="imageId">#(row.ImageId)</span>
<span class="listingId">#(row.ListingId)</span>
<input type="button" class="btn btn-success approveBtn">Approve</button>
}
JQuery code in the script section:
<script>
$(document).on("click",
".approveBtn",
function () {
var imageId = $(this).closest(".imageId").text();
var listingId = $(this).closest(".listingId").text();
$.ajax({
url: "/Admin/ApproveImage",
type: "POST",
data: {
__RequestVerificationToken: $('input[name=__RequestVerificationToken]').val(),
listingID : listingId,
imageID: imageId,
isFeatured: false
},
timeout: 5000,
success: function(results) {
// result action
},
contentType: "application/x-www-form-urlencoded; charset=utf-8"
})
.fail(function (xhr, textStatus, errorThrown) {
// error handling
});
});
</script>
Hints:
If you use one, include the antiforgery token in the request as shown in the sample.
You can also send the payload as JSON. You then need to edit the content type and use JSON.stringify in the data section to convert the payload.

Related

.net core 2 upload multiple images appended to div

So I have created a website where you can upload images.
Here's the problem:
I'm using hidden field of input type=file
And I have a designed button which trigger this input.
Now, I have a div that displays those images..
Problem is that I get only the last selected images from the user at my controller.
What happens if the user wants to upload from different directories?
I couldn't find answer after searching, Also I can't show the code right now,
I'm using really simple code tho,
Input of file (multiple)
Button that triggers it
Div that shows the pictures (appended with jquery)
This is a simplified example:
Html:
<button id="inputMask">Upload Image</button>
<div id="ImageHolder">
</div>
<form id="holder" method="post" enctype="multipart/form-data">
<h1>Form</h1>
<input type="submit" />
</form>
JS:
$(document).ready(() => {
let inputs = [];
//Model binding name
let name = "file";
let loadImagesFromInputs = () => {
$("#ImageHolder").empty();
//Lets load those images
if (!FileReader) {
console.log("Cant load files in the navigator");
return;
}
//For each input
inputs.forEach(i => {
//for each file in each input
Array.from(i.files).forEach(f => {
//prepare the file reader
let fr = new FileReader();
//this will run when conversion is finished
fr.onload = function () {
$("#ImageHolder").append($("<img>", {
src: fr.result
}));
}
//convert file to url
fr.readAsDataURL(f);
});
});
}
$("#inputMask").click(() => {
//Create file input
let newInput = $("<input>", {
type: "file",
name: name,
id: name,
multiple: true,
accept: "image/x-png,image/gif,image/jpeg"
}).css({ width: "1", position: "absolute", opacity: "0" });
//update the list of images on change
newInput.change((e) => { loadImagesFromInputs() });
//Add input to list of inputs
inputs.push(newInput[0]);
//Add input to form
$("#holder").append(newInput);
//Click input
newInput.click();
});
$("#holder").submit((e) => {
e.preventDefault();
e.target.submit();
})
});
.Net Core 2 Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace MultipleFileDirectoriesDemo
{
[Route("[controller]")]
public class FileController : Controller
{
// GET: /<controller>/
[HttpGet]
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult Index(List<IFormFile> file)
{
//Do something with the list of files
return View();
}
}
}
The built in multi-file up-loaders on most browsers are pretty bad.
Consider looking at something like jQuery.Fileuploader
This will have the added benefit of being relatively consistent across devices and browsers.

Razor #Html.ActionLink not getting passed control value correctly

I am new to Razor. I am making good progress on this project but have hit a major road block with something that would seem to be easy. I have read a lot of posts about how to pass the value of a control as a parameter to a controller in order to redirect to a new view. The problem is that I either get the value passed to the controller but can't redirect OR I redirect and the parameter is not passed.
This is my latest attempt. I was hoping to pass the return of GetSelectedEmail to the controller (the value of "selectedEmail"). I can see that the Javascript is getting the correct value and the controller is being called, but the value is always NULL.
#Html.ActionLink("Get Scoring Report...", "History", "Student", null, new { onclick = "return GetSelectedEmail();" });
<select id="selectedEmail" name="align">
#foreach( var s in Model.Students )
{
<option id=#s.Email>#s.Email</option>
}
</select>
function GetSelectedEmail() {
var str = "new {email=" + $("#selectedEmail").val() + "}";
return str;
}
The controller...
public ActionResult History(string email, string sort)
{
string localEmail="";
if ( email == null || email == "" )
localEmail = AccountProfile.CurrentUser.UserName;
...
I have also tried to call the controller with Ajax like below. The controller does get the "selectedEmail" parameter but the page never redirects. I just does nothing. I tried both having the action link with the link parameters or not (show below).
#Html.ActionLink("Get Scoring Report...", "", "", null, new { onclick = "return GetSelectedEmail();" });
<select id="selectedEmail" name="align">
#foreach( var s in Model.Students )
{
<option id=#s.Email>#s.Email</option>
}
</select>
function GetSelectedEmail() {
$.ajax({
url: '/Student/History',
data: { email: $("#selectedEmail").val(), sort: "CaseID" },
type: 'POST',
dataType: 'json',
});
return true;
}
Any ideas?
Your first approach is not actually doing the redirection. ( also it calls a different method, which i am assuming a copy paste mistake)
Your current code is not passing the values because it is a link and when it is clicked, it is supposed to navigate to that url, which is exactly what it is doing.
I just changed the code to use unobtrusive javascript. Replaced the onclick with an id for the link
#Html.ActionLink("Get Scoring Report", "History", "Student", null, new { id="score" });
and when the click happens on this link, read the value of the select element and navigate to the second action method by setting the location.href property value
$(function () {
$("#score").click(function(e) {
e.preventDefault(); // Stop the normal redirection
var url = $(this).attr("href"); //Get the url to action method
url += "?email=" + $("#selectedEmail").val(); //append querystrings
window.location.href = url; // redirect to that url
});
});
For what I needed the solution was simple.
Show Student Scores
<select id="selectedEmail" name="align">
#foreach( var s in Model.Students )
{
<option id="#s.Email">#s.LastName,#s.FirstName</option>
}
</select>
And the Javascript magic...
function GetScoreHistory() {
var emailVal = $('#selectedEmail').find('option:selected').attr('id');
var url = '#Url.Action("History", "Student")';
url += "?email=" + escape(emailVal);
window.location.href = url;
}
The controller was called exactly how I needed it to be called.

Pop-up dialog using jQuery issue

Hi I have a problem with my page. I have one view page and 2 forms in the same page.
The problem is that I have a main form and another form which is a shown by JQuery. My Problem is when I open the dialog box, submit its form and return the view, the dialog box diappears. I don't know how to return a result which will still show the opened the dialog box.
I need your help on this please!
Below are the codes I used in my application.
.CSTHML Forms
#using (Html.BeginForm("Login2", "Account", FormMethod.Post, new { #id = "loginForm" }))
{
<a id="submitlink" href="#" class="button button-large">Sign In</a>
}
// This is the pop-up dialog box
#using (Html.BeginForm("TroubleSubmit", "Account", FormMethod.Post, new { #id = "troubleSubmitForm" }))
{
<a id="troubleSubmitLink" href="#" class="button">OK</a>
}
JQUERY
$('a#submitlink').click(function () {
$('#loginForm').submit();
});
$('a#troubleSubmitlink').click(function () {
$('#troubleSubmitForm').submit();
});
Below is the code of my controller action to handle the dialog form submit:
public ActionResult SignInTrouble(some parameter...)
{
// some operation and validation here
return View(); // ??? What view will I return? When I return "Login" it will reload the page and close the dialog box.
}
Again, how do I return the View that will still display the dialog box
You're submitting your form in a traditional (non-AJAX) manner so the page will reload. So you'll need to post in an AJAX way.
$("#submitlink").on("click", function () {
var loginForm = $("#loginForm");
$.ajax({
url: loginForm.attr("action"),
type: "post",
data: loginForm.serialize(),
})
.done(function(result) {
// do something with the returned response
});
});
The successful response is handled with .done() but what you return and how you handle the result is up to you. Simplest option is to return a partial view so it's just a html fragment to insert into an existing div container.
.done(function(result) {
$("#someDiv").html(result);
});
I often return JSON with the view rendered as an html string { status: 0, message: "Success", html: "<div>..." }. You could omit the html fragment from the JSON if you just need a simple YES/NO.
public ActionResult SignInTrouble(some parameter...)
{
// some operation and validation here
return Json({
status: 1,
message: "Validation Error"
});
}
Then you get a few more possibilities with your response
.done(function(result) {
var status = result.status;
var message = result.message;
if (status == 0) {
// OK
} else {
// ERROR
}
$("#messageDiv").text(message);
}
It simply due to the page is reloaded, you must use ajax form in this case, so it'll only process the action of ajax form and then return the result to the form without reload the page
#Ajax.BeginForm("TroubleSubmit","Account", new AjaxOptions(){ ...... })

Passing a List of Radio buttons objects to my Action Method

I am working on a security metrix page similar to the below:-
currently i have the following code inside my view:-
#foreach(var item in Model.PermisionLevel.OrderByDescending(a=>a.PermisionSize)){
<th>
</th>}
</tr>
#{
int i =0 ;
foreach (var item2 in Model.TechnologyType.OrderBy(a=>a.Name)) {
<tr>
<td class="f">
#item2.Name
</td>
#foreach (var item3 in Model.PermisionLevel.OrderByDescending(a=>a.PermisionSize))
{
<td class="f">
#Html.RadioButton(item2.Name, item3.PermisionLevelID)
#Html.Hidden("AssetTypeID" , item2.AssetTypeID)
#Html.Hidden("PermisionLevelID",item3.PermisionLevelID)
#Html.Hidden("SecurityRoleID",Model.SecurityRole.SecurityRoleID)
</td>
}
</tr>
}
}
and the following ActionMethod:-
[HttpPost]
public ActionResult AssignPermisionLevel(ICollection<SecurityroleTypePermision> list)
{
foreach (var c in list)
{
repository.InsertOrUpdateSecurityroleTypePermisions(c);
}
repository.Save();
return RedirectToAction("Index");
}
But i am not sure about how i can pass the associated hidden field values only if the related radio button was checked. currently if i submit the view , the action method will raise a null exception?
Can anyone advice on how to fix this ?
each radio group has a name, which looks like your item2.Name.
// see if this group is selected
var radWebSite = $('[name="SomeName"]:checked');
if (radWebSite.length > 0) {
// this group has a selection, get the value
var WebSiteVal = radWebSite.val();
}
if you get all of your answers into variables or some object, you can pass that down to your action method, here is a suggestion
$('#SaveClick').click(function() {
var SavePermissions = {};
SavePermissions.WebSite = WebSiteVal // this is the variable from above
// .... do this for each radio group
var DTO = { 'DTO': SavePermissions };
$.ajax({
type: "POST",
contentType: 'application/json;charset=utf-8',
url: "/ControllerName/ActionMethodName",
dataType: "json",
data: JSON.stringify(DTO),
success: function (data) {
// do success stuff
},
error: function(data){
// do error stuff
},
complete: function () {
//do final stuff
}
});
});
Then in your action method create a class that has a property for each item you put in SavePermissions on the javascript, probably should even call it SavePermissions
public ActionResult AssignPermisionLevel(SavePermissions DTO){
// save stuff
}
*Edit: *
I didn't think about this before, but we have a reference to Newtonsoft.Json in our application, you'll probably need that to do this...

Index ViewResult not re-rendering after callback from ajax call

I'm trying to callback into my ViewResult Index() controller action from an ajax call to update the page contents based on a dropdown select but my view is not re-updating (re-rendering).
I have set breakpoints and the index() action in the controller is being executed as invoked from the ajax 'get' and the model is being passed off to the view (breakpoints are being hit in the view as well).
View:
#* Contains code to build a webgrid and display data based on the model passed in... *#
#* Contains a couple of dropdowns for filtering *#
#*
Catch the select event from a dropdown and call back into the view to re-update page contents
for filter requests.
*#
<script type="text/javascript">
$("select").multiselect({
click: function (event, ui) {
$.ajax(
{ type: "GET",
url: '#Url.Action("Index","Data")',
data: { FilterRequest: (ui.checked ? 'checked' : 'unchecked') },
success: function () {
alert('hello again');
}
})
}
});
</script>
Controller:
// GET: /Data/
public ViewResult Index(string FilterRequest)
{
IList<DataModel> dataResult;
if (FilterRequest == null)
{ // Not a filter request so just update grid with full contents
dataResult = db.DataObjs.OrderByDescending(x => x.id).ToList();
}
else
{ // Filter request so update grid with filtered data
dataResult = db.DataObjs.Where(/*Build some filtered stuff here*/).OrderByDescending(x => x.id).ToList();
}
// Build some sub totals based on the resultset from above result set to display
// Other business logic number mashing here to display in other grids on the same view
return View(dataResult);
}
You're not doing anything with the response of the $.ajax call.
Something like this:
$.ajax(
{
type: 'GET',
url: '#Url.Action("Index","Data")',
data: { FilterRequest: (ui.checked ? 'checked' : 'unchecked') },
dataType: 'html',
success: function (html) {
$('#somecontainer').html(html);
}
});
Also, you can't return a full view (e.g a HTML page) from your action method - you need to either return a PartialView, or a JsonResult which you can iterate over and manualy bind the contents.
For a partial view, you need something like this:
return PartialView(dataResult);
It all depends on what your trying to re-render. If the HTML you require to re-render is complex, then use a partial view. If it's simply a bunch of data that is to be shoved into an input element (e.g a dropdown list), you should save on the HTTP payload over the wire and use JsonResult.

Resources