Show results on a different page - c#

I have a blazor app that gets some inputs, calculates, and displays some outputs via Edit Form and single model.
Some of the inputs:
<EditForm Model="#model" OnValidSubmit="hundlevalidsubmit">
<div class="form-group">
<label>Full Name</label>
<InputText style="width: 25%" #bind-Value="model.name" class="form-control">
</InputText>
</div>
<div class="form-group">
<label>Type of Vessel</label>
<InputSelect style="width: 25%" #bind-Value="model.typeofvessel" class="form-control">
<option value="">Select the type of Vessel</option>
<option value="5">General Cargo Ship</option>
<option value="10">General Cargo Ship</option>
<option value="15">Roll on-roll off Ship</option>
<option value="20">Bulk Carrier</option>
</InputSelect>
</div>
<div class="form-group">
<label>Gross Tonnage</label>
<InputNumber style="width: 25%" #bind-Value="model.gt" class="form-control" />
</div>
</EditForm>
I then bind the results at the end of the page with
<EditForm Model="#model" OnValidSubmit="hundlevalidsubmit">
<div class="card-body">
<div class="row">
<div class="col">Full Name:</div>
<div class="col text-right">#model.name</div>
</div>
<div class="row">
<div class="col">EEXI Value:</div>
<div class="col text-right">#eexi.ToString("N")</div>
</div>
<div class="row">
<div class="col">Compliance:</div>
<div class="col text-right">#model.compliance</div>
</div>
</div>
</EditForm>
And display them with a calculate button at the end of the page with this:
<EditForm Model="#model" OnValidSubmit="hundlevalidsubmit">
<center>
<div class="checkbox">
<label>
<input name="cbPrivacy" id="cbPrivacy" type="checkbox"> I accept Dromon Bureau of Shipping (Privacy Policy).
</label>
</div>
<div class="col-lg-10 #*col-lg-offset-2*#" style="margin-bottom:20px;">
<button class="btn btn-default btn-medium">Cancel</button>
<button href="/Results" #onclick="#(() => { SendEmail(); })" style="background-color:green;" class="btn btn-primary btn-medium">
Calculate
</button>
#*href="/Results"*#
#* <p>Calculate</p>*#
</div>
</center>
</EditForm>
This is the code that does the calculations
#code {
public infomodel model = new infomodel();
public double eexi;
public void hundlevalidsubmit()
{
eexi = (int.Parse(model.typeofvessel) + model.gt);
if (eexi > 50)
{
model.compliance = "Yes";
}
else
{
model.compliance = "No";
}
}
Now I would like to display the results on a new page (results).
Please note this is my first app so limited technical knowledge.

Method 1:
You can pass parameters to page being navigated like this, if the values are few and not sensitive and are used only in one place
NavigationManager.NavigateTo($“NextPage/{Value1}/{Value2}”);
NextPage:
#page “/nextpage/{value1}/{value2}
[Parameter]
Public string Value1 {get; set;}
[Parameter]
Public string Value2 {get; set;}
Method 2:
It involves using DI to hold global data
Register service like this in program.cs
builder.Services.AddScoped<DataService>();
In first page:
#inject DataService dataService;
dataService.model = new model with calculated values
In second page:
#inject DataService dataService;
Use dataService.model to retrieve values
DataService.cs
Public class DataService{
public infomodel model{get; set;}
}

You can use startup.cs file for DI
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddScoped<DataService>();
}

