Pass Model from Partial View to the PageModel in Asp.Net Core Razor - asp.net-core

I am trying to pass a Model from a Partial view back to the PageModel, but the results are always empty. In my main .cshtml, I have an 'Export' button that when clicked opens a bootstrap modal. Users then click a checkbox to select data to download. Here is my code:
In my cs, I set the partial with this code:
// FileContents contains a list of FileObjects (which include Name as IsSelected)
[BindProperty]
public FileContents ExportData { get; set; }
// Get method to return the partial.
// ExportData is passed as the model for the partial
public PartialViewResult OnGetExportModel()
{
ExportData = new FileContents();
ExportData.Files.Add(new FileObject("filename.txt", true);
return Partial("_ExportDetails", ExportData);
}
// Handles postback of the FileContents data
public IActionResult OnPostExportData(FileContents data)
{
//The count is always zero
Console.WriteLine(data.Files.Count);
}
The partial is a table with the file name and a checkbox:
#model FileContents
<div class="form-group">
<table>
<tbody>
#foreach (var item in Model.Files)
{
<tr>
<td class="clsChkBox" data-item="#item.Name">
#Html.CheckBoxFor(modelItem => item.IsSelected)
</td>
<td>#item.Name</td>
</tr>
}
</tbody>
</table>
</div>
In the main page .cshtml, I display the partial:
<div class="dvExport" id="exportPartial"></div>
The partial is set with a class from a script:
function ScriptExport() {
$('.dvExport').load('/index/exportmodel);
}
I have tried several ways to pass the FileContents model of the partial, back to the .cs file.
One by using a <form method=post" asp-page-handler="ExportData" asp-route-data="#Model.ExportData"> . When returned, data.Files is empty.
Second by calling an ajax postback. When serializing #Model.ExportData, the files are also empty.
How can I return FileContents model in the partial back to my main page?

I did a test using ajax, you can refer to my code below:
_ExportDetails.cshtml:
#model FileContents
<div class="form-group">
<table>
<tbody>
#foreach (var item in Model.Files)
{
<tr>
<td class="clsChkBox" data-item="#item.Name">
#Html.CheckBoxFor(modelItem => item.IsSelected)
</td>
//Add a class as an identifier
<td class="Name">#item.Name</td>
</tr>
}
</tbody>
</table>
</div>
Index.cshtml:
#page
#model IndexModel
#{
ViewData["Title"] = "Home page";
}
<button type="button" onclick="ScriptExport()">ScriptExport</button>
<button type="button" onclick="ScriptSubmit()">Submit</button>
<div class="dvExport" id="exportPartial"></div>
//Required
<div>#Html.AntiForgeryToken()</div>
<script>
function ScriptExport() {
$('.dvExport').load('index?handler=ExportModel');
}
function ScriptSubmit(){
var data = [];
$("table > tbody > tr").each(function() {
var Files = {
Name: $(this).find('.Name').text(),
IsSelected: $(this).find("input[type='checkbox']").prop("checked")
}
data.push(Files);
});
$.ajax({
type: "post",
url: "/index?handler=ExportData",
data: JSON.stringify({ Files: data }),
//Required
headers:
{
"RequestVerificationToken": $('input:hidden[name="__RequestVerificationToken"]').val()
},
contentType: "application/json; charset=utf-8",
success: function(result)
{
alert("success");
},
error: function(error)
{
console.log(error);
}
});
}
</script>
Index.cshtml.cs:
public IActionResult OnPostExportData([FromBody]FileContents data)
{
Console.WriteLine(data.Files.Count);
return Page();
}
Test Result:

Related

How to return a partial view and invoke the model's constructor with DI system

I have a parent view, and want to call a handler with ajax that will return a partial view. The problem I'm having is that my partial view needs it's model also which has all its own OnGet, OnPost etc methods.
When calling:
public PartialViewResult OnGetPartialView(Guid Id)
{
return Partial("MyPartialView");
}
I don't know how to add the model for this view, as its only constructor takes several services that usually the DI systems takes care of for me. I also need to pass the Id to the partial view as its used in the OnGet method (which I'm assuming will be invoked when this works properly).
Thanks!
To pass the model to the partial view in razor pages, you need to add the second parameter as the model you need to pass when returning to the Partial:
return Partial("MyPartialView", model);
It should be noted that in MyPartialView, you need to delete the #Page in the first line of the page and add the model reference you passed.
This will ensure that MyPartialView receives the model data, otherwise there will be an error that model is null.
Regarding the OnGet and OnPost methods of the MyPartialView page you mentioned, if you delete #Page, they will lose their actual contact meaning.
My suggestion is that if you have some post or get methods that need to be used in MyPartialView, you can write these methods to other pages.
Here is a complete example:
TestModel.cshtml.cs:
public class TestModel : PageModel
{
public void OnGet()
{
}
public PartialViewResult OnGetPartialView(Guid Id)
{
List<Person> persons = new List<Person>()
{
new Person(){ Age = 12,
FirstName = "dd",
LastName = "aa" },
new Person(){ Age = 13,
FirstName = "bb",
LastName = "ff" },
new Person(){ Age = 14,
FirstName = "ggr",
LastName = "rwe" },
};
return Partial("MyPartialView", persons);
}
public IActionResult OnPostTest()
{
return Content("aa");
}
}
TestModel.cshtml:
#page
#model WebApplication_razorpage_new.Pages.TestModel
#{
ViewData["Title"] = "Test";
Layout = "~/Pages/Shared/_Layout.cshtml";
}
<h1>Test</h1>
<input id="Button1" type="button" value="Get partial view" /><br /><br /><br />
<div id="partial" class="border"></div>
#section Scripts{
<script>
$(function () {
$("#Button1").click(function () {
$.ajax({
type: "get",
url: "/Test?handler=PartialView",
data: { Id: "780cd7ce-91b2-40fd-b4c8-7efa6b7c84a5" },
success: function (data) {
$("#partial").html(data);
}
});
});
})
</script>
}
MyPartialView.cshtml:
#model List<Person>
<form method="post">
<input id="Button1" type="button" value="button" onclick="Click()" />
<input id="Text1" type="text" />
<table class="table table-bordered">
#foreach (var item in Model)
{
<tr>
<td>#item.Age</td>
<td>#item.FirstName</td>
<td>#item.LastName</td>
</tr>
}
</table>
</form>
<script>
function Click() {
$.ajax({
type: "POST",
url: "/Test?handler=test",
beforeSend: function (xhr) {
xhr.setRequestHeader("XSRF-TOKEN",
$('input:hidden[name="__RequestVerificationToken"]').val());
},
contentType: "application/json; charset=utf-8",
success: function (data) {
$("#Text1").val(data);
}
});
}
</script>
Here is the test result:

