Razor #page routing to a different page - asp.net-core

I'm using aspnet core 3.
In a controller, my user can type in a single url like ... /login ... and then I test which company it is and return the correct view - like this:
[Route("login")]
public IActionResult Login()
{
return _company == "a" ? View("ThisView") : View("ThatView");
}
But if I use Razor routing ... #page "/login" ... how can I do the same? I need the user to type a single url, but then I need to test which company it is and then direct to the correct razor page.

You can use "RedirectToPageResult()" in razor page to return to
the corresponding page instead of view.
The premise is that you need to create the corresponding page to
return.
For example, I have a page named MyTest, user can input the parameter in this page.
If user enter "a" , then return to ThisPage.cshtml, otherwise, return to ThatPage.cshtml.
MyTest.cshtml:
#page
#model WebApplication1_rzaor_page.MyTestModel
#{
ViewData["Title"] = "MyTest";
Layout = "~/Pages/Shared/_Layout.cshtml";
}
<h1>MyTest</h1>
<form asp-page-handler="Login" method="post">
<input id="Text1" type="text" name="_company" />
<input id="Submit1" type="submit" value="submit" />
</form>
MyTest.cshtml.cs:
public class MyTestModel : PageModel
{
public void OnGet()
{
}
public IActionResult OnPostLogin(string _company)
{
return _company == "a" ? new RedirectToPageResult("ThisPage") : new RedirectToPageResult("ThatPage");
}
}
Here is the result :

Related

Razor pages view renders original form value instead of modified when reshown

Moving from ASP.NET Core MVC to Razor pages, there must be an issue in my understanding of passing data to a Razor view.
Here's a simple view:
#page
#model TestModel
#{
System.Diagnostics.Debug.WriteLine(Model.Name);
}
<form class="form-horizontal" method="get">
Name: <input type="text" class="form-control" asp-for="Name">
<button type="submit" class="btn btn-default">Send...</button>
</form>
And here is the view model class with one event handler:
public class TestModel : PageModel
{
[BindProperty(SupportsGet = true)]
public string Name { get; set; } = "";
public TestModel() {}
public IActionResult OnGet()
{
Name += "N";
return Page();
}
}
Then running the project:
Debug.WriteLine shows "N" as expected
input fields has "N" default value as expected
overwrite "N" in the input field eg. to "A" then press Send button
Debug.WriteLine shows "AN" as expected so view has got the value modified by OnGet()
the input field in the page itself contains the value "A" instead of "AN", the generated HTML contains:
value="A"
View does not render the modified Model.Name value but the original from the form data.
How can it be corrected to make view to render the modified string?
You can try to add ModelState.Clear(); in OnGet handler.When binding data in client side,it will get value from ModelState prior to Model.
public class TestModel : PageModel
{
[BindProperty(SupportsGet = true)]
public string Name { get; set; } = "";
public TestModel() {}
public IActionResult OnGet()
{
Name += "N";
ModelState.Clear();
return Page();
}
}

Razor Pages - Return Error on Duplicate Name