This one is Blazor server-side .NET 6.0 project
Program.cs
using FinalExiiCalculator.Data;
using FinalExiiCalculator.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddSingleton<DataService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
**DataService.cs**
using FinalExiiCalculator.Models;
namespace FinalExiiCalculator.Services
{
public class DataService
{
public infomodel infomodel{get; set;} = new();
}
}
**infomodel.cs**
public class infomodel
{
public string compliance { get; set; } = "";
public string typeofvessel { get; set; } = "";
}
counter.razor
#page "/counter"
#using FinalExiiCalculator.Services
#inject DataService dataService
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: #currentCount</p>
<button class="btn btn-primary" #onclick="IncrementCount">Click me</button>
#code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
dataService.infomodel.typeofvessel = "My Vessel";
dataService.infomodel.compliance = "Yes";
}
}
FetchData.razor
#page "/fetchdata"
<PageTitle>Weather forecast</PageTitle>
#using FinalExiiCalculator.Data
#inject WeatherForecastService ForecastService
#using FinalExiiCalculator.Services
#inject DataService dataService
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from a service.</p>
#if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
#foreach (var forecast in forecasts)
{
<tr>
<td>#forecast.Date.ToShortDateString()</td>
<td>#forecast.TemperatureC</td>
<td>#forecast.TemperatureF</td>
<td>#forecast.Summary</td>
</tr>
}
</tbody>
</table>
<div>
<span>#dataService.infomodel.typeofvessel</span>
<span>#dataService.infomodel.compliance</span>
</div>
}
#code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
}
}

Related

Data not being passed from Razor page

I have a Blazor app that takes user input via a form field and puts it into a database.
However, the data is not being passed from the front end correctly:
Razor file
#using Blogs.Shared.Models
#page "/addpost"
#inject HttpClient Http
#inject NavigationManager NavigationManager
<h2>Create Post</h2>
<hr />
<div class="row">
<div class="col-md-4">
<form>
<div class="form-group">
<label for="Name" class="control-label">Titles</label>
<input for="Name" class="form-control" bind="#posts.title" />
</div>
<div class="form-group">
<label for="Address" class="control-label">Content</label>
<input for="Address" class="form-control" bind="#posts.content" />
</div>
<div class="form-group">
<input type="button" class="btn btn-default" onclick="#(async () => await tas())" value="Save" />
<input type="button" class="btn" onclick="#Cancel" value="Cancel" />
</div>
</form>
</div>
</div>
#functions {
public Posts posts = new();
protected async Task tas()
{
await Http.PostAsJsonAsync("api/Posts/Create", posts);
NavigationManager.NavigateTo("/listposts");
}
void Cancel()
{
NavigationManager.NavigateTo("/listposts");
}
}
What I would expect this to do, is when Save is pressed, the data from Title and Content is assigned to posts and then passed to my POST method:
Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Blogs.Shared.Models;
using Microsoft.AspNetCore.Mvc;
namespace Blogs.Server.Controllers
{
public class PostsController : Controller
{
private readonly IDataAccessProvider _dataAccessProvider;
private readonly ILogger _logger;
public PostsController(IDataAccessProvider dataAccessProvider, ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger("PostsController");
_dataAccessProvider = dataAccessProvider;
}
[HttpPost]
[Route("api/Posts/Create")]
public void Create(Posts post)
{
_logger.LogCritical("Data 1", post);
_logger.LogCritical("Data 2", post.content);
_logger.LogCritical("Data 3", post.title);
_dataAccessProvider.AddPosts(post);
}
}
}
All of my _logger.LogCritical lines just return blank, and when the write to the DB occurs it complains that title is empty (this field in my DB is set to NOT NULL).
Can anyone help as to why this is not working?
EDIT
I have updated the code to better match Antoine B's suggestions but its still not working:
#page "/newpost"
#using Blogs.Shared.Models
#inject HttpClient Http
#inject NavigationManager NavigationManager
<h1>#Title Post</h1>
<hr />
<EditForm Model="#posts" OnValidSubmit="SaveUser">
<DataAnnotationsValidator />
<div class="mb-3">
<label for="Name" class="form-label">Title</label>
<div class="col-md-4">
<InputText class="form-control" #bind-Value="posts.title" />
</div>
<ValidationMessage For="#(() => posts.title)" />
</div>
<div class="mb-3">
<label for="Address" class="form-label">Content</label>
<div class="col-md-4">
<InputText class="form-control" #bind-Value="posts.content" />
</div>
<ValidationMessage For="#(() => posts.content)" />
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Save</button>
<button class="btn btn-light" #onclick="Cancel">Cancel</button>
</div>
</EditForm>
#code {
protected string Title = "Add";
protected Posts posts = new();
protected async Task SaveUser()
{
await Http.PostAsJsonAsync("api/Posts/Create", posts);
Cancel();
}
public void Cancel()
{
NavigationManager.NavigateTo("/listposts");
}
}
To bind a value to an input component you must add an # before the bind property, like following #bind="posts.title" (see https://learn.microsoft.com/en-us/aspnet/core/blazor/components/data-binding?view=aspnetcore-6.0)
That's what's not working for you.
I also don't recommend you to use the #functions because it is not recommended by Microsoft for razor files (see https://learn.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-3.0#functions)
#code {
public Posts posts = new();
protected async Task tas()
{
await Http.PostAsJsonAsync("api/Posts/Create", posts);
NavigationManager.NavigateTo("/listposts");
}
void Cancel()
{
NavigationManager.NavigateTo("/listposts");
}
}
I hope it helped you