Triggering sweet alert message from the code behind for create method

Thanks to the excellent answer by Rena in the previous question I am also asking how do I achieve the same effect of the sweet alert message box on the create controller action I tried with the code they gave in this.
Using JavaScript code behind asp.net core c#
But because the action create submits the form first it doesn't seem to like the trigger the alert.
swal({
title: "MIS",
text: "Case Created your Case Number is ",
icon: "warning",
buttons: true,
dangerMode: true,
})
just post the form with ajax after that jsut show the message you want
<form action="/your action url" method="post" id="formCreate">
...Yıur item inputs
<button type="submit"></button>
</form>
$("#formCreate").submit(function (event) {
event.preventDefault(); //prevent default action
var post_url = $(this).attr("action"); //get form action url
var request_method = $(this).attr("method"); //get form GET/POST method
var form_data = $(this).serialize(); //Encode form elements for submission
$.ajax({
url: post_url,
type: request_method,
data: form_data
}).done(function (response) { //
Swal.fire({
position: 'center',
icon: "warning",
text: "Case Created your Case Number is "+response.Id,
title: "MIS",
showConfirmButton: false,
timer: 1500,
}).then(() => {
window.location.reload(true);
})
});
});
in cotroller side you should return something like this
[HttpPost]
public IActionResult Create(Item item){
//create item
return Json(new {Id=item.id});
}
It seems you want to trigger the sweet alert when you click the submit button.
Here is a working demo:
Model:
public class Test
{
public int Id { get; set; }
public string Name { get; set; }
}
Index.cshtml:
#model IEnumerable<Test>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
<a asp-action="Edit" asp-route-id="#item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="#item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="#item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Create.cshtml:
#model Test
<h1>Create</h1>
<h4>Test</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<input type="button" onclick="Create()" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
<script>
function Create() {
swal({
title: "MIS",
text: "Case Created your Case Number is " + $("#Name").val(),
icon: "warning",
buttons: true,
dangerMode: true,
}).then((willCreate) => {
if (willCreate) {
var data = $('form').serialize();
$.ajax({
url: "/tests/create",
type: "POST",
data: data,
dataType: "html",
success: function () {
swal("Done!", "It was succesfully created!", "success")
.then((success) => {
window.location.href="/tests/index"
});
},
error: function (xhr, ajaxOptions, thrownError) {
swal("Error creating!", "Please try again", "error");
}
});
}
});
}
</script>
}
Controller:
public class TestsController : Controller
{
private readonly Mvc3_1Context _context;
public TestsController(Mvc3_1Context context)
{
_context = context;
}
// GET: Tests
public async Task<IActionResult> Index()
{
return View(await _context.Test.ToListAsync());
}
// GET: Tests/Create
public IActionResult Create()
{
return View();
}
// POST: Tests/Create
[HttpPost]
public async Task<IActionResult> Create(Test test)
{
if (ModelState.IsValid)
{
_context.Add(test);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(test);
}
}
Result:

How do i send the data to edit boxes on the same page?

i have the following page generated
when i click the Edit link, the record data must be sent to the input boxes on teh same page (without refreshing the page)
currently i have the controller code and views
controller: ProductCategory
public class ProductCategoryController : Controller
{
//
// GET: /ProductCategory/
TUDBEntities _db = new TUDBEntities();
public ActionResult Index(string Code)
{
var categories = _db.mt_ProductCategories
.Where(pc => Code == "" || Code == null|| pc.CatCode == Code)
.Select(
c => new ProductCategory {
Id = c.Id,
CategoryCode = c.CatCode,
Name = c.CatName,
Url = c.Url
});
if (Request.IsAjaxRequest())
{
return PartialView("_ProductCategoryList", categories);
}
return View(categories);
}
[HttpPost]
public ActionResult Save(ProductCategory userData)
{
try
{
if (ModelState.IsValid)
{
mt_ProductCategories cat = new mt_ProductCategories { CatCode = userData.CategoryCode, CatName = userData.Name };
// TODO: Add insert logic here
_db.mt_ProductCategories.Add(cat);
_db.SaveChanges();
return RedirectToAction("Index");
}
return View();
}
catch
{
return View();
}
}
public ActionResult Edit(int id)
{
var category = _db.mt_ProductCategories
.Where(pc => pc.Id == id)
.Select(pc => new ProductCategory
{ Id=pc.Id, CategoryCode=pc.CatCode,Name=pc.CatName }).ToList();
return RedirectToAction("Index", category);
}
}
Index view
#model IEnumerable<eComm1.Models.ProductCategory>
#using(Ajax.BeginForm("Save", "ProductCategory",
new AjaxOptions {
HttpMethod="POST",
UpdateTargetId="prod-grid",
InsertionMode=InsertionMode.Replace,
OnSuccess="loaddivdata"
}))
{
<fieldset class="form-group">
<label for="Code">Category Code</label>
<input type="text" class="form-control focus" id="Code" name="CategoryCode" placeholder="Product category code" >
</fieldset>
<fieldset class="form-group">
<label for="ProdName">Product Name</label>
<input type="text" class="form-control" id="ProdName" name="Name" placeholder="Product Name">
</fieldset>
<button type="Submit" class="btn btn-primary">Save</button>
}
<hr />
<div id="prod-grid">
#Html.Partial("_ProductCategoryList", #Model)
</div>
<script type="text/javascript">
$(function () {
$('.focus :input').focus();
});
function loaddivdata() {
$('#prod-grid').html();
$('#Code, #ProdName').val('');
};
// $('#prod-grid').load(function () {
// $.ajax({
// url:'ProductCategoryController/Index',
// method:'GET',
// type:'application/html',
// success: function () { alert('called');}
// });
// });
//});
</script>
Partial View: _ProductCategoryList
#model IEnumerable<eComm1.Models.ProductCategory>
<div class="panel panel-default">
#if (Model.Count() == 0) { <div class="panel-heading">Product Categories - <span style='color:red;font-weight:bold' >0 RESULTS FOUND</span></div>
}else{
<!-- Default panel contents -->
<div class="panel-heading">Product Categories</div>
}
<!-- Table -->
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.CategoryCode)
</th>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Url)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.CategoryCode)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Url)
</td>
<td>
#*#Html.beActionLink("Edit", "Edit", new { id=item.Id }) |
#Html.ActionLink("Details", "Details", new { id=item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id=item.Id })*#
#Ajax.ActionLink("Edit", "Edit", "ProductCategory", new { id=item.Id}, new AjaxOptions { HttpMethod = "GET", UpdateTargetId = "", OnSuccess = "loadformdata" }) |
#Ajax.ActionLink("Delete", "Delete", "ProductCategory", new { id=item.Id}, new AjaxOptions{ HttpMethod="POST", UpdateTargetId="", OnSuccess="loadformdata"})
</td>
</tr>
}
</table>
</div>
How do i modify my code to send data those input control and in my code how do i create hidden field for Id value so it can be send to the Edit(collection, int id) action to update the record?
for Stephen Muecke, i have added my jquery files through the bundles
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/ecomm").Include(
"~/Scripts/jquery-{version}.js",
"~/Scripts/jquery-2.1.4.min.js",
"~/Scripts/bootstrap.js",
"~/Scripts/bootstrap.min.js",
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"
));
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
"~/Scripts/jquery-ui-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"));
// Use the development version of Modernizr to develop with and learn from. Then, when you're
// ready for production, use the build tool at http://modernizr.com to pick only the tests you need.
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
"~/Scripts/modernizr-*"));
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/bootstrap.min.css",
"~/Content/bootstrap.css",
"~/Content/style.css"));
bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(
"~/Content/themes/base/jquery.ui.core.css",
"~/Content/themes/base/jquery.ui.resizable.css",
"~/Content/themes/base/jquery.ui.selectable.css",
"~/Content/themes/base/jquery.ui.accordion.css",
"~/Content/themes/base/jquery.ui.autocomplete.css",
"~/Content/themes/base/jquery.ui.button.css",
"~/Content/themes/base/jquery.ui.dialog.css",
"~/Content/themes/base/jquery.ui.slider.css",
"~/Content/themes/base/jquery.ui.tabs.css",
"~/Content/themes/base/jquery.ui.datepicker.css",
"~/Content/themes/base/jquery.ui.progressbar.css",
"~/Content/themes/base/jquery.ui.theme.css"));
}
In the partial view
#Ajax.ActionLink("Edit", "Edit", "ProductCategory", new { id = item.Id }, new AjaxOptions { HttpMethod = "GET", OnSuccess = "loadformdata" }) |
#Ajax.ActionLink("Delete", "Delete", "ProductCategory", new { id=item.Id}, new AjaxOptions{ HttpMethod="POST", OnSuccess="loadformdata"})
in the index view the following js function:
function loadformdata() {
var cells = $(this).closest('tr').children('td');
alert(cells.eq(0).text());
//$('#Code').val(cells.eq(0).text());
//$('#ProdName').val(cells.eq(1).text());
}
To: Stephen Muecke:
i have removed above loadformdata() and put everything as you said. this youtube video shows the problem that still does not call that click event
To: Steven Meucke:
there's still no luck, for ease i have added a alert() in the function and the alert() won't show. Here is the video
Give you 'Edit' link a class name (say) class="edit" and handle its .click() event to update the form controls
$('.edit').click(function() {
var cells = $(this).closest('tr').children('td');
$('#Code').val(cells.eq(0).text());
$('#ProdName').val(cells.eq(1).text());
return false; // cancel the default redirect
});
Side note: You could just replace the ActionLink() code with Edit and the return false; line is not necessary.
write script for ajax call:
$('#edit').click(function () {
// var data = {here you will pass item.id }
$.ajax({
url:'ProductCategoryController/Edit',
data: {id : data}
method:'GET',
success: function (data) {
//clear html page here
//reload html page by passing 'data' passes in function
//e.g. suppose html page id is 'xyz' then $("#xyz").html(data)
}
});
});

