How to send non-model data using a submit button (Razorpages) - c#

I need to post a form but I also need to include the Id of the widget I want to remove from the model collection. Can I pass extra data with a submit, that is not part of the model?
I have generated html using a foreach loop. This displays the ID of a widget. The model for this page is a collection of widgets.
So the code used to display all widget Id's is as follows:
#foreach (var widget in widgets){
#i++; // Assume declared above or could use simple for loop ...
Widget #widget.Id
<input type="hidden" asp-for="Widgets[i].Id" /> // For binding the collection on POST
<br />
}
This displays:
Widget 1
Widget 2
Widget 3
...etc
I would like the user to have the option to remove Widget 2 from the list.
So the display will be something like this:
Widget 1 [x]
Widget 2 [x]
Widget 3 [x]
...etc
I would like to post the model back to the server and pass the Id of the widget... how do I do this?
If I use an input like below, how can I add the Id to it?
#foreach (var widget in widgets){
#i++; // Assume declared above or could use simple for loop ...
Widget #widget.Id
<input type="hidden" asp-for="Widgets[i].Id" /> // For binding the collection on POST
<input type="submit" asp-page-handler="RemoveWidget" name="x" />
<br />
}
I've thought about creating a global hidden input field and setting this field to the Id using javascript before the submit is sent, but I assume there is a better way than this?

In order to achieve your desired functionality, I am using ActionLink with a parameter:
#foreach (var widget in widgets){
Widget #widget.Id #Html.ActionLink("Delete", "Home", new { id = #widget.Id})<br />
}
And in your Controller:
public ActionResult Delete(int id)
{
//Get your widget ID here and do the deletion as required.
return View("Your View");
}
You can style your ActionLink as required like this:
#Html.ActionLink("Delete", "Home", new { id = #widget.Id},new { #style="your style goes here" });
EDIT:
You can use AJAX if you want to POST your data to your controller. Specifically in your case, I will show you an example:
#foreach (var widget in widgets){
Widget #widget.Id : <br />
}
In you AJAX:
function confirmDelete(event) {
var recordToDelete = $(event).attr("data-id"); //Get your current widget id here
if (confirm("Are you sure you want to delete this widget") == true) {
//Prepare our data
var json = {
id: recordToDelete
};
$.ajax({
url: '#Url.Action("Delete", "Home")',
type: "POST",
dataType: "json",
data: { "json": JSON.stringify(json) },
success: function (data) {
if(data == "success") {
alert("Successfully deleted selected widget");
location.reload();
}
},
error: function (data) {
alert("Could not delete selected widget. Please try again!");
},
});
}
};
And finally in your Controller:
//Delete a widget based on the ID that you get from your View
[HttpPost]
public JsonResult Delete(string json)
{
var serializer = new JavaScriptSerializer();
try
{
dynamic jsondata = serializer.Deserialize(json, typeof(object));
string id = jsondata["id"];
if(id != "")
{
int getid = Convert.ToInt32(id);
//Call your db or your logic to delete the file
DatabaseAccess data = new DatabaseAccess();
string result = data.DeleteFile(getid);
if(result.Equals("Success"))
{
return Json("success", JsonRequestBehavior.AllowGet);
}
else
{
return Json("fail", JsonRequestBehavior.AllowGet);
}
}
else
{
return Json("notfound", JsonRequestBehavior.AllowGet);
}
}
catch
{
return Json("dberror", JsonRequestBehavior.AllowGet);
}
}

Related

HTML inside foreach statement not rendering even though data is being passed back to view