How do I display a View inside another View

I am practicing ASP and MVC by making a website like Twitter and I am having trouble with using a PartialView.
I have my User Home Page Controller here, which gets called from my Login Page Controller.
public class UserController : Controller
{
private readonly Twitter _twitter = null;
private readonly TwitterCloneDBContext _context;
public UserController(TwitterCloneDBContext context)
{
_twitter = new Twitter(context);
_context = context;
}
public ActionResult Index()
{
List<Tweet> tweets = _twitter.TweetList();
ViewBag.Tweets = tweets.Count;
ViewBag.Followers = 2;
ViewBag.Following = 3;
return View();
}
public PartialViewResult TweetList()
{
List<Tweet> tweetList = _twitter.TweetList();
return PartialView("TweetList",tweetList);
}
}
Here is the View for it.
#{
ViewData["Title"] = "Home Page";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using Microsoft.AspNetCore.Http;
#model IEnumerable<Tweet>
#if (Context.Session.GetString("FullName") != null)
{
<div class="text-center">
<h1 class="display-4">Welcome, #Context.Session.GetString("FullName")!</h1>
</div>
<hr />
<div class="row">
<div class="col-sm-2">
<div class="text-left">
<h4>Follow</h4>
<form asp-action="Search">
<div class="input-group mb-3">
<input type="text" name="searchName" class="form-control" placeholder="Search User" />
<div class="input-group-append">
<button class="btn btn-primary" type="submit">Search</button>
</div>
</div>
</form>
<span class="text-danger">#ViewBag.SearchFail</span>
</div>
<div>
<h6>#ViewBag.Tweets Tweets</h6>
<br />
<h6>#ViewBag.Followers Followers</h6>
<br />
<h6>#ViewBag.Following Following</h6>
</div>
</div>
<div class=border></div>
<div class="col-4">
<h4>Tweet your thoughts!</h4>
<form asp-action="Tweet">
<textarea rows="5" cols="100" name="message" class="form-control"> </textarea>
<br />
<input type="submit" value="Tweet" class="btn btn-primary" />
</form>
<span class=" text-danger">#ViewBag.ErrMessage</span>
<div>
#Html.PartialAsync("TwitterList",Model)
</div>
</div>
</div>
}
else
{
<h2>You are not Authorized!</h2>
<script type="text/javascript">
window.setTimeout(function () {
window.location.href = '/Home/UserLogin';
}, 1000);
</script>
}
The Partial Line is supposed to call TwitterList View which is working perfectly and displaying List as it is supposed to.
#model IEnumerable<TwitterClone.Models.Tweet>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.User_ID)
</th>
<th>
#Html.DisplayNameFor(model => model.Message)
</th>
<th>
#Html.DisplayNameFor(model => model.Created)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.User_ID)
</td>
<td>
#Html.DisplayFor(modelItem => item.Message)
</td>
<td>
#Html.DisplayFor(modelItem => item.Created)
</td>
</tr>
}
</tbody>
</table>
But when I call it using the #Html.PartialAsync() It fails by saying
System.NullReferenceException
HResult=0x80004003
Message=Object reference not set to an instance of an object.
Source=TwitterClone.Views
StackTrace:
at AspNetCore.Views_User_TweetList.<ExecuteAsync>d__0.MoveNext() in C:\Users\Cloud\source\repos\TwitterClone\TwitterClone\Views\User\TweetList.cshtml:line 19
and giving me this in the main page
See below Tweet Button, that is my Partial View, if I do the Partial View any other way I get NullReferenceException in both the places. This is the closes I've gotten to this and I cannot figure it out.
EDIT:
My ViewBags are in another Actions that are not relevant here. They basically print an error message.
The NullExceptions are gone, my Model is not empty when I open the Table View on its own. But its empty when being called through Partial. I suspect its because something isn't being called in the chain.
If I change my Index Action to like this,
public ActionResult Index()
{
List<Tweet> tweets = _twitter.TweetList();
ViewBag.Tweets = tweets.Count;
ViewBag.Followers = 2;
ViewBag.Following = 3;
return View("Index",tweets);
}
I get this error.
InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'System.Collections.Generic.List`1[TwitterClone.Models.Tweet]', but this ViewDataDictionary instance requires a model item of type 'TwitterClone.Models.Person'.
I have no Idea where ViewDataDictionary is setting Person to. The only place where I can think of that connects to UserController is my HomeController.
EDIT
This was a dumb mistake, I left out an #model Person in my Layout file. Sorry.
EDIT
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly TwitterCloneDBContext _context;
public HomeController(ILogger<HomeController> logger, TwitterCloneDBContext context)
{
_logger = logger;
_context = context;
}
//GET: Home/UserLogin
public ActionResult UserLogin()
{
return View();
}
//POST: Home/UserLogin
[Microsoft.AspNetCore.Mvc.HttpPost]
[ValidateAntiForgeryToken]
public ActionResult UserLogin(Person userLogin)
{
var login = _context.Person.Where(a => a.UserID.Equals(userLogin.UserID)
&& a.Password.Equals(userLogin.Password)).FirstOrDefault();
if (login != null)
{
Person session = _context.Person.SingleOrDefault(u => u.UserID == userLogin.UserID);
session.Active = 1;
HttpContext.Session.SetString("FullName", login.FullName);
HttpContext.Session.SetString("UserID", login.UserID);
_context.SaveChanges();
return RedirectToAction("Index", "User");
}
else
{
ViewBag.ErrMsg = "Invalid Credentials";
return View();
}
}
}
And its View
#model Person
#{
ViewData["Title"] = "UserLogin";
Layout = "~/Views/Shared/LoginLayout.cshtml";
}
<h1>Welcome!</h1>
<h4>Enter Credentials</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="UserLogin">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="UserID" class="control-label">User ID</label>
<input asp-for="UserID" class="form-control" />
<span asp-validation-for="UserID" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password" class="control-label">Password</label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Login" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
New User? <a asp-action="Signup">Signup</a>
</div>
What is
<span class="text-danger">#ViewBag.SearchFail</span>
I can't see ViewBag.SearchFail or ViewBag.ErrMessage in your action.
You have a big bug. Move redirect from Index view to action, otherwise it will be always running after creating the page.
public ActionResult Index()
{
if (Context.Session.GetString("FullName") != null)
{
List<Tweet> tweets = _twitter.TweetList();
.....
return View("Index", tweets);
}
RedirectToAction("Login", "Users")
}
Since you are using the same model for both, try to replace
#Html.PartialAsync("TwitterList",Model)
with
<partial name="TwitterList" />
and fix the model of both views
#model List<TwitterClone.Models.Tweet>
also fix the partial view
#if(#Model!=null && Model.Count >0)
{
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model[0].User_ID)
</th>
<th>
#Html.DisplayNameFor(model => model[0].Message)
</th>
......
}