Get Id from Dynamically generated Images in asp.net mvc4

I have some Dynamically Generated images on view using asp.net mvc4...and I have to delete selected
images from from view...but i don't know how to pass id from view to controller
controller code:
public ActionResult imagelist(ShirtDb dg)
{
List<ShirtDb> all = new List<ShirtDb>();
using (patternChangeEntities8 et = new patternChangeEntities8())
{
all = et.ShirtDbs.ToList();
}
return View(all);
}
View Code:
#model List<patternchange.Models.ShirtDb>
#using (Html.BeginForm("Edit", "Patterchange", FormMethod.Post))
{
<table>
#{
int j=0;
for (int i = 0; i < Model.Count(); i += 4) {
j = i;
<tr>
#while(j<i+4&&j<Model.Count())
{
<td>
<img src="data:image/png;base64,#Convert.ToBase64String(Model[j].Image, 0, Model[j].Image.Length)" width="100" onclick="location.href='#Url.Action("Edit", "Patterchange")'" #(Model[j].SId) />
#Html.TextBoxFor(m => Model[j].SId)
<input type="submit" value="delete" />
</td>
j++;
}
<td>
</td>
</tr>
}
}
</table>
}
You can try with below code.in that I have created one JavaScript function which calls when button clicks and I have pass ID in that function at view time.
You can call your controller action via Ajax call
View Code
#model List<patternchange.Models.ShirtDb>
#using (Html.BeginForm("Edit", "Patterchange", FormMethod.Post))
{
<table>
#{
int j=0;
for (int i = 0; i < Model.Count(); i += 4) {
j = i;
<tr>
#while(j<i+4&&j<Model.Count())
{
<td>
<img src="data:image/png;base64,#Convert.ToBase64String(Model[j].Image, 0, Model[j].Image.Length)" width="100" onclick="location.href='#Url.Action("Edit", "Patterchange")'" #(Model[j].SId) />
#Html.TextBoxFor(m => Model[j].SId)
<input type="button" onclick="DeleteImage(#(Model[j].SId))" value="delete" />
</td>
j++;
}
<td>
</td>
</tr>
}
}
</table>
}
Javascript
<script type="text/javascript">
function DeleteImage(ID) {
$.ajax({
type: "POST",
url: '#Url.Action("Edit", "Patterchange")',
contentType: "application/json; charset=utf-8",
data: "{ id: '"+ID+"' }",
dataType: "json",
success: function () { alert('Success'); },
error: function () { alert('error');}
});
}
</script>
Controller code
[HttpPost]
public ActionResult Edit(string id)
{
// Write your code for delete record by using id
return View();
}