I'm working on a Razor Pages form that takes in a string to create a new customer in a SQL Server Database. I want to make it work so that if the string that is the customer already exists, a prompt comes up that says "This Customer Already Exists". Just to be safe for data integrity.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
int customerCheck = -1; //No Customer ID is -1
try
{
using (var context = new DataWarehouseContext())
{
customerCheck = context.Customer //Tries to grab a Customer with this name
.Where(a => a.Name == Customer.name)
.Select(b => b.CustomerId)
.FirstOrDefault();
}
}
catch (Exception)
{
}
if(customerCheck == -1)
{
_context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("/Customer/List");
}
else
{
return Page();
}
}
This is the code I have so far in my backend. What happens is that when a user tries to create a new customer, the backend of the page tries to see if it can grab a customer ID that correlates to this name. If it can, then the value of customerCheck is not -1, therefore some error should get printed out.
I don't know what methods can be used to do this, so any help would be great!
I found a solution, and it wasn't hard to implement. When a duplicate customer was found in the backend, I create a ModelState.AddModelError object and fill it with a key and a description of the error. Next, in the frontend, I put it within an H3 tag to print it out like so:
Backend OnPost() Code
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
int customerCheck = 0; //No Customer ID is 0
try
{
using (var context = new DataWarehouseContext())
{
customerCheck = context.Customer //Tries to grab a Customer with this name
.Where(a => a.Name == Customer.name)
.Select(b => b.CustomerId)
.FirstOrDefault();
}
}
catch (Exception)
{
}
if(customerCheck == 0)
{
_context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("/Customer/List");
}
else
{
ModelState.AddModelError("DuplicateCustomer", "This Customer Already Exists");
return Page();
}
}
So on the frontend, it gets implemented like this:
<h3 align="center" style="color:yellowgreen">#Html.ValidationMessage("DuplicateCustomer")</h3>
When return Page(); is hit, the page is reloaded and the DuplicateCustomer Error appears.
At first, glad to hear you have found a solution.
Besides, I think you could also use the Remote Validation to check whether the Customer is exist or not. Check the following sample code:
Remote validation in ASP.NET (Core) relies on Unobtrusive AJAX, so you will need to install that first. The easiest way to do this is via LibMan. Right click on the lib folder in wwwroot, choose Add ยป Client-side Library, and then choose jsdelivr as the source, and type in jquery-ajax-unobtrusive, click the "Install" button to install the package.
In the CreateCustomer.cshtml.cs page, add a Email property and use the PageRemote attribute, then, add a handler method to perform the validation.
public class CreateCustomerModel : PageModel
{
private readonly IRepository _repository;
public CreateCustomerModel(IRepository repository)
{
_repository = repository;
}
[PageRemote(ErrorMessage = "Email Address already exists", AdditionalFields = "__RequestVerificationToken", HttpMethod = "post",PageHandler = "CheckEmail")]
[BindProperty]
public string Email { get; set; }
public void OnGet()
{
}
public IActionResult OnPost()
{
if (ModelState.IsValid)
{
//insert data into database.
}
return Page();
}
#pragma warning disable MVC1001 // Filters cannot be applied to page handler methods.
[ValidateAntiForgeryToken]
#pragma warning restore MVC1001 // Filters cannot be applied to page handler methods.
public JsonResult OnPostCheckEmail()
{
//query database and check whether the email is exist or not.
var existingEmails = _repository.GetCustomers().Select(c => c.Email.ToLower()).ToList();
var valid = !existingEmails.Contains(Email.ToLower());
return new JsonResult(valid);
}
In the CreateCustomer.cshtml razor page, add JQuery reference and add a form to enter the values.
#page
#model RazorSample.Pages.CreateCustomerModel
#{
}
<div class="row">
<div class="col-md-4">
<form method="post" asp-antiforgery="true">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
#* add other fields *#
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
#section scripts{
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<partial name="_ValidationScriptsPartial" />
<script src="~/lib/jquery-ajax-unobtrusive/jquery.unobtrusive-ajax.min.js"></script>
}
After submit the button, the result as below: if the email is exist, it will show the prompt:
[Note] In the above sample, we are adding the properties in the PageModel (instead of nested model), and use it to validate the field. Because, if using nested object, we might meet the 400 Bad Request result. The 400 error is related to the AntiForgeryToken, if you meet this error, try to ignore validate the AntiForgeryToken or custom add the __RequestVerificationToken token at the body or header, check this link.
More detail information about Remote Validation in Razor Pages, check the following articles:
Remote Validation in Razor Pages
Improved Remote Validation in Razor Pages

MVC : Pass values from textbox to controller action