ViewBag is not sending any value to the view

In ASP.NET Core MVC, I'm facing trouble creating a login panel, I'm using sessions after the user is logged into the account and I'm storing the session values inside the ViewBag. But the ViewBag does not get any value inside of it, it rather gets null value inside it.
Here's the controller
[HttpPost]
public IActionResult Login(userModel model)
{
var findValue = _context.users.Any(o => o.username == model.username);
var findValue2 = _context.users.Any(o => o.password == model.password);
if (findValue && findValue2)
{
HttpContext.Session.SetString("Username", model.username);
}
return View(model);
}
public IActionResult Index()
{
ViewBag.Username = HttpContext.Session.GetString("Username");
return View();
}
And here's the view
Index.cshtml
#model ComplaintManagement.Models.userModel
#{
ViewData["Title"] = "Portal";
}
<h1>Welcome #ViewBag.Username</h1>
Login.cshtml
#model ComplaintManagement.Models.userModel
#{
ViewData["Title"] = "Login";
}
<div class="row mb-3">
<div class="col-lg-4"></div>
<div class="col-lg-4 border login" style="background-color: #d3d1d1;">
<h4 class="mt-3 text-center">
<i class="fa fa-lg fa-user text-secondary"></i><br />
Login
</h4>
<hr />
<form method="post" asp-action="Index" asp-controller="Portal">
<div class="text-danger"></div>
<div class="text-warning">#ViewBag.Name</div>
<div class="form-group">
<label class="mt-4 asp-for=" username"">Username</label>
<input class="form-control" type="text" required="required" asp-for="username" />
<span></span>
</div>
<div class="form-group">
<label class="mt-4" asp-for="password">Password</label>
<input type="password" class="form-control" required="required" asp-for="password" />
<span></span>
</div>
<center>Don't have an account? <a asp-controller="Portal" asp-action="Register">Register here</a>.</center>
<center><button value="login" class="btn btn-primary mt-3 w-25 mb-3 align-content-center">Login</button></center>
</form>
</div>
<div class="col-lg-4"></div>
</div>
Session and state management in ASP.NET Core
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-3.1
Here is a demo How to use Session in ASP.NET Core.
1. Codes of Startup Configurations
AddSession in ConfigureServices, UseSession in Configure.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSession();
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSession();
app.UseStaticFiles();
....
}
2. Codes of Controller
public class AccountController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpGet]
public IActionResult Login()
{
return View();
}
[HttpPost]
public IActionResult Login(userModel model)
{
if (string.IsNullOrEmpty(model.username
) || string.IsNullOrEmpty(model.password))
{
return NotFound();
}
//var user = await _context.users.FirstOrDefaultAsync(x => x.username == model.username && x.password == model.password);
//if (user != null)
if (model.username.Equals("test") && model.password.Equals("123"))
{
HttpContext.Session.SetString("username", model.username);
}
else
ViewBag.error = "Invalid Account";
return View("Index");
}
[HttpGet]
public IActionResult Logout()
{
HttpContext.Session.Remove("username");
return RedirectToAction("Index");
}
}
3. Codes of View
<h3>Login Page</h3>
#ViewBag.error
<form method="post" asp-controller="account" asp-action="login">
<table border="0" cellpadding="2" cellspacing="2">
<tr>
<td>Username</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>Password</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td> </td>
<td><input type="submit" value="Login"></td>
</tr>
</table>
</form>
4. Codes of returning View when success
#using Microsoft.AspNetCore.Http;
<h3>Success Page</h3>
Welcome #Context.Session.GetString("username")
<br>
<a asp-controller="account" asp-action="logout">Logout</a>
Test result

