Razor class library and html helpers problem - asp.net-core

I create a small admin tool and I decided to convert it to a Razor class library so that I will be able to use it in other applications as well. I created the Razor Class Library project and I added all the razor pages that I had in my main project and I tried to test the new project. The problem was that the framework for some reason did not recognize the html helpers so I created a new clean project and try to find out what is wrong and the result was that the application did not fire the post action of the razor page and the asp-for property was not using properly the property value. I used the following code in order to test the Razor Class Library.
Page1.cshtml.cs
public class Page1Model : PageModel
{
[BindProperty]
public Input MyInput { get; set; }
public class Input
{
public string Name { get; set; }
}
public void OnGet()
{
}
public void OnPost()
{
}
}
Page1.cshtml
#page
#model WebApplication1.MyFeature.Pages.Page1Model
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Page1</title>
</head>
<body>
<form method="post">
<input asp-for="MyInput.Name" /><br />
<input type="submit" />
</form>
</body>
</html>
The generated html was the following
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Page1</title>
</head>
<body>
<form method="post">
<input asp-for="MyInput.Name" /><br />
<input type="submit" />
</form>
</body>
</html>
As you can see the input for MyInput.Name appears as I typed it the Page1.cshtml file. The right output shoud be the following:
<input type="text" id="MyInput_Name" name="MyInput.Name" value="" /><br />
Do I have to do something in order to make the html helpers work and the OnPost action to be called when a post request occurs?

I found the solution to the problem and I decided to share it with you just in case someone else has the same problem.
In order to make it work I had to add the file _ViewImports.cshtml in the pages folder of the Razor Class Library and add the following line:
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Related

How to set focus for InputRadio / InputRadioGroup in Blazor?

I want to set the focus on the InputRadioGroup but it appears it doesn't have the ElementReference attribute unlike the other Blazor built-in form components. Should I just extend the InputRadioGroup and add the ElementReference or is there another way to set focus on the InputRadio or InputRadioGroup?
You could refer to the sample below to focus on the InputRadio.
Vehicle.cs
namespace BlazorApp1.Model
{
public class Vehicle
{
public string Name { get; set; }
}
}
file1.js
window.jsfunction = { focusElement: function (id) { const element = document.getElementById(id); element.focus(); } }
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>BlazorApp1</title>
<base href="/" />
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="css/app.css" rel="stylesheet" />
<link href="BlazorApp1.styles.css" rel="stylesheet" />
<script src="file1.js"></script>
</head>
<body>
<div id="app">Loading...</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
Reload
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
</body>
</html>
Index.razor
#inject IJSRuntime js
#page "/"
<div>
<h4> vehicle Selected - #vehicle.Name </h4>
<EditForm Model="vehicle">
<InputRadioGroup #bind-Value="vehicle.Name" >
#foreach (var option in rdOptions)
{
<InputRadio Value="option" id=#option #onfocus="alrt" /> #option <br />
}
</InputRadioGroup>
<br>
<input Id="idPassWord" Type="password" />
<button #onclick="clickOK">Set Focus</button>
</EditForm>
</div>
#code{
BlazorApp1.Model.Vehicle vehicle=new BlazorApp1.Model.Vehicle(){Name = "auto"};
List<string> rdOptions = new List<string> { "car", "bus", "auto" };
private async void clickOK()
{
await Focus("car");
}
private void alrt()
{
Console.WriteLine("Element focused");
}
public async Task Focus(string elementId)
{
await js.InvokeVoidAsync("jsfunction.focusElement", elementId);
}
}
Output:
In the above code example, I am generating the InputRadio on the page which has the OnFocus event. While we try to set the Focus on the InputRadio using the JS code. OnFocus event gets fired and displays the message in a browser console. This proves that InputRadio is getting focused.
Further, you could modify the code as per your own requirements.
After some investigation, seems like the ability to focus for InputRadio/InputRadioGroup was removed due to some prior issues. They now returned the focus after I raised the issue, and it will be included to .NET 7.

Why OnPost is not getting hit without "#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers" in ASP.NET Core version 5.0

