I'm trying to understand how Razor pages work, as well as .Net Core, by creating a small web application and I'm stuck on how to handle the button action within a form. I'm used to the MVC type of process (from when I first tried web apps 5 years ago) where the button would have a onClick action that could be accessed from the code behind but it seems like that's not the same with a Razor page (unless I'm just not seeing it). I have a basic form like this
<form method="post">
<fieldset>
<input type="text" value="" placeholder="user name"/>
<input type="password" value="" placeholder="password"/>
<input type="button" value="Submit" id="submitButton"/>
</fieldset>
So what I'm trying to achieve is when the button is pressed an action in the .cs file is called that will perform a couple different operations (like calling an API, getting a result and then depending on result route to a different page) but even if I add an "onClick" to the button I can't figure out how to hook it up to the code behind. I've seen various answers, most using models and a database but since that's not the same as what I'm doing those examples haven't helped.
I will try to make a simple example for you. Create a razor page and use the name "Test". The Test.cshtml file should have the following contents:
#page
#model WebApplication1.Pages.TestModel
<form method="post">
<fieldset>
<input asp-for="username" placeholder="user name" />
<span asp-validation-for="username" class="text-danger"></span>
<br />
<input asp-for="password" type="password" placeholder="password" />
<span asp-validation-for="password" class="text-danger"></span>
<br />
<input type="submit" value="Submit" id="submitButton" />
</fieldset>
</form>
The Test.cshtml.cs should have the following contents
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace WebApplication1.Pages
{
public class TestModel : PageModel
{
[BindProperty]
public string username { get; set; }
[BindProperty]
public string password { get; set; }
public void OnGet()
{
// you can initialize the values. for example I set the username
username = "test";
}
public IActionResult OnPost()
{
// do something with username and password
if (string.IsNullOrEmpty(password))
{
ModelState.AddModelError("password", "Password is a required field.");
return Page();
}
// or you can redirect to another page
return RedirectToPage("./Index");
}
}
}
Tell me if you need extra explanation for this example. I hope it helps.
Related
I have a form build via Razor Pages. The form consists of two submit buttons - each of them is assigned to an appropriate asp-page-handler, like this:
#page "{id:int}"
//...
<form asp-action="Post">
<input type="submit" asp-page-handler="Approve" class="btn btn-success" value="Approve" />
<input type="submit" asp-page-handler="Decline" class="btn btn-danger" value="Decline" />
</form>
Handlers works correctly. Now I need to disable the Approve button by some condition.
Unfortunately I can't figure out what is the right way to archieve this using Razor Pages?
I tried the following (condition is simplified):
#{ var disableTag = "disabled"; }
...
<input type="submit" asp-page-handler="Approve" class="btn btn-success" value="Approve" #disableTag />
After launch of the app markup remained unchanged and the button is still enabled. Seems like asp-page-handler tag forces the razor engine to ignore #disableTag variable.
Next attempt was (#disableTag was put in front of asp-page-handler):
<input type="submit" #disableTag asp-page-handler="Approve" class="btn btn-success" value="Approve" /
Now the page rendered correctly (disabled tag is there), but the handler itself stopped working (the markup shows asp-page-handler="Approve" as a plain tag instead of something like /{PageName}/{id}?handler=Approve)
What am I doing wrong here?
Try to think the tag helper way. Disable is a property that you can define like all the other properties which are supported by the equivalent tag helper. Below you can find two different ways do define your disable attribute for your buttons:
#{
bool disable1 = true;
string disable2 = "disable"; // or string disable2 = null;
}
<h2>Test</h2>
<form asp-action="Post">
<input type="submit" asp-page-handler="Approve" class="btn btn-success" value="Approve" disabled="#(disable1 ? "disabled" : null)" />
<input type="submit" asp-page-handler="Decline" class="btn btn-danger" value="Decline" disabled="#disable2" />
</form>
I hope it helps.
Complete Example
DisabledButton.cshtml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace MyWebApplication.Pages
{
public class DisabledButtonModel : PageModel
{
public bool Disbale1 { get; set; }
public bool Disbale2 { get; set; }
public void OnGet()
{
}
public ActionResult OnPostApprove()
{
Disbale1 = true;
Disbale2 = false;
return Page();
}
public ActionResult OnPostDecline()
{
Disbale1 = false;
Disbale2 = true;
return Page();
}
}
}
DisabledButton.cshtml
#page "{handler?}"
#model MyWebApplication.Pages.DisabledButtonModel
<form asp-action="Post">
<input type="submit" asp-page-handler="Approve" class="btn btn-success" value="Approve" disabled="#(Model.Disbale1 ? "disabled" : null)" />
<input type="submit" asp-page-handler="Decline" class="btn btn-danger" value="Decline" disabled="#(Model.Disbale2 ? "disabled" : null)" />
</form>
I'm hitting what I think is a pretty stupid issue that I am obviously missing something simple on.
I made a simple asp.net mvc site (.net 4.5) and changed the index to have a simple form that I'd like to just post back to itself and spit back the variables.
here is my form
#using(Html.BeginForm())
{
<input type="text" class="form-control" id="empId" placeholder="Enter EmployeeId (ex. 999999)">
<input type="text" class="form-control" id="account" placeholder="Enter account)">
<input type="email" class="form-control" id="email" placeholder="Enter email">
<input type="submit" class="btn btn-default" value="Submit" />
}
and here is my post method
[HttpPost]
public ActionResult Index(string empId, string account, string email)
{
return Content(Request["empId"]);
}
I get nothing back when the page posts. Also in the debugger I can see that the method gets hit, however all the parameters are null even though I filled in the form.
Am I missing something obvious?
You just forget the name attribute:
#using(Html.BeginForm())
{
<input type="text" class="form-control" name="empId" id="empId" placeholder="Enter EmployeeId (ex. 999999)">
<input type="text" class="form-control" name="account" id="account" placeholder="Enter account)">
<input type="email" class="form-control" name="email" id="email" placeholder="Enter email">
<input type="submit" class="btn btn-default" value="Submit" />
}
I always recommend to use model binding instead of some strings or int. If you use them well, it will make the model binding work effortlessly:
Model:
public class ExampleModel
{
public int empId { get; set; }
public string account{ get; set; }
public string email{ get; set; }
}
In the Razor page:
#using(Html.BeginForm())
{
#Html.EditorFor((m => m.intempId, new { #class = "form-control" } ))
#Html.EditorFor((m => m.account, new { #class = "form-control" }))
#Html.EditorFor((m => m.email, new { #class = "form-control" }))
}
and then in controller:
[HttpPost]
public ActionResult Index(ExampleModel model)
{
return Content(model.empId);
}
With the model, you can also add validation and so on, directly on the model and then ASP.NET MVC can put validation in both front-end with jQuery validation and back-end (if (ModelState.IsValid)). Lots of benefits to use models!
I am trying to implement a paypal payment in my .aspx page. I have the following html in my page:
<form target="paypal" action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_xclick">
<input type="hidden" name="hosted_button_id" value="19218">
<input type="hidden" name="rm" value="2">
<input type="hidden" name="business" value="<%= ConfigurationManager.AppSettings("BusinessEmail").ToString%>">
<input type="hidden" name="item_name" value="<%= "CityBits Gold " & listplans.SelectedItem.ToString & " listing plan for " & trim(txttitle.text)%>">
<input type="hidden" name="item_number" value="<%= HiddenFieldid.Value%>">
<input type="hidden" name="amount" value="<%= listplans.SelectedValue%>">
<input type="hidden" name="currency_code" value="EUR">
<input type="hidden" name="return" value="<%= ConfigurationManager.AppSettings("ReturnUrl").ToString & "?requestID=" & HiddenFieldid.Value%>">
<input type="hidden" name="cancel_return" value="<%= ConfigurationManager.AppSettings("CancelUrl").ToString%>">
<input type="hidden" name="no_shipping" value="1">
<input type="image" src="https://www.sandbox.paypal.com/en_US/i/btn/btn_cart_LG.gif" border="0" name="submit" alt="">
<img alt="" border="0" src="https://www.sandbox.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1">
</form>
In my aspx page, when i click the paypal button, it just refresh the page. When I put the exact same code (with actual values of course) in a plain html file and click the button, it redirect me to paypal as desired. I have tried using actual values for inputs just like in the html page, but it still does not work.
if it matters, I have update panels in my page, but this form is not inside them.
Anyone knows what I am doing wrong? It might be something stupid, but this is giving me headaches for 2 days now!
The problem here is the way ASP.NET pages work. ASP.NET always assumes only one large form on the page, and begins to perform various tricks when you introduce a second one.
However what you can use in your case is a Button control and its PostBackUrl property. It will handle your inner form correctly, gathering all parameters and performing a post:
<form target="paypal">
...
<asp:ImageButton runat="server" ID="PayPalSubmit"
PostBackUrl="https://www.paypal.com/cgi-bin/webscr"
ImageUrl="https://www.sandbox.paypal.com/en_US/i/btn/btn_cart_LG.gif" />
</form>
This is a well-known issue with ASP.NET forms when trying to post data to PayPal. Here's a really cool solution that I use all the time for E-Commerce Web Forms applications:
http://jerschneid.blogspot.com/2007/03/hide-form-tag-but-leave-content.html
And here are two personal blog posts expanding on Jeremy Schneider's idea of using a custom version of HtmlForm:
http://codersbarn.com/post/2008/03/08/Solution-to-ASPNET-Form-PayPal-Problem.aspx
http://codersbarn.com/post/2008/03/27/Integrate-PayPal-Checkout-Button-with-ASPNET-20.aspx
Stay away from using double form tags, JavaScript and other hacks.
using System;
using System.Web.UI;
using System.Web.UI.HtmlControls;
/// <summary>
/// This is a special form that can _not_ render the actual form tag, but always render the contents
/// </summary>
public class GhostForm : System.Web.UI.HtmlControls.HtmlForm
{
protected bool _render;
public bool RenderFormTag
{
get { return _render; }
set { _render = value; }
}
public GhostForm()
{
//By default, show the form tag
_render = true;
}
protected override void RenderBeginTag(HtmlTextWriter writer)
{
//Only render the tag when _render is set to true
if (_render)
base.RenderBeginTag(writer);
}
protected override void RenderEndTag(HtmlTextWriter writer)
{
//Only render the tag when _render is set to true
if (_render)
base.RenderEndTag(writer);
}
}
I am working on a test mvc project and since it's my 1st time working in mvc environment I am almost lost and is completely different compared to asp.net web forms.
I am trying to put a textbox and a button on a form, but when I am using <%= Html.TextBox("name") %> for textbox for example, the code displays as a text on the screen and is not rendered as a textbox. When I am using html markup for textbox and button I can see the textbox but shouldn't <%= Html.TextBox("name") %> be correct way to do that?
Here is what I have here:
#{
Layout = "~/_SiteLayout.cshtml";
Page.Title = "Welcome to my Web Site!";
}
<p>
ASP.NET Web Pages make it easy to build powerful .NET based applications for the web.
Enter your name: <%= Html.TextBox("name") %>
<input id="Text1" type="text" />
<input id="Button1" type="button" value="button" />
</p>
Which way should I go, can I go with the standard html format or what am I doing wrong that the textbox from <%= Html.TextBox("name") %> doesn't get displayed?
Thanks in advance, Laziale
You are using ASPX syntax. For Razor, it would be something like this:
#Html.TextBox("TextBoxName")
So your code would look like:
<p>
ASP.NET Web Pages make it easy to build powerful .NET based applications for the web.
Enter your name:
#Html.TextBox("name")
<input id="Button1" type="button" value="button" />
</p>
In addition to the previous answers, if you are referencing a Model on your View page, then you can use the Razor HTML Helpers with Lambda expressions.
Updated Example (This update is in response to the Laziale's comment):
In your Models directory you have a User class:
namespace MvcApplication.Models
{
public class User
{
public string Name { get; set; }
}
}
In your Controllers directory, you have a UserController:
namespace MvcApplication.Controllers
{
public class UserController : Controller
{
//
// GET: /User/
public ActionResult Index()
{
return View();
}
}
}
In your Views directory, you have a sub-directory named "User" which contains an "Index.cshtml" file:
#model MvcApplication.Models.User
#using (Html.BeginForm())
{
#Html.TextBoxFor(x => x.Name)
<input type="submit" />
}
MVC/Razor will create the following HTML:
<html>
<head>...</head>
<body>
<form action="/User" method="post">
<input id="Name" name="Name" type="text" value="" />
<input type="submit" />
</form>
</body>
</html>
You use the Razor syntax which is denoted using # at the start:
#Html.TextBox("name")
I am trying to bind a dynamic array of elements to a view model where there might be missing indexes in the html
e.g. with the view model
class FooViewModel
{
public List<BarViewModel> Bars { get; set; }
}
class BarViewModel
{
public string Something { get; set; }
}
and the html
<input type="text" name="Bars[1].Something" value="a" />
<input type="text" name="Bars[3].Something" value="b" />
<input type="text" name="Bars[6].Something" value="c" />
at the moment, bars will just be null. how could I get the model binder to ignore any missing elements? i.e. the above would bind to:
FooViewModel
{
Bars
{
BarViewModel { Something = "a" },
BarViewModel { Something = "b" },
BarViewModel { Something = "c" }
}
}
Add the .Index as your first hidden input to deal with out of sequence elements as explained in this Phil Haacked blog post:
<input type="text" name="Bars.Index" value="" />
<input type="text" name="Bars[1].Something" value="a" />
<input type="text" name="Bars[3].Something" value="b" />
<input type="text" name="Bars[6].Something" value="c" />
A possible workaround could be to instantiate the ViewModel and the collection to the correct size (assuming it's known), then update it with TryUpdateModel... something like:
[HttpPost]
public ActionResult SomePostBack(FormCollection form)
{
// you could either look in the formcollection to get this, or retrieve it from the users' settings etc.
int collectionSize = 6;
FooViewModel bars = new FooViewModel();
bars.Bars = new List<BarViewModel>(collectionSize);
TryUpdateModel(bars, form.ToValueProvider());
return View(bars);
}H
MVC is able to populate list itself.
public ActionResult Index(FooViewModel model)
{
...
So no matter if anything is missing mvc will create new List<BarViewModel> and
for each found index - [1],[3],[6] it will create new BarViewModel and add it to List. So you will get FooViewModel with populated Bars.
i didnt know even that worked!
bearing that in mind, id have done something like:
<input type="text" name="Bars.Something" value="a" />
<input type="hidden" name="Bars.Something" value="" />
<input type="text" name="Bars.Something" value="b" />
<input type="hidden" name="Bars.Something" value="" />
<input type="hidden" name="Bars.Something" value="" />
<input type="text" name="Bars.Something" value="c" />
which would hopefully post
a,,b,,,c
but I suspect that will bind in the same way as you describe
Youre probably going to have write a custom model binder that looks for the max index, makes a list of that size then puts the elements in the correct place.
Saying all that, wait for someone else to post a really simple attribute you can put on your property that makes it just work ;D