I went through the debug and found that the values are being passed but for some reason the property values are not being added to page. I can't figure out why. On submit, data chosen from the form is sent to DisplayProviders.
DisplayProviders then compares the string values to the property names in the list of provider agencies then stores the data found inside TempData. A RedirectToAction occurs and sends the TempData to Index().
The View, after the second request of Index(), then recieves the list of providers. However, even though ViewBag is not null (I went though it in debug) it doesn't add the agency names when I go through it with foreach. Using debug, I found that the names ARE being passed but the HTML is not being added to the page.
Please help!
In Controller:
public ActionResult Index()
{
ViewBag.ListOfProviders = TempData["ProvidersFound"];
return View();
}
[HttpGet]
public ActionResult DisplayProviders(string[] area, string[] mode, string[] eligibility)
{
var allProviders = repo.GetProviders();
//Code to narrow down list of providers using the options passed
//through ajax (area, mode, and eligibility) this part works and
//provider agencies were found and stored in TempData
TempData["ProvidersFound"] = providersFound;
return RedirectToAction("Index");
}
Code in View:
#model TransportationProvidersDemo.Models.Provider
#{
var providersFound = ViewBag.ListOfProviders;
}
<--HTML form here (omitted)-->
<input type="submit" />
<div>
#if (providersFound != null)
{
foreach (var item in providersFound)
{
<div>
<h3>#item.AgencyName</h3>
</div>
}
}
</div>
And I'm using ajax to send data to DisplayProviders
$.ajax({
type: "GET",
url: "/Home/DisplayProviders",
contentType: "application/json; charset=utf-8",
data: { area: areaSelected, mode: modeSelected, eligibility: eligibilitySelected },
datatype: "json",
traditional: true,
success: function (response) {
if (response != null) {
alert("SUCCESS! Good job!");
} else {
alert("Something went wrong");
}
},
failure: function (response) {
alert("Failure");
},
error: function (response) {
alert("Error");
}
});
can you try passing your found providers list from Tempdata as model in the view object ? like below:
Controller:
public ActionResult Index()
{
var ListOfProviders = TempData["ProvidersFound"] as (your strongly type list);
return View(ListOfProviders);
}
View:
#model List<TransportationProvidersDemo.Models.Provider>
#if (Model != null)
{
foreach (var item in Model)
{
<div>
<h3>#item.AgencyName</h3>
</div>
}
}
</div>
The problem is that RedirectToAction is a client side redirect that stores data in session state. You won't get the correct data from the ajax response. You should just return the view directly from DisplayProviders instead.
If you want to use the ViewBag you must be aware that the elements you read from it are of type object so you need to cast them.
View :
#model List<TransportationProvidersDemo.Models.Provider>
#{
var providersFound = ViewBag.ListOfProviders != null ?
ViewBag.ListOfProviders as List<Providers> :
null;
}
<ommited form here>
#if (providersFound!= null)
{
foreach (var item in Model)
{
<div>
<h3>#item.AgencyName</h3>
</div>
}
}
</div>

After getting the data from controller to view, how to send back the data to the view

