In my razor page in form I have button that on submit calls method "OnPostEditMe" with parameter "int id":
#{
var idbook = #Html.DisplayFor(a => a.EditBook.Id);
//idbook on page load gets id number of object EditBook when page loads, this works, if I add this value anywhere in razor page it displays correctly
}
<form method="post">
<div class="form-group row">
<div class="col-3 offset-3">
<button asp-page-handler="EditMe" asp-route-id="#idbook" type="submit">
Update #idbook
</button>
</div>
</div>
</form>
But when I click on this button, It calls method OnPostEditMe, but with parameter "0" instead value that was load with "idbook":
public async Task<IActionResult> OnPostEditMe(int id)
{
var tmp = id; //id is always 0
...
}
What is wrong, Why does asp-route-id="#idbook" not passing parameter that was load on page?
You have to set on the form instead of the button
<form method="post" asp-route-id="#idbook">
Or you can use hidden input
<input value="#idbook" name="id" id="id" type="hidden"/>.
Related
When using method handlers to execute OnGet or OnPost methods, &handler=[action] query string gets added.
Problem is if user manually refreshes the page afterwards by hitting browser's refresh button, the same action will get executed for the 2nd time unintentionally.
What is the recommended approach to avoid this?
Problem is if user manually refreshes the page afterwards, same action
will get executed for the 2nd time.
For the browser refresh button click event, we can't prevent it. But, as a workaround, you could defined a TriggerCount property in the page model, and use a hidden field to store the value in the form, then in the handler method, get the hidden field value and based on the count to do something. Code as below:
code in the .cshtml.cs page:
public void OnPostDelete()
{
if (Request.Form["TriggerCount"].Count > 0)
{
TriggerCount = Convert.ToInt32(Request.Form["TriggerCount"]);
TriggerCount++;
}
if (TriggerCount < 2)
{
// do something.
Message = "Delete handler fired, Count:" + TriggerCount;
}
else
{
Message = "Over 2 times";
}
}
Code in the .cshtml page:
#page
#model RazorPageSample.Pages.HandlerPageModel
#{
}
<div class="row">
<div class="col-lg-1">
<form asp-page-handler="edit" method="post">
<button class="btn btn-default">Edit</button>
</form>
</div>
<div class="col-lg-1">
<form asp-page-handler="delete" method="post">
<input type="hidden" asp-for="TriggerCount" />
<button id="btndelete" disabled="#(Model.TriggerCount>=1?true:false)" class="btn btn-default">
Delete
</button>
</form>
</div>
</div>
<h3 class="clearfix">#Model.Message</h3>
the screenshot as below:
I am trying to navigate the user to a new page with a button click. I am having issues with rendering the new view. Any time I do click on the button, I either get an error for a dropdown on my page, or I get the home page view but with my desired route in the URL. I wanted to note that the user will be navigating to this page from the home page, which I made into my new landing page on the app. I wanted to point that out in case something I did here can be modified.
How I created a new landing page
I want to navigate from my landing page to my book inventory page.
My View (On the landing page):
<form method="post" asp-controller="Home" asp-action="Home" role="post">
<div class="form-group">
<label asp-for="bookName"></label>
<select name="bookName" asp-items="#(new SelectList(ViewBag.message, "ID", "bookName"))">
</select>
</div>
<div class="form-group">
<input type="submit" value="Submit" class="btn btn-primary" />
</div>
</form>
<div class="row">
<div class="form-group">
<a asp-controller="BookInventory" asp-action="Index">
<input type="button" value="Book Inventory Page" />
</a>
</div>
</div>
My Controller (On my landing page)
public void GetBooksDDL()
{
List<BookModel> bookName = new List<BookModel>();
bookName = (from b in _context.BookModel select b).ToList();
bookName.Insert(0, new BookModel { ID = 0, bookName = "" });
ViewBag.message = bookName;
}
[HttpGet("[action]")]
[Route("/Home")]
public IActionResult Home()
{
GetBooksDDL()
return View();
}
My Controller (On my book inventory page):
[HttpGet("[action]")]
[Route("/Index")]
public IActionResult Index()
{
return View();
}
I wanted to note that my breakpoint on my book inventory controller does hit the 'return View()', but it will still render the items from the homepage.
The error I get with the book dropdown says:
ArgumentNullException: Value cannot be null. (Parameter 'items')
Microsoft.AspNetCore.Mvc.Rendering.MultiSelectList.ctor(IEnumerable items, string dataValueField, string dataTextField, IEnumerable selectedValues, string dataGroupField).
I'm wondering why I'm getting this error when I'm trying to navigate to a different page. Since this is the new landing page, is it possible that it is passing along all of its data to the rest of the pages?
ArgumentNullException: Value cannot be null. (Parameter 'items')
Microsoft.AspNetCore.Mvc.Rendering.MultiSelectList.ctor(IEnumerable
items, string dataValueField, string dataTextField, IEnumerable
selectedValues, string dataGroupField).
About this error, it means that you didn't set the value for the select element, before return to the view, please check the ViewBag.message value, make sure it contains value.
Note: Please remember to check the post method, if the Http Get and Post method returns the same page, make sure you set the ViewBag.message value in both of the action methods.
I wanted to note that my breakpoint on my book inventory controller
does hit the 'return View()', but it will still render the items from
the homepage.
In the BookInventory Controller Index action method, right click and click the "Go to View" option, make sure you have added the Index view.
Based on your code, I have created a sample using the following code, it seems that everything works well.
Code in the Home Controller:
[HttpGet("[action]")]
[Route("/Home")]
public IActionResult Home()
{
GetBooksDDL();
return View();
}
[HttpPost("[action]")]
[Route("/Home")]
public IActionResult Home(BookModel book, string bookName)
{
GetBooksDDL();
//used to set the default selected value, based on the book id (bookName) to find the book.
List<BookModel> booklist = (List<BookModel>)ViewBag.message;
book = booklist.Find(c => c.ID == Convert.ToInt32(bookName));
return View(book);
}
Code in the Home view:
#model BookModel
#{
ViewData["Title"] = "Home";
}
<h1>Home</h1>
<form method="post" asp-controller="Home" asp-action="Home" role="post">
<div class="form-group">
<label asp-for="bookName"></label>
<select name="bookName" asp-items="#(new SelectList(ViewBag.message, "ID", "bookName", Model == null? 0:Model.ID))">
</select>
</div>
<div class="form-group">
<input type="submit" value="Submit" class="btn btn-primary" />
</div>
</form>
<div class="row">
<div class="form-group">
<a asp-controller="BookInventory" asp-action="Index">
<input type="button" value="Book Inventory Page" />
</a>
</div>
</div>
Code in the BookInventory controller:
public class BookInventoryController : Controller
{
[HttpGet("[action]")]
[Route("/Index")]
// GET: BookInventory
public ActionResult Index()
{
return View();
}
The screenshot as below:
If still not working, please check your routing configuration, perhaps there have some issue in the routing configure.
Your anchor tag formation is incorrect. You cannot write a button within anchor tag.
Do something like this:
<a asp-action="Index" asp-controller="BookInventory" class="btn btn-primary">Book Inventory Page</a>
Here the class will help your anchor tag look like buttons. I hope you have used bootstrap in your project. If not, then use it.
Hope this helps.
Here is another simple way:
<button type="button" class="btn" onclick="window.location.href = '/YourPage'">Button Title</button>
I am trying to (non-Ajax) get a checkbox to resubmit form in Razor Pages and reload the page / and catch the result in my OnPost method.
I have in my index.cshtml
#page "{id:int?}"
#model IndexModel
<div class="text-center">
<form action="post" name="form1">
<strong>Filter:</strong> Hide Single Runs <input onclick="document.form1.submit()" asp-for="#Model.HideSingle" />
<hr />
And in my PageModel
public class IndexModel : PageModel
{
public bool HideSingle { get; set; } = true;
public async Task OnPost(int? id, bool hideSingle)
{
for say a starting page URL:
http://localhost:5000/TestRuns/1
The form submits on the checkbox click, but it ends up with a Url:
http://localhost:5000/TestRuns/post?HideSingle=false
Which obviously fails to resolve as I am expecting a route of http://localhost:5000/TestRuns/1.
For Asp.net Core form, the default method is Get which means for your current code, it send request with Get instead of post. You could specify the method with post like
<div class="text-center">
<form method="post" name="form1">
<strong>Filter:</strong> Hide Single Runs <input onclick="document.form1.submit()" asp-for="#Model.HideSingle" />
<hr />
</form>
</div>
For another way, you could explictly set the handler like
<div class="text-center">
<form asp-page-handler="post" name="form2">
<strong>Filter:</strong> Hide Single Runs <input onclick="document.form2.submit()" asp-for="#Model.HideSingle" />
<hr />
</form>
</div>
<input onclick="document.form1.submit()" />
is using JavaScript to submit the form. Do you want the page to post back, since you're not using Ajax (from your question)?
You want the call from your page to end up being http://localhost:5000/TestRuns/post?id=1&HideSingle=false. Is id required for your post to work (it's unclear with OnPost(int? id..)?
I order to get both values, you'll need to have hidden form values or multiple <input asp-for="Email" class="form-control" />. You need 1 for hideSingle and on for the id.
I'm trying to update only part of my page from a partial View.
It works perfectly fine if i use this
Click heeeeeeeere
But this is a simple get and i'd like to actually post some data and do something with it. I wrote a form, set its method to post like this.
<form method="post" data-ajax="true" data-ajax-method="post" data-ajax-complete="completed" data-ajax-update="#panel" >
<div class="row">
id : #Html.TextBoxFor(x => x.customer.ID)
</div>
<div class="row">
Name : #Html.TextBoxFor(x => x.customer.Name)
</div>
<div class="row">
<input type="submit" value="send data" />
</div>
</form>
BUT this updates my entire page so my entire page is just the little partial view thats supposed to be updated.
a first observation, it seems you are missing the data-ajax-url from the second form .
Saying that, then in your Razor view you should include on the top of the page
#page "{handler?}"
This will allow you to pass additional information to your handler, then in your form you can simply include something like
<input type="hidden" name="id" value="send value"/>
where value is the value you want to pass and name is how the handler will identify what property to bind this to, then in your .cshtml.cs page your handler should look something like this
public IActionResult OnPostPartial(string id) {...//do something here id == "send value"}
hope this helps
I have a list of "workbooks" displayed in a table. Each workbook has a "Share" button next to the workbook's title. When the user clicks on the share button a modal dialog is shown containing a form.
The form allows the user to enter a list of the recipient's emails separated by a comma which is validated on the client-side.
As the dialog is located in a partial view _ShareView.cshtml that allows me to pass a modal WorkbookShareModel that has some fields like WorkbookId and Title. The goal here is to pass the details of each workbook when the user presses the share button (i.e. construct a modal and pass it to the already rendered model).
I am not sure how to pass a model to an already rendered view?
The solution have to be done on the client (i.e. dont involve actions on the server that return the partial view provided the parameters are passed). I want to avoid unnesessary calls to the server - we have all the data on the client regarding a workbook and I need to do a POST when the user types in list of emails.
This is my index.cshtml:
#section BodyFill
{
<div id="shareFormContainer">
#{ await Html.RenderPartialAsync("_ShareView", new WorkbookShareModel());}
</div>
<div class="landing-container">
<div class="workbook-container">
<table class="table">
<tbody>
#foreach (var workbook in Model.Workbooks)
{
string trClassName, linkText;
if (workbook.Metadata.SharedBy == null)
{
trClassName = "saved-workbooks";
linkText = workbook.Name;
} else {
trClassName = "shared-with-me";
linkText = string.Format(
BaseLanguage.SharedWithMeWorkbook,
workbook.Name,
workbook.Metadata.SharedBy,
workbook.Metadata.SharedDate.ToShortDateString()
);
}
<tr class="#trClassName">
<td>#Html.ActionLink(linkText, "Open", "OpenAnalytics", new { id = Model.Id, workbook = workbook.Name })</td>
<td class="last-modified-date" title="Last Modified Date">#workbook.ModifiedDate.ToShortDateString()</td>
<td class="share">
<button title="Share" class="share-button" onclick='showSharingView("#workbook.Name", "#workbook.Id", "#Model.Id")'> </button>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
#section Scripts
{
<!--Load JQuery 'unobtrusive' validation -->
#await Html.PartialAsync("_ValidationScriptsPartial")
<script type="text/javascript">
// hide the modal as soon as the page loads
$('#shareFormModal').modal("hide");
function showSharingView(title, workbookId, id) {
$('#shareFormModal').modal("show");
// how to pass a WorkbookShareModel to my partial view from here?
}
function hideDialog() {
var form = $("#partialform");
// only hide the dialog if the form is valid
if (form.valid()) {
activateShareButtons();
$('#shareFormModal').modal("hide");
}
}
// Helper method that validates list of emails
function IsEmailValid(emailList, element, parameters) {
var SPLIT_REGEXP = /[,;\s]\s*/;
var EMAIL_REGEXP =
/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+##[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$/i;
var emails = emailList.split(SPLIT_REGEXP);
for (var i = emails.length; i--;) {
if (!EMAIL_REGEXP.test(emails[i].trim())) {
return false;
}
}
return true;
}
</script>
}
That is my dialog:
#using DNAAnalysisCore.Resources
#model DNAAnalysisCore.Models.WorkbookShareModel
#* Partial view that contains the 'Share Workbook dialog' modal *#
<!-- Modal -->
<div onclick="activateShareButtons()" class="modal fade" id="shareFormModal" role="dialog">
<div class="modal-dialog modal-md">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Share Workbook - #Model.Title</h4>
</div>
#using (Html.BeginForm("ShareWorkbook", "Home", FormMethod.Post, new { #id = "partialform" }))
{
<div class="modal-body">
<label>#BaseLanguage.Share_workbook_Instruction_text</label>
<div class="form-group">
<textarea class="form-control" asp-for="Emails" rows="4" cols="50" placeholder="#BaseLanguage.ShareDialogPlaceholder"></textarea>
<span asp-validation-for="Emails" class="text-danger"></span>
</div>
<input asp-for="Title" />
<input asp-for="Id" />
<input asp-for="WorkbookId"/>
</div>
<div class="modal-footer">
<button onclick="hideDialog()" type="submit" class="btn btn-primary">Share</button>
<button onclick="activateShareButtons()" id="btnCancelDialog" type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
}
</div>
</div>
</div>
There are two solutions to solve your problem :
Option 1 :
Since you have got the parameters(title, workbookId, id) , you can call server side function using AJAX to render the partial view , then replace the DIV contained in the partial view with the updated contents in the callback function of AJAX .
You can click here for code sample .
Option 2 :
Directly update related input/area using Jquery . For example , the input tag helper :
<input asp-for="<Expression Name>">
generates the id and name HTML attributes for the expression name specified in the asp-for attribute. So you can set the value using Jquery like :
$("#Title").val("Title")
Please click here for Tag Helpers in forms in ASP.NET Core
With Option 2 , you need to clear the Emails area firstly after user click the share button ; With Option 1 , you don't need to care that since the HTML will replace entirely .