Two Partial Views, posting data to actions causing issue

I am using MVC 4 for a project. one of my view has 2 partial views in it as:
<div id="fPassword">
#Html.Partial("_ForgotPassword",new UserForgotPasswordModel())
</div>
<div id="aLink">
#Html.Partial("_ActivationLink", new UserActivationLinkModel())
</div>
The Partial Views are as:
#model Me2Everyone.Web.UI.Models.User.UserForgotPasswordModel
#using (Html.BeginForm("ForgotPassword", "Home", FormMethod.Post, new { id = "personal-form" }))
{
<table width="100%" cellspacing="0" cellpadding="0" border="0" style="padding: 0px 0 20px 0;">
<tbody>
<tr>
<td width="375">
<div class="text-problem">
#Html.EditorFor(model => model.Email)
</div>
</td>
<td width="80" valign="bottom">
<input type="submit" value="Send" />
</td>
</tr>
</tbody>
</table>
}
and the second one is almost same to above the only difference is model type and action method of Form.
In Home Controller the ForgotPassword action is as:
[HttpPost]
public ActionResult ForgotPassword(UserForgotPasswordModel model)
{
if (ModelState.IsValid)
{
// Search email in database.
if(emailNotFound)
{
model.ErrorMessage = "Email not found.";
}
}
else
{
model.ErrorMessage = "Email address not found.";
}
return PartialView("_ForgotPassword", model);
}
Now when I was posting data to server, it was returning the partial view as independent not in the main View, so I looked around on net and found that I need to send ajax call for it, so I did it as in the parent view as:
<script type="text/javascript">
$(function () {
$("form").submit(function () {
if ($(this).valid()) {
var dataToSend = { model: { Email: $("#Email").val() } };
var serializedForm = $(this).serialize();
var isForgotPassword = true;
if ($(this).attr('action').indexOf("ForgotPassword") < 0)
isForgotPassword = false;
$.ajax({
url: $(this).attr('action'),
data: serializedForm,
type: 'POST',
success: function (data, textStatus, request) {
if (isForgotPassword == true)
$("#fPassword").html(data);
else
$("#aLink").html(data);
},
error: function (xhr, ajaxOptions, thrownError)
{
alert('error')
}
});
return false;
}
});
});
and also in my parent view I have:
#if(Model.ErrorMessage != "")
{
<script type="text/javascript">
alert(#Model.ErrorMessage);
</script>
}
The problem is when I give a valid email address, it works fine but when I provide an email address that doesnot exist in database, I get the alert that Email not found but when I click again on the submit button, the partial view is created independently in browser instead of being in parent view.
I tried by changing my parent view as:
<div id="fPassword">
#{Html.RenderAction("ForgotPassword");}
</div>
<div id="aLink">
#{Html.RenderAction("ActivationLink");}
</div>
but still its not working, any ideas or help on it?
Replace:
$("form").submit(function () {
with:
$(document).on('submit', 'form', function () {
This will ensure that your submit handler is registered in a lively manner. This means that after you refresh your DOM with contents coming from the AJAX call, the submit handler that you registered will continue to be associated to this new form. You could read more about the .on() method in the documentation. The .on() replaces the deprecated (and even removed in jQuery 1.9) .live() method which allowed you to achieve the same task. After the .live() method was deprecated, they introduced the .delegate() method with the same semantics. So depending on the jQuery version you are using, you should pick the right method.