I am new to MVC.Basically I need to pass values entered in the textbox from my view to controller action method. As I enter the values in the text box and click the enter button I need to display the value on the screen. I am currently unable to do so. Please find my code below
The model class
public class ProteinTrackingService
{
public int? Total { get; set; }
public int Goal { get; set; }
public void AddProtein(int? amount)
{
Total += amount;
}
}
The controller class
public class ProteinTrackerController : Controller
{
ProteinTrackingService proteinTrackingService = new ProteinTrackingService();
// GET: ProteinTracker
public ActionResult Index()
{
ViewBag.Total = proteinTrackingService.Total;
ViewBag.Goal = proteinTrackingService.Goal;
return View();
}
// GET: ProteinTracker/Details/5
public ActionResult AddProtein(ProteinTrackingService model)
{
proteinTrackingService.AddProtein(model.Total);
ViewBag.Total = proteinTrackingService.Total;
ViewBag.Goal = proteinTrackingService.Goal;
return View("Index");
}
}
The view
using (Html.BeginForm("ProteinTracker", "AddProtein",FormMethod.Post))
{
#Html.AntiForgeryToken()
<form>
<div class="form-horizontal">
<h4>Protein Tracker</h4>
<hr />
Total : #ViewBag.Total
Goal : #ViewBag.Goal
<input id="Text1" type="text" value="TextInput" /> <input type="Submit" value="Add" />
</div>
</form>
}
I am modifying the code above based on your suggestions. I basically need to display the following in the view
Total : value
Goal : value
Textbox control (To enter the total) Button (pass the total to contoller) Please note that when the user clicks the Add button the total should show in above field Total : value.
New View
#using (Html.BeginForm( "AddProtein","ProteinTracker", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Protein Tracker</h4>
<hr />
#Html.LabelFor(m => m.Total, "Total" ) <hr />
#Html.LabelFor(m => m.Goal, "Goal") <hr />
#Html.TextBoxFor(m => m.Total) <hr />
<input type="Submit" value="Add" />
</div>
}
New Controller
public class ProteinTrackerController : Controller
{
ProteinTrackingService proteinTrackingService = new ProteinTrackingService();
// GET: ProteinTracker
public ActionResult Index()
{
var model = new ProteinTrackingService()
{ Total = proteinTrackingService.Total, Goal = proteinTrackingService.Goal };
return View(model);
}
// GET: ProteinTracker/Details/5
public ActionResult AddProtein(ProteinTrackingService model)
{
proteinTrackingService.AddProtein(model.Total);
model.Total = proteinTrackingService.Total;
model.Goal = proteinTrackingService.Goal;
return View("Index",model);
}
}
You need to add the HttpPost attribute to your action.Looking at your form #using (Html.BeginForm( "AddProtein","ProteinTracker", FormMethod.Post)) , apparently you are sending a post request to your controller.
[HttpPost]
public ActionResult AddProtein(ProteinTrackingService model)
{
proteinTrackingService.AddProtein(model.Total);
model.Total = proteinTrackingService.Total;
model.Goal = proteinTrackingService.Goal;
return View("Index",model);
}
First of all your this syntax
using (Html.BeginForm("ProteinTracker", "AddProtein", FormMethod.Post))
already creates a form tag when html generates. No need to create from tag again in it.
So for your want, in view you need give to your input field a name
<input id="Text1" type="text" value="TextInput" name="textname"/>
and add this name as parameter in your controller method like that
public ActionResult AddProtein(ProteinTrackingService model,string textname)
{
// your code
return View("Index");
}
It will pass your textfield value from view to controller. For clearing your concept you may visit Loopcoder.com

ASP.NET MVC 4 - ListBoxFor, send selectedValue in ActionLink