Controller:
{
private static string selection = String.Empty;
dynamic mymodel = new ExpandoObject();
public ActionResult Post(string Name)
{
selection = Name;
return RedirectToAction("Index");
}
public ActionResult Index()
{
SegmentRepository segment = new SegmentRepository();
mymodel.listofSegments = segment.GetSegmentation();
DynamicRepository dynamic = new DynamicRepository();
mymodel.listofDynamic = dynamic.GetDynamicContent(selection); //After selecting the segmentation in the view it returns the required dynamic content in mymodel.listofDynamic but does not display it in the view.
return View(mymodel);
}
}
After selecting the segmentation in the view, it returns the required dynamic content in mymodel.listofDynamic but does not display it in the view.
View:
<script>
function seg() {
var employment = document.getElementById("Employment").value;
$.ajax({
type: "POST", //HTTP POST Method
url: '#Url.Action("Post","Home")', // Controller/View
data: {
//Passing data
Name: employment //Reading text box values using Jquery
}
})
}
</script>
<tr>
<td height="100">
<label>220</label>
</td>
<td>
<select id="Employment">
<option>---Select---</option>
#foreach (var item in Model.listofSegments)
{
<option name="selectedSegment" value="#item">#item</option>
}
</select>
<input type="submit" value="Send" name="submit" onclick="seg()">
</td>
<td>
<select name="Dynamic">
<option>---Select---</option>
#foreach (var item in Model.listofDynamic)
{
<option name="selectedDynamic" value="#item">#item</option>
}// I need the data to get listed here
</select>
<input type="submit" value="Send" name="Submit1">
</td>
</tr>
I would need the public ActionResult Index() method to run again so the data in listofDynamic gets printed in the view. How can I achieve this?
If you are wanting to get values by Ajax you forget success and error part.
View part:
$.ajax({
type: "POST", //HTTP POST Method
url: '#Url.Action("Post","Home")', // Controller/View
data: {
//Passing data
Name: employment //Reading text box values using Jquery
},
success:function(result){alert(result);},
error:function(error){alert(error.responseText);}
})
controller side like AT-2017 wrote JsonResult or you can return partial view/any another type.
In the data section of the Ajax, use the following:
data: { "name": employment }, //Pass value of employment variable as the name parameter
success: function(data) {
alert(data); //In the success function, pass the data to be notified in the view
}
The above will return data from controller to view using Ajax.
In the same time, make a Json method to make it work or an ActionResult will also do:
public JsonResult GetResults(string name) //Here is the name parameter
{
MainEntities db = new MainEntities(); //Db context
var con = (from c in db.TableName
where c.ColumnName == name //Pass the name parameter in the query
select c).ToList();
return Json(con); //Finally returns the result in Json
}

How to return a dynamic number of multiple partial views inside another partial view

I want to return a dynamic number of multiple partial views inside another partial view in the controller, and inject it to the DOM using an Ajax call.
The user is going to select a package (radio buttons) and depending on this package I need to return X number of forms to be filled by the user.
This is my Ajax code:
$(function() {
var serviceURL = "/NewOrderRequestForms/GetFormsToCreateNewOrderRequest";
$(":radio").change(function() {
$.ajax({
url: serviceURL,
type: "POST",
data: { account: $("#AccountId").val(), serviceAvailabilityPackageId: $(":radio:checked").val() },
success: function(xhrData) {
populateNORForms(xhrData);
},
error: function() {
alert("error");
}
});
});
});
My controller method looks like the following:
public virtual ActionResult GetFormsToCreateNewOrderRequest(Guid account, int serviceAvailabilityPackageId)
{
var customerNumber = _authorizationUtil.GetAccount(account).CustomerNumber;
var result = _customFormService.GetFormsToCreateNewOrderRequest(customerNumber.Value,
serviceAvailabilityPackageId).Select(x => x.FormKey);
var forms = CustomFormUtil.GetCustomMetaPartial(result);
//I am confused here
//return PartialView(something)
}
CustomFormUtil.GetCustomMetaPartial(result) is going to return an IEnumerable<string> of 1 to 6 strings for example "form1, form3" or "form1, form2, form3, form6" etc. I am using this to return a Dictionary of View names and Models to add them in my ultimate partial view.
//Dictionary for Custom Meta Partialviews
public static Dictionary<string, CustomMetaPartialViewModel> CustomMetaPartial2 = new Dictionary<string, CustomMetaPartialViewModel>()
{
{"form1_v1", new CustomMetaPartialViewModel(MVC.Service.NewOrderRequestForms.Views.form1_v1, new form1_v1())},
{"form2_v1", new CustomMetaPartialViewModel(MVC.Service.NewOrderRequestForms.Views._form2_v1,new form2_v1())},
...
{"form7_v1", new CustomMetaPartialViewModel(MVC.Service.NewOrderRequestForms.Views._form7_v1,new form7_v1())}
};
My question is: how can I add these partial Views with Models into another big partial view and send this one back to the View?
Ok, I have my answer for this, instead of using Ajax to call the List of string I am using to create a PartialView that renders everything I need.
This is my AJAX call
$(function () {
var serviceURL = "/NewOrderRequestForms/GetFormsToCreateNewOrderRequest";
$(":radio").change(function () {
$.ajax({
url: serviceURL,
type: "POST",
data: { account: $("#AccountId").val(), serviceAvailabilityPackageId: $(":radio:checked").val() },
success: function (xhrData) {
$('#NORForms').html(xhrData);
},
error: function () {
alert("error");
}
});
});
});
And the function in the controller is the following:
public virtual ActionResult GetFormsToCreateNewOrderRequest(Guid account, int serviceAvailabilityPackageId)
{
var customerNumber = _authorizationUtil.GetAccount(account).CustomerNumber;
var result = _customFormService.GetFormsToCreateNewOrderRequest(customerNumber.Value,
serviceAvailabilityPackageId).Select(x => x.FormKey);
return PartialView(Views.GenericParentView, result);
}
I am getting all the information I need from the View, however, instead of returning a List of Strings to the callback in Ajax, I am returning a PartialView having the list of Strings that I needed as the parameter that I need to render the partials Views. Here is the new Partial View that handles this.
#using UI.Shared.Utils
#model IEnumerable<string>
#{
var forms = CustomFormUtil.GetCustomMetaPartial2(Model);
var result = forms.Select(x => x.Model);
}
#using (Html.BeginForm(MVC.Service.NewOrderRequest.Index(new TdlMasterModel()), FormMethod.Post))
{
foreach (var form in forms)
{
{ Html.RenderPartial(form.View, form.Model); }
}
<p style="clear: both">
<input id="submitNOR" type="submit" value="Submit" style="float: right" />
</p>
}
I am sending a list of Strings, and using it to spit out a Dictionary with Model,Views and I am rendering them in the partial view, using a foreach loop.
Saving me at the end some extra Ajax calls, in resume I was tackling my problem the wrong way.