I'm using ASP.NET CORE version 5.0, and I'm using razor pages for my web UI. I was having a problem hitting a simple OnPost method with the following structure in HTML:
#page
#model EmptyOne.Web.Pages.SaysHelloModel
#{
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Search</title>
</head>
<body>
<p>Enter the name you wanna say hello two ;)</p>
<form method="post">
Search term:
<input name="nameToHello" />
<input type="submit" />
</form>
#* some logic to say hello if model is not null... *#
</body>
</html>
And this is my page model class:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace EmptyOne.Web.Pages
{
public class SaysHelloModel : PageModel
{
public string guysName { get; set; }
public void OnGet()
{
}
public IActionResult OnPost(string nameToHello)
{
guysName = nameToHello;
return Page();
}
}
}
It returns an HTTP 400 status core and a blank page when I try to hit it, but for some reason, when I add this helper:
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
It works fine!
The final and working view(i didn't change anything in the page model class):
#page
#model EmptyOne.Web.Pages.SaysHelloModel
#{
}
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Search</title>
</head>
<body>
<p>Enter the name you wanna say hello two ;)</p>
<form method="post">
Search term:
<input name="nameToHello" />
<input type="submit" />
</form>
#* some logic to say hello if model is not null... *#
</body>
</html>
So can anyone explain to me why that is? I looked up an ASP.NET Core version 5.0 code example and saw this helper. Am I using this helper in any way? What does it have to do with sending an HTTP post request to my page handler?
Razor Pages includes an anti-CSRF mechanism, known as request verification, by default. It relies on a token that is applied to a hidden form field being sent with each POST request. The hidden form field is generated automatically by the form tag helper. If you omit the token value from your post, for example by disabling the form tag helper, the framework returns a 400 Bad Request.

How to make this ASP.NET Core Model Validation work on client-side?