I have a list of model. I want to retrieve the listBoxSelectedValue to send it in my actionLink to edit it.
This is my view :
#using (Html.BeginForm())
{
#Html.ListBoxFor(a => a.SelectedApplis, new SelectList(ViewBag.Applis,"ID","Name", Model.SelectedApplis))<br/>
#Html.ActionLink("Add","Create","Application")<br/>
#Html.ActionLink("Edit","Edit","Application", null, new { listAppId = Model.SelectedApplis})<br/>
#Html.ActionLink("Delete","Delete","Application")<br/>
}
I created a class "ListBoxApplication" with the List which will contain the selectedValue of the ListBox.
public class ListBoxApplication
{
public IEnumerable<int> SelectedApplis { get; set; }
public ListBoxApplication()
{
SelectedApplis = new List<int>();
}
}
I have 2 controllers : Application and Home
In HomeController, I created the model ListBoxApplication which contain the List. In my ViewBag.Applis, i have all my ApplicationModel.
public ActionResult Index()
{
ListBoxApplication listeApplis = new ListBoxApplication();
ViewBag.Applis = ApplicationModels.GetListApplications();
return View(listeApplis);
}
In my ApplicationController :
public ActionResult Edit(ListBoxApplication listAppId)
{
// I WANT TO RETRIEVE MY listAppId HERE, but it is always 'null'
return View();
}
So I think my problem is in the actionLink :
#Html.ActionLink("Edit","Edit","Application", null, new { listAppId = Model.SelectedApplis})
Me Edit Method is not is the actual controller (Home/Index). I need to send the selectedValue of my ListBox in my actionLink to (Application/Edit).
The listAppId is always 'null'. It doesn't retrieve the value... Is there a mistake in my actionLink ?
Thanks for advance
I don't believe that action links will trigger a postback to the server. Try this instead:
#Html.ActionLink("Delete","Delete","Application")<br/>
#Html.ActionLink("Add","Create","Application")<br/>
#using (Html.BeginForm("Detail","Application"))
{
#Html.ListBoxFor(a => a.SelectedApplis, new SelectList(ViewBag.Applis)) //not sure what the other params you had here were for, but it should work like this
<br/>
<input type="submit" name="Edit" value = "Edit"/>
#*added in response to comment*#
<input type="submit" name="Delete" value = "Delete"/>
<input type="submit" name="Add" value = "Add"/>
}
If you plan on having all of those buttons post back to the server, you could also use ajax (and javascript) to accomplish this same goal, without needing to write out a form for each individual button. Both ways would work just fine, multiple forms is technically easier though.
public ActionResult Detail(ListBoxApplication listAppId, bool Edit, bool Add, bool Delete)
{
if(//check your bools here){
}
return View();
}

Why submit button does't call controller function?

I have a small problem with calling controller function. Strange is that every other submit button works fine. But this one has problem which I cannot solve for now.
I will show you two forms with submit buttons becouse there is only one working fine.
Controller:
public class MyController : Controller
{
public ActionResult MethodOne()
{
...
return RedirectToAction("index");
}
public ActionResult MethodTwo()
{
...
return RedirectToAction("index");
}
}
And the view:
//This one works fine!!
#using (Html.BeginForm("MethodOne", "My", FormMethod.Post))
{
<input id="Some-cool-id" type="submit" value="Add!" />
}
//This one doesn't work?!
#using (Html.BeginForm("MethodTwo", "My", FormMethod.Post))
{
<input id="some-cool-id2" type="submit" value="Delete"!" />
}
Error is telling that Method2 is not in the required path.
Resource not found.
Description: HTTP 404. Searched resource (or ...) ...
Required path URL: /My/MethodTwo
I was searching what is bad but in the end, I need a help, thanks.
Add the property [HttpPost] before the method.
Try this,
#using (Html.BeginForm("MethodTwo", "Test", FormMethod.Post))
{
<input type="submit" value="asd" />
}
#using (Html.BeginForm("MethodOne", "Test", FormMethod.Post))
{
<input type="submit" value="poll" />
}
Controller
public class TestController : Controller
{
public ActionResult MethodOne()
{
return RedirectToAction("CustomerInfo");
}
public ActionResult MethodTwo()
{
return RedirectToAction("CustomerInfo");
}
[HttpGet]
public ActionResult CustomerInfo()
{
ViewBag.CustomerNameID = new SelectList(List, "CustomerId", "customerName");
ViewBag.RegisterItems = GetAllRegisterData();
ViewData["SampleList"] = GetAllRegisterData();
return View();
}
}