Blazor Role Management Add Role trough UI (Crud)

I'm pretty new to blazor and have gotten myself in some doubt on adding roles to the database.
I have implemented to Identity role management and have a working system.
But now i want to add new roles trough the GUI instead of editing the database.
I have a razor page called RolesOverview.razor
On this page i have a input field and a button.
When i click this button i want to add the text to the roles manager and save it to the database.
This is my razor component
#page "/admin/roles"
#using Microsoft.AspNetCore.Identity
#inject RoleManager<IdentityRole> roleManager
<div class="jumbotron">
<!-- Roles Overview Group Box -->
<div class="row mb-5">
<div class="col-12">
<h1 class="display-6">Roles Options</h1>
<hr class="my-4" />
<div class="row" style="background-color:white; margin-bottom:10px; margin-top:10px;">
<div class="col-12">
<div class="card w-100 mb-3" style="min-width:100%;">
<div class="card-body">
<h5 class="card-title">Roles</h5>
<p class="card-text">
<div class="row">
<div class="col-1">
Role Name:
</div>
<div class="col-10">
<input type="text" style="min-width:100%;" placeholder="Role Type" />
</div>
<div class="col-1">
Add Role
</div>
</div>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Not Getting Saved...
#code {
private string CurrentValue { get; set; }
private async void AddRole()
{
if (CurrentValue != string.Empty)
{
if (!await roleManager.RoleExistsAsync(CurrentValue))
{
await roleManager.CreateAsync(new IdentityRole
{
Name = CurrentValue
});
}
}
}
}
I have no clue on what todo next.
I's it posible todo it with razor component or do i need to do it trough razor page?
Example would be perfect.
Regards Me!
Answer :
<div class="col-10">
<input value="#CurrentValue" #onchange="#((ChangeEventArgs __e) => CurrentValue =__e.Value.ToString())" />
#*<input type="text" style="min-width:100%;" placeholder="Role Type" />*#
</div>
<div class="col-1">
<a #onclick="AddRole" class="btn btn-primary" style="min-width:90px;">Add Role</a>
</div>
#code {
private string CurrentValue { get; set; }
private async void AddRole()
{
if (CurrentValue != string.Empty)
{
if (!await roleManager.RoleExistsAsync(CurrentValue))
{
await roleManager.CreateAsync(new IdentityRole
{
Name = CurrentValue
});
}
}
}
}
You can use RoleManager to create a new role by using the CreateAsync method:
if (!await roleMgr.RoleExistsAsync("RoleName"))
{
await roleManager.CreateAsync(new IdentityRole
{
Name = "RoleName"
});
}

RuntimeBinderException: Cannot perform runtime binding on a null reference

I'm making a create item page, and in this create item page there is a popup modal table where we can choose the type of UoM that we want. And normally when this form is submitted with all of the fields filled in, it saved the values into the database. But when the form is submitted with one or some or all of the fields not filled in, it supposed to give some error message that the fields are required. But it didn't and it shows this error.
These are my code
ItemController
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Threading.Tasks;
using CRMandOMS.Models;
using CRMandOMS.ViewModels;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
// For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace CRMandOMS.Controllers
{
public class ItemController : Controller
{
private readonly IItemRepository _itemRepository;
private readonly IUoMRepository _uoMRepository;
public ItemController(IItemRepository itemRepository, IUoMRepository uoMRepository)
{
_itemRepository = itemRepository;
_uoMRepository = uoMRepository;
}
// GET: /<controller>/
public ViewResult Index()
{
var model = _itemRepository.GetAll();
return View(model);
}
public ViewResult Details(Guid? id)
{
Item item = _itemRepository.GetById(id.Value);
return View(item);
}
[HttpGet]
public ViewResult Create()
{
ItemCreateViewModel itemCreateViewModel = new ItemCreateViewModel()
{
UoMs = _uoMRepository.GetAll()
};
return View(itemCreateViewModel);
}
[HttpPost]
public IActionResult Create(ItemCreateViewModel model)
{
if (ModelState.IsValid)
{
Item newItem = new Item
{
Name = model.Name,
Price = model.Price,
UoMId = model.UoMId
};
_itemRepository.Insert(newItem);
return RedirectToAction("Details", new { id = newItem.Id });
}
return View();
}
}
}
Create
#model CRMandOMS.ViewModels.ItemCreateViewModel
#{
ViewData["Title"] = "Item Create";
}
<h2>Item Create</h2>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a asp-controller="Item" asp-action="Index">Item</a></li>
<li class="breadcrumb-item active" aria-current="page">Create</li>
</ol>
</nav>
<form enctype="multipart/form-data" asp-controller="Item" asp-action="Create" method="post" class="mt-3">
<div class="form-group row">
<label asp-for="Name" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Name" class="form-control" placeholder="Name" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label asp-for="Price" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Price" class="form-control" placeholder="Price" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label asp-for="UoMId" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="UoMId" id="uomid" class="form-control" hidden />
<div class="input-group mb-3">
<input id="uomname" type="text" class="form-control" placeholder="UoM" aria-label="UoM" aria-describedby="button-uom" disabled>
<div class="input-group-append">
<button class="btn btn-outline-success" type="button" id="button-uom" data-toggle="modal" data-target="#uoMLookupTableModal">Select UoM</button>
</div>
</div>
<span asp-validation-for="UoMId" class="text-danger"></span>
</div>
</div>
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group row">
<div class="col-sm-2"></div>
<div class="col-sm-10">
<a asp-controller="Item" asp-action="Index" class="btn btn-light">Back</a>
<button type="submit" class="btn btn-success">Create</button>
</div>
</div>
</form>
#{
await Html.RenderPartialAsync("_UoMLookup");
}
#section scripts {
<script>
$(document).ready(function () {
var uoMTable = $("#uoMTable").DataTable({
"columnDefs": [
{
"targets": [0],
"visible": false
}
],
"order": [[1, "asc"]]
});
$('#uoMTable tbody').on('click', 'tr', function () {
if ($(this).hasClass('table-success')) {
$(this).removeClass('table-success');
}
else {
uoMTable.$('tr.table-success').removeClass('table-success');
$(this).addClass('table-success');
}
});
$("#getUoM").click(function () {
var uomdata = uoMTable.row('.table-success').data();
//alert(uomdata[0]);
$('#uomid').val(uomdata[0]);
//alert(uomdata[1]);
$('#uomname').val(uomdata[1]);
});
});
</script>
}
_UoMLookup
<div class="modal fade" id="uoMLookupTableModal" tabindex="-1" role="dialog" aria-labelledby="uoMLookupTableModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<table id="uoMTable" class="table table-striped table-bordered table-bordered nowrap" style="width:100%">
<thead>
<tr>
<td>Id</td>
<td>Name</td>
<td>Description</td>
</tr>
</thead>
<tbody>
#foreach (UoM uom in Model.UoMs)
{
<tr>
<td class="uom-id">#uom.Id</td>
<td class="uom-name">#uom.Name</td>
<td>#uom.Description</td>
</tr>
}
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light" data-dismiss="modal">Cancel</button>
<button id="getUoM" type="button" class="btn btn-success" data-dismiss="modal">Select</button>
</div>
</div>
</div>
</div>
ItemCreateViewModel
using CRMandOMS.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace CRMandOMS.ViewModels
{
public class ItemCreateViewModel
{
[Required]
[MaxLength(100, ErrorMessage = "Name cannot exceed 100 characters")]
public string Name { get; set; }
[Required(ErrorMessage = "{0} is required")]
[Range(1000, 999999999)]
public int Price { get; set; }
[Required]
public Guid UoMId { get; set; }
public IEnumerable<UoM> UoMs { get; set; }
public string PhotoPath { get; set; }
}
}
In the HTTP POST Create method (ItemController) if the model is not valid (so ModelState.IsValid == false) you are not passing a model to your View. Ensure passing a valid model, as shown in the controller methods tutorial.
But when the form is submitted with one or some or all of the fields not filled in, it supposed to give some error message that the fields are required. But it didn't and it shows this error.
You do not have a reference to validation scripts, make sure you have _ValidationScriptsPartial.cshtml in Shared folder, then modify your code:
#section scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
<script>
//...
</script>
}
For the error on your page, just like other community has said, it is likely that the model state is invalid and it execute return View() without returning any data to create view.
However,your partial view does not allow the Model.UoMs to be null.
In your Create Post action, if the model contains UoMs, you could just use
return View(model)
otherwise ,assign UoMs data to model like what you have done in Create Get action, then return it to view.
You could always use a breakpoint on the Post action to debug the result.

Categories

Resources