In my ASP.NET Core 1.1.1 app the following Model Validation is not working. I think the issue is related to me not properly adding validation scripts in Main View below.
Scenario:
I click on a button on Main View that calls a partial view.
I enter all correct values, in partial view and submit the form (in partial view), the form successfully gets submitted and all the values are correctly entered into SQL server db.
I then intentionally enter a string, say, abc into the input box for price (that is of nullable type float) and submit the form. A client side error does NOT show up even (the javascript is enabled on my Chrome browser). Hence, Form gets submitted to the server where ModeState.IsValid, as expected, is false in the POST action method.
Question: Why client-side validation (as shown in step 3) above is not working and how we can make it work?
Note: All the css and javascripts were added and configured by default by VS2017 when the project was created. So I think scripts are all there and I may not be calling them correctly on the views - but that's just an assumption.
MyViewModel
public class MyViewModel
{
public int FY { get; set; }
public byte OrderType { get; set; }
public float? Price { get; set; }
....
}
Main View
#model MyProj.Models.MainViewModel
...
<div>
<button type="submit" name="submit"...>GO</button>
</div
#section scripts
{
<script>
$(document).ready(function () {
....
$('.tab-content').on('click', '.BtnGO', function (event) {
....
$.ajax({
url: '#Url.Action("SU_AddCustOrder", "MyContr")',
data: { ....},
contentType: 'application/json',
dataType: 'html',
type: 'GET',
cache: false,
success: function (data) {
if (BtnVal == 'AddOrderBtnGo')
$('#menuAP').html(data);
else if ....
error: function (....){
alert(...);
}
});
});
MyContrController:
[HttpGet]
public IActionResult AddCustOrder(int Order_id)
{
....
return PartialView("~/Views/PartialsV/MyPartialView.cshtml", myVM);
....
}
[HttpPost]
public IActionResult AddCustOrder(MyViewModel model)
{
....
if(ModelState.IsValid)
{
....
}
....
}
Partial View
....
<div class="form-group">
<label asp-for="Price"></label>
<div class="col-md-10">
<input asp-for="Price" class="form-control"></input>
<span asp-validation-for="Price" class="text-danger"></span>
</div>
</div>
....
<button type="submit" name="submit"...>Add Order</button>
UPDATE
_layout.cshtm file
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>#ViewData["Title"] - Test</title>
<environment names="Development">
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" />
</environment>
<environment names="Staging,Production">
<link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment>
#RenderSection("styles", required:false)
</head>
<body>
<header>
<div class="container navbar navbar-inverse navbar-fixed-top text-center">
</div>
<div class="container nav nav-pills" style="margin-top:4px;background-color:cornsilk;">
#await Component.InvokeAsync("Welcome")
</div>
</header>
<div class="container body-content">
#RenderBody()
<hr />
<footer class="text-center">
<a asp-controller="Misc" asp-action="AccessibilityStatement" class="text-center text-muted">Accessibility Statement</a>
</footer>
</div>
<environment names="Development">
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
</environment>
<environment names="Staging,Production">
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js"
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery">
</script>
<script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/bootstrap.min.js"
asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal">
</script>
<script src="~/js/site.min.js" asp-append-version="true"></script>
</environment>
#RenderSection("scripts", required: false)
#RenderSection("css", required:false)
</body>
</html>
I see. If you go and open Shared folder inside Views folder you will find a file called _ValidationScriptsPartial.cshtml that contains the validation scripts.
Now the first thing to do is to add validation attributes such as [Required] to your view model.
Than in Main View add #{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } before <script>.
After you add the html of the partial view in this line $('#menuAP').html(data);, find the form and call $.validator.unobtrusive.parse() like the following
if (BtnVal == 'AddOrderBtnGo') {
$('#menuAP').html(data);
var $form = $('#menuAP').find('#your-form-id-here');
$.validator.unobtrusive.parse($form);
}

Authenticated users being redirected to login

My application (MVC4/C#) uses the SimpleMembershipProvider and generally works fine. However, I have a problem that I cannot resolve after spending many hours researching and testing.
If I leave my application for a period of time (say 30 minutes) then select a menu item, the page renders (sidebar/header/footer), but the #RenderBody section redirects to the ~/Account/Login action.
If I then ignore the login and click on any controller action link (from the menu) then it loads as expected. It appears that the razor layout view correctly thinks I am authenticated, but the controller doesn't think I am authorized. I have a base class for most of my controllers that I inherit from that has the [Authorize] attribute.
If I logout, only the RenderBody section renders as expected, for ~/Account/Login action.
From web.config
<system.web>
<roleManager enabled="true" />
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>
Base controller
[Authorize]
public abstract class AuthorizeBaseController : Controller
{
}
Controllers
public class SiteController : AuthorizeBaseController
{
private SiteContext db = new SiteContext();
public ActionResult Index()
{
return View(db.Sites.ToList());
}
:
_Layout.cshtml
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>#ViewBag.Title</title>
<link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<meta name="viewport" content="width=device-width" />
#Styles.Render("~/Content/css")
#Scripts.Render("~/bundles/modernizr")
#Styles.Render("~/Content/menubar.css")
</head>
<body>
#if (Request.IsAuthenticated)
{
<div id="header">
:
</div>
<div id="sidebar">
:
</div> <!-- sidebar -->
}
<div id="body">
#RenderBody()
</div>
#if (Request.IsAuthenticated)
{
<footer>
:
</footer>
}
#Scripts.Render("~/bundles/jquery")
#RenderSection("scripts", required: false)
</body>
</html>
It is because
[Authorize]
AuthorizeAttribute is MVC in built Attribute . make your own customize Attribute . You can have result as expect you .
Right now you can remove this Authorize Attribute from your every Controller and Action then your problem will solved .
The problem was caused by the SimpleMembershipProvider. In short, sometimes my Authorise filter was being called before InitializeSimpleMembershipAttribute().
I got my solution from this post which refers to a more detailed explanation on Scott Allen's blog

Use controller result from other mvc project within solution

I've got a new solution that contains several project 'groups'. Each group contains a Cloud Project, a Web Role Project and a Test Project. All Web Role Projects are MVC4.
One of the Web Roles is the entry point for the visitor. Parts of what the visitor will see, should come from the other Web Roles from within the solution. I cannot get it working and after spending several hours googling, I still cannot solve it. Maybe anyone can be helpful on this issue.
For example, I need to fill the header content with data that I can get from one of the other Web Roles. Let's call that controller HeaderController. The controller in the main project (HomeController) looks like this:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
//var headerController = new Generic.HeaderFooterContainer.Controllers.HeaderController();
//var foo = headerController.Index().ExecuteResult();
//ViewBag.Header = headerController.;
return View();
}
In the comments are some things I've tried to debug some things.
Here's the page layout of the main project. The body is still empty, as this problem is just about the page header which could be placed as html from the ViewBag or ViewData or even with an own #section.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta charset="utf-8" />
<title>#ViewBag.Title</title>
<link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<meta name="viewport" content="width=device-width" />
#Styles.Render("~/Content/css")
#Scripts.Render("~/bundles/modernizr")
</head>
<body>
<div class="headerHolder">
<div class="innertube">
<div id="header">
#ViewBag.Header
</div>
</div>
</div>
<div class="contentHolder">
<div class="innertube">
<div id="content">
#RenderBody()
</div>
</div>
</div>
<div class="footerHolder">
<div class="innertube">
<div id="footer">
</div>
</div>
</div>
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/bms")
#RenderSection("scripts", required: false)
</body>
</html>
The problem I face is that I just can't find out how I should make this work. How do I get the data in the header? A few things I've noticed are:
If I run the HeaderFooterContainer project in its dedicated solution, it works alright (I can get /Header and get the expected response displayed in the browser window)
If I run the Main project, I can see the home page, but if I ask for /Header, it can find the controller, but not the view. It's probably looking for the view in the main project although it is located in the HeaderFooterContainer project.