How to replace div's content by partial view or update it's content depending on what json result returns on ajax complete?

Well I have simple ajax form:
This is MyPartialView
#using(Ajax.BeginForm("action", "controller", new AjaxOptions
{
OnBegin = "beginRequest",
OnComplete = "completeRequest",
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "div-to-replace"
}, }))
{
<input type="text" id="my-input" />
...
}
This is parent view:
<div id="div-to-replace">
#Html.RenderPartial("MyPartialView")
</div>
In my controller I have:
[HttpPost]
public ActionResult action(Model model)
{
if (ModelState.IsValid)
{
// do staff with model
// return partial view
return PartialView("MyPartialView");
}
// else add error and return json result
return Json(new {error = "invalid data"});
}
And my javascript on ajax complete method:
function completeRequest(data) {
var result = $.parseJSON(data.responseText);
if (result != 'undefined' && result != null && result.error) {
// just display error and not replace all content
// attachModelError is my custom method, it just adds vlaidation-error class to inputs, etc.
attachModelError("my-input", result.error);
return;
}
// or show returned html (depending on returned model form inputs will be modified:
// select box with different items in my case
$('#div-to-replace').html(data.responseText);
}
But the problem is I have empty #div-to-replace if model state is invalid. If model state is ok every thing works fine. If I use different insertion mode it creates duplicates of div's content before or after div.
Summary:
I want different InsertionMode behavior depending on json result. I don't need replace data if (result != 'undefined' && result != null && result.error).
I had to solve this problem once so very long ago. I came up with a simple solution, which today, may not be the best solution but it gets the job done.
My solution involved setting up a controller action that would render just the partial with data that it would need and have my JavaScript request it.
C#
MyController: Controller
{
public ActionResult GetPartialViewAction()
{
return PartialView("mypartialview", new partialViewModel());
}
}
JavaScript
$.ajax({
url: "/my/getpartialaction/"
}).done(function(data) {
$("#partialViewDiv").html(data);
});
HTML
<div id="partialViewDiv"></div>
A better solution would be to use a MVVM/MVC JavaScript library that would allow you to leverage html templates and only have to transmit the data over your ajax solution. I recommend looking into knockout.js or backbone.js for this more accepted pattern.
I have the same problem with the default c# ajax forms. I have a solution what might work.
jQuery:
$(function () {
var ajaxFormSubmit = function () {
var $form = $(this);
var options = {
url: $form.attr("action"),
type: $form.attr("method"),
data: $form.serialize(),
cache: false
}
$.ajax(options).done(function (data) {
data.replaces.each(function (replace) {
$(replace.id).replaceWith(replace.html);
});
});
return false;
};
$("form[data-ajax='true']").submit(ajaxFormSubmit);});
form.cshtml
#using (Html.BeginForm("Create", "Menu", FormMethod.Post, new { data_ajax = "true" }))
{}
model sample
public string Id {get;set;}
public string Html {get;set;}
The last thing you need to do in your controller is return a json result with a list of your model sample, id is target element to update, for the html you must use a render partial / or view as string.
For render view to partial see [question]: https://stackoverflow.com/questions/434453

How to delete multiple items in MVC with a single link?

Consider following:
Partial View:
<%= Html.ListBox("BlackList", Model.Select(
x => new SelectListItem
{
Text = x.Word,
Value = x.ID.ToString(),
Selected = Model.Any(y=> y.ID == x.ID)
}))%>
Main View:
<td><% Html.RenderPartial("GetBlackList", ViewData["BlackList"]); %></td>
Controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult DeleteWord(int[] wordIdsToDelete)
{
if (!ModelState.IsValid)
return View();
try
{
_wordManager.DeleteWord(wordIdsToDelete);
return RedirectToAction("WordList");
}
catch
{
return View();
}
}
Model (WordManager)
public void DeleteWord(int[] idsToDelete)
{
var dataContext = GetLinqContext();
var currentList = GetTabooWordList();
foreach (var id in idsToDelete)
{
foreach (var item in currentList)
{
if (item.ID == id)
{
dataContext.BadWords.DeleteOnSubmit(item);
}
}
}
dataContext.SubmitChanges();
}
The question is how correctly pass the parameter - idsForDel ?
I.E I have to pass a client data to the server?
<%= Html.ActionLink("Delete Selected", "DeleteWord", "AdminWord", new { wordIds = idsForDel })%>
I think this can be made by jQuery. Any ideas?
You can bind to an array using the Model Binding to a List
(Haacked.com Model binding to a list, here you can see how to bind complex types as well).
Although I'm not happy with code where I create elements
so I can serialize it in order to bind it to the controllers action input parameter, this code works just what you want:
<script type="text/javascript">
function DeleteWords() {
var el = $("<form></form>");
//for every selected item create new input element and insert it
//inside of temporarty created form element
var selItems = $('#BlackList option:selected').each(function(intIndex) {
//is important how you name input elements (as you can read in link I referenced)
el.append($('<input/>')
.attr({ type: "hidden", name: "wordIdsToDelete", value: $(this).val() })
);
});
//make an ajax call with serialized temporary form
$.ajax({
type: "POST",
url: "/YourController/DeleteWord",
data: $(el).serialize(),
// error: HandleUnespectedError,
success: function(response) {
//handle response }
});}
Hope this helps...
How about this code?
<%= Html.ActionLink("Delete Selected", "DeleteWord",
"AdminWord", new { id="send" })%>
<script type="text/javascript">
$(function() {
$("#send").click(function() {
$.post(
$("form:first").attr("action"),
$("#GetBlackList").serialize()
);
});
});
</script>
And, If two or more records are deleted, DeleteAllOnSubmit is good.
dataContext.BadWords.DeleteAllOnSubmit(
currentList.Where(item=>currentList.Containts(item.id)).ToList()
);

Categories