need your thoughts on how the following can be implemented.
I need to process on the OnPost a list of <string,file> objects and do some business logic after, but at this stage, I am unsure of how to implement it either using RazorPages logic or what was usually done with MVC.
At this stage what I can't do is to get on OnPostAsync the picked values on selectedCompany and inputFile, which I was expecting to come from the formData.
Any thoughts? TY
View
(...)
<form method="post" enctype="multipart/form-data">
<div class="border p-3 mt-4">
<table class="table" id="filesToProcess">
<tr>
<td>
<div class="mb-3">
<select>
<option value="" name="selectedCompany">Pick a company ...</option>
#foreach (var company in Model.Companies)
{
<option value="#company.Id">#company.Name</option>
}
</select>
</div>
</td>
<td>
<div class="mb-3">
<div>
<input type="file" name="inputFile" />
</div>
</div>
<td>
</tr>
</table>
</div>
<button type="submit" class="btn btn-primary" style="width:150px">Calculate</button>
</form>
(...)
ViewModel
public class CalculatorModel : PageModel
{
private IHostingEnvironment _environment;
private ICompaniesService _companyService;
private IIndicatorsService _indicatorsService;
//To be presented on the front-end
public List<CompanyDto> Companies { get; set; }
//The initial idea would be that one row on the table of the front-end corresponds to one instance of IndicatorsRequest
[BindProperty]
public List<IndicatorsRequest> IndicatorsRequests { get; set; }
public class IndicatorsRequest
{
public Guid CompanyGuid { get; set; }
public IFormFile File { get; set; }
public List<IndicatorDto> CalculatedIndicators { get; set; }
}
public CalculatorModel(IHostingEnvironment environment, ICompaniesService companyService, IIndicatorsService indicatorsService)
{
_environment = environment;
_companyService = companyService;
_indicatorsService = indicatorsService;
}
public async Task OnGet()
{
Companies = await this._companyService.GetCompanies();
}
public async Task OnPostAsync(IFormCollection formData)
{
try
{
var selectedCompanies = formData.Where(f => f.Key.Contains("selectedCompany")).ToList();
var inputFiles = formData.Where(f => f.Key.Contains("inputFile")).ToList();
//Do some business logic with provided companies and files;
}
catch (Exception ex)
{
throw ex;
}
}
}
Solution - https://www.learnrazorpages.com/razor-pages/model-binding#binding-complex-collections
View
The '0' on [0].CompanyGuid and [0].File has obviously to be an auto-generated sequencial number.
<td>
<div class="mb-3">
<select name="[0].CompanyGuid"> <<<<<<<<<<<<<<<
<option value="">Pick a company ...</option>
#foreach (var company in Model.Companies)
{
<option value="#company.Id">#company.Name</option>
}
</select>
</div>
</td>
<td>
<div class="mb-3">
<div>
<input type="file" name="[0].File" /> <<<<<<<<<<<<<
</div>
</div>
<td>
ViewModel
public async Task OnPostAsync(List<IndicatorsRequest> requests)
{
Console.WriteLine(requests.ElementAt(0).CompanyGuid);
}
public class IndicatorsRequest
{
public Guid CompanyGuid { get; set; }
public IFormFile File { get; set; }
}
Related
Faced such peculiar problem. For a better understanding, I will try to describe in more detail.
I have two metod in ArticleController:
[HttpGet]
public async Task<IActionResult> Create()
{
var sources = await _sourceServices.GetSourceNameAndId();
var listSources = new List<SourceNameAndIdModel>();
foreach (var source in sources)
{
listSources.Add(_mapper.Map<SourceNameAndIdModel>(source));
}
var viewModel = new ArticleCreateViewModel()
{
SourceNameAndIdModels = listSources
};
return View(viewModel);
}
[HttpPost]
public async Task<IActionResult> Create(ArticleCreateViewModel viewModel)
{
await _articleService.CreateArticle(_mapper.Map<ArticleDTO>(viewModel));
return RedirectToAction("Index", "Article");
}
As you can see, in the Get-method, I get the names and ids of all Sources from the database via _sourceService in the form of IEnumerable :
public class SourceNameAndIdDTO
{
public Guid Id { get; set; }
public string Name { get; set; }
}
Next, I enumerate them in a foreach loop and add each SourceNameAndIdDTO object to the List listSources I created before:
public class SourceNameAndIdModel
{
public string Name { get; set; }
public Guid Id { get; set; }
}
Next, I create an instance of the ArticleCreateViewModel model, which I will use further in the View:
public class ArticleCreateViewModel
{
public Guid Id { get; set; } = Guid.NewGuid();
public string Title { get; set; }
public string Description { get; set; }
public string Body { get; set; }
public Guid SourceId { get; set; }
public DateTime CreationDate { get; set; }
public List<SourceNameAndIdModel> SourceNameAndIdModels { get; set; }
}
And I assign to the field public List SourceNameAndIdModels { get; set; } List listSources values:
var viewModel = new ArticleCreateViewModel()
{
SourceNameAndIdModels = listSources
};
You can see this in the controller code I posted above. Next, I send the viewModel to the View.
Code of my View:
#model ArticleCreateViewModel
<div class="container">
<h2>Edit article</h2>
<div class="row gx-5">
<form asp-controller="Article" asp-action="Create" asp-antiforgery="true" method="post">
<div>
<input type="hidden" asp-for="Id" />
<div class="mb-3">
<label class="col-sm-2 col-form-label" asp-for="Title"></label>
<input class="form-control" type="text" asp-for="Title">
</div>
<div class="mb-3">
<label class="col-sm-2 col-form-label" asp-for="Description"></label>
<textarea class="form-control" asp-for="Description"></textarea>
</div>
<div class="mb-3">
<label class="col-sm-2 col-form-label" asp-for="Body"></label>
<textarea class="form-control" asp-for="Body"></textarea>
</div>
<div class="dropdown">
<a class="btn btn-secondary dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Source
</a>
<ul class="dropdown-menu">
#foreach (var item in #Model.SourceNameAndIdModels)
{
<li><select class="dropdown-item" asp-for="SourceId" asp-items="#item.Id">#item.Name</select></li>
}
</ul>
</div>
<div class="mb-3">
<label class="col-sm-2 col-form-label" asp-for="CreationDate"></label>
<input type="datetime-local" class="form-control" asp-for="CreationDate">
</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
And finally, in this place of the code, in which I want to set the Source of the article I am creating, I have an error:
enter image description here
Can you please tell me how in my case to make friends with my code with dropdown on my request? What am I doing wrong?
Using asp-items in this way is incorrect.
The Select Tag Helper asp-items specifies the option elements
Details and example:
https://learn.microsoft.com/...netcore-6.0#the-select-tag-helper
I have problem in edit view. When I debug my code, after Id is passed from ShowCategory view to EditPostGroup method in controller, it works correctly, but after page loads, Id zero is sent to the Edit method in controller and throws an error
Object reference not set to an instance of an object
This is my code: this method is in the PostRepository and get postGroup with Id from database :
public PostGroupViewModel GetPostGroupById(int postGroupId)
{
var postGroup = _context.PostGroups.Find(postGroupId);
PostGroupViewModel postGroupViewModel = new PostGroupViewModel()
{
GroupId = postGroup.GroupId,
GroupTitle = postGroup.GroupTitle,
GroupImageName = postGroup.GroupImageName,
IsDelete = postGroup.IsDelete,
ParentId = postGroup.ParentId
};
return postGroupViewModel;
}
These are Get and Post edit method in CategoryController:
[HttpGet]
public IActionResult EditPostGroup(int id)
{
var groups = _postService.GetGroupForManagePost();
ViewData["Groups"] = new SelectList(groups, "Value", "Text");
var postGroupViewModel = _postService.GetPostGroupById(id);
return View(postGroupViewModel);
}
[HttpPost]
public IActionResult EditPostGroup(PostGroup postGroup, IFormFile imgPostGroupUp)
{
if (!ModelState.IsValid)
return View("EditPostGroup");
_postService.UpdatePostGroup(postGroup, imgPostGroupUp);
return RedirectToAction("ShowCategory", "Category");
}
This is 'ShowCategory' View, It sends PostGroupId to Edit action method:
#{
ViewData["Title"] = "Category List";
}
<div class="row">
<div class="col-lg-12">
<h4 style="color:darkblue;" >Category List</h4>
</div>
<!-- /.col-lg-12 -->
</div>
<br />
<div class="row">
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-body">
<br />
<div class="col-md-12" style="margin: 10px 0;">
<a asp-controller="Category" asp-action="CreateCategory" asp-area="Admin" class="btn btn-outline btn-success">Create Category</a>
</div>
<br />
<br />
<br />
<table class="table table-striped table-bordered table-hover dataTable no-footer text-center" id="dataTables-example" aria-describedby="dataTables-example_info">
<thead class="table-success black-ribon">
<tr>
<th>Title</th>
<th>Parent Category</th>
<th>Image</th>
<th>Operations</th>
</tr>
</thead>
<tbody>
#foreach (var item in #ViewBag.CategoryList)
{
<tr>
<td>
#item.GroupTitle
</td>
<td>
#item.ParentId
</td>
<td>
<img class="thumbnail" style="width:40px;height:40px;" src="/img/postGroup/#item.GroupTitle/#item.GroupImageName" alt="">
</td>
<td>
<a href="/Admin/Category/EditPostGroup/#item.GroupId" class="btn btn-primary btn-md">
Edit
</a>
</td>
</tr>
}
</tbody>
</table>
</div>
<!-- /.panel-body -->
</div>
<!-- /.panel -->
</div>
<!-- /.col-lg-12 -->
</div>
This is the EditPostGroupView:
#model DataLayer.Models.ViewModels.PostGroup.PostGroupViewModel
#{
ViewData["Title"] = "Post Edit";
//Layout = "~/Areas/Admin/Views/Shared/_AdminLayout.cshtml";
}
<div class="row">
<form method="post" asp-action="EditPostGroup" enctype="multipart/form-data">
#* <input type="hidden" asp-for="GroupId" />*#
#*<input type="hidden" asp-for="Post.PostImageName"/>*#
<div class="col-md-8">
<h2>Category List</h2>
<hr/>
<div class="form-group">
<label>Title</label>
<input type="text" asp-for="GroupTitle" class="form-control">
<span asp-validation-for="GroupTitle"></span>
</div>
<div class="form-group">
<label>ParentId</label>
<select class="form-control" asp-for="ParentId" asp-items="#(ViewData["Groups"] as SelectList)">
<option value="">-- please select --</option>
</select>
<span asp-validation-for="ParentId"></span>
</div>
</div>
<div class="col-md-4">
<p></p>
<img id="imgPost" class="thumbnail" src="img/postGroup/#Model.GroupTitle/#Model.GroupImageName"/>
<div class="form-group">
<label>Select Image</label>
<input type="file" name="imgPostGroupUp" id="imgPostGroupUp">
</div>
</div>
<input type="submit" value="Edit" class="btn btn-success"/>
</form>
</div>
#section scripts
{
<script>
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('#imgPost').attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]);
}
}
$("#imgPostUp").change(function () {
readURL(this);
});
</script>
}
This is the Routing in Program.cs:
app.MapControllerRoute(
name: "MyArea",
pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
This is PostGroup Model:
public class PostGroup
{
[Key]
public int GroupId { get; set; }
//public int? SubGroupId { get; set; }
public string GroupTitle { get; set; }
[MaxLength(50)]
public string? GroupImageName { get; set; }
public bool IsDelete { get; set; }
[Display(Name = "Category")]
public int? ParentId { get; set; }
[ForeignKey("ParentId")]
public List<PostGroup>? PostGroups { get; set; }
[InverseProperty("PostGroup")]
//[NotMapped]
public List<Post>? Posts { get; set; }
}
This is postGroupViewModel:
public class PostGroupViewModel
{
public int GroupId { get; set; }
public string GroupTitle { get; set; }
[MaxLength(50)]
public string? GroupImageName { get; set; }
public bool IsDelete { get; set; }
public int? ParentId { get; set; }
}
What is the problem?
Could it be a problem with the Program.cs and middleware?
I changed EditPostGroup(PostGroup postGroup, IFormFile imgPostGroupUp) method to
EditPostGroup(PostGroupViewModel postGroupViewModel, IFormFile imgPostGroupUp),but there is still a problem.
You can use a query string instead of passing the id in .net core:
[HttpGet]
public async Task<IActionResult> Edit()
{
string id = HttpContext.Request.Query["id"].ToString();
var model = await _service.FindAsync(int.Parse(id));
return View(model);
}
in razor view:
<a href="/service/edit?id=#item.ServiceId">
Also, using # in the src of a file in razor view causes this problem. remove it or give it a value.
<img id="img-master" class="thumbnail" src="#" />
to
<img id="img-master" class="thumbnail" src="/images/#Model.ImageEdit" />
I have been racking the web trying to find an easy solution (or at least a regular walk through) of what I would think would be a common activity, but I have been unable to find any. I have the following classes:
public class Attribute
{
...
public int Id { get; set; }
[Required]
public string AttributeName { get; set; }
public string Description { get; set; }
public bool UserCanChangeValue { get; set; }
public ICollection<AttributeValue> Values { get; set; }
}
class AttributeValue
{
public int Id { get; set; }
[ForeignKey("AssociatedAttribute")]
public int AssociatedAttributeId { get; set; }
public virtual Attribute AssociatedAttribute { get; set; }
[Required]
public string Value { get; set; }
public bool PromptChange { get; set; } = true;
public bool ShowonInvoice { get; set; } = false;
}
Then in a standard controller:
...
// GET: AttributesController/Create
public ActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,Description,Values")] Attribute newAttribute)
....
What I am confused on is how to create this where an attribute has to have 1 or more values that need to be created at the same time, and dynamically an attribute can have a variable number. Ideally I would add rows to the form for each attribute value to create.
Some scaffolded details:
...
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="AttributeName" class="control-label"></label>
<input asp-for="AttributeName" class="form-control" />
<span asp-validation-for="AttributeName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<input asp-for="Description" class="form-control" />
<span asp-validation-for="AttributeName" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="UserCanChangeValue" /> #Html.DisplayNameFor(model => model.UserCanChangeValue)
</label>
</div>
<div>
<table>
<thead>
<tr>
<th>Value</th>
<th>Prompt Change</th>
<th>Show on Invoice</th>
</tr>
</thead>
<tbody>
INSERT DYNAMIC NEW VALUE FORM?
</tbody>
</table>
</div>
Firstly, you set PromptChange and ShowonInvoice default value, if you make checkbox/radio input unchecked, they will pass the default value(PromptChange= true and ShowonInvoice=false) to backend. But actually, I prefer do not set default value which can control true/false by checked or unchecked. Not sure which way is what you want, just modify by yourself.
Then your class AttributeValue need public access modifier.
Here is a simple working demo you could follow:
Model:
namespace MvcProj5_0.Models
{
public class Attribute
{
public int Id { get; set; }
[Required]
public string AttributeName { get; set; }
public string Description { get; set; }
public bool UserCanChangeValue { get; set; }
public ICollection<AttributeValue> Values { get; set; }
}
public class AttributeValue
{
public int Id { get; set; } //add a primary key
[ForeignKey("AssociatedAttribute")]
public int AssociatedAttributeId { get; set; }
public virtual Attribute AssociatedAttribute { get; set; }
[Required]
public string Value { get; set; }
public bool PromptChange { get; set; }
public bool ShowonInvoice { get; set; }
}
}
View(Create.cshtml):
#model MvcProj5_0.Models.Attribute
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="AttributeName" class="control-label"></label>
<input asp-for="AttributeName" class="form-control" />
<span asp-validation-for="AttributeName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<input asp-for="Description" class="form-control" />
<span asp-validation-for="AttributeName" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="UserCanChangeValue" /> #Html.DisplayNameFor(model => model.UserCanChangeValue)
</label>
</div>
<button type="button" onclick="AddRow()">Add row</button> //add button
<div>
<table>
<thead>
<tr>
<th>Value</th>
<th>Prompt Change</th>
<th>Show on Invoice</th>
</tr>
</thead>
<tbody id="AttributeValueList" data-count="0"> //add id and data-count
INSERT DYNAMIC NEW VALUE FORM?
</tbody>
</table>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
JS in Create.cshtml:
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script>
function AddRow()
{
var countVal = parseInt($('#AttributeValueList').attr('data-count'));
var html = '';
html += '<tr>';
html += '<td><input type="text" name="Values[' + countVal + '].Value" class="form-control"/></td>';
html += '<td><input type="checkbox" name="Values[' + countVal + '].PromptChange" value="true"/></td>';
html += '<td><input type="checkbox" name="Values[' + countVal + '].ShowonInvoice" value="true"/></td>';
html += '</tr>';
$('#AttributeValueList').append(html);
countVal += 1;
$('#AttributeValueList').attr('data-count', countVal);
}
</script>
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(
[Bind("Id,AttributeName,Description,UserCanChangeValue,Values")] MvcProj5_0.Models.Attribute attribute)
{
if (ModelState.IsValid)
{
_context.Add(attribute);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(attribute);
}
I have two models and one join table.
I would like to use the same view to edit and create new purchase order while adding items in the same form.
PurchaseOrder Model:
public class PurchaseOrder
{
public int ID { get; set; }
[Required]
public string Requester { get; set; }
public int WorkOrder { get; set; }
[Required]
public string WorkSite { get; set; }
public string Equipment { get; set; }
public string Operator { get; set; }
public string Remarks { get; set; }
public ICollection<PurchaseOrderItem> Items { get; set; }
}
Item Model:
public class Item
{
public int ID { get; set; }
public string PartNumber { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
[Required]
public int Quantity { get; set; }
public string Remarks { get; set; }
public ICollection<PurchaseOrderItem> PurchaseOrders { get; set; }
}
Join Table Entity
public class PurchaseOrderItem
{
public int PurchaseOrderID { get; set; }
public int ItemID { get; set; }
public PurchaseOrder PurchaseOrder { get; set; }
public Item Item { get; set; }
}
PurchaseOrdersController Edit:
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var purchaseOrder = await _context.PurchaseOrders
.Include(x => x.Items)
.ThenInclude(x => x.Item)
.Where(x => x.ID == id)
.AsNoTracking()
.SingleOrDefaultAsync();
if (purchaseOrder == null)
{
return NotFound();
}
return View(purchaseOrder);
}
Edit Post Method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, PurchaseOrder order)
{
if (id != order.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
var po = _context.PurchaseOrders.FirstOrDefault(i => i.ID == order.ID);
po.Requester = order.Requester;
po.WorkOrder = order.WorkOrder;
po.WorkSite = order.WorkSite;
po.Equipment = order.Equipment;
po.Operator = order.Operator;
po.Remarks = order.Remarks;
_context.Update(po);
foreach (var i in order.Items)
{
var item = _context.Items.FirstOrDefault(n => n.ID == i.Item.ID);
item.PartNumber = i.Item.PartNumber;
item.Name = i.Item.Name;
item.Description = i.Item.Description;
item.Quantity = i.Item.Quantity;
item.Remarks = i.Item.Remarks;
_context.Update(item);
}
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!PurchaseOrderExists(order.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(order);
}
Edit View
#model OrderTrackingApp.Models.PurchaseOrder
#{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>PurchaseOrder</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="ID" />
<div class="form-group">
<label asp-for="Requester" class="control-label"></label>
<input asp-for="Requester" class="form-control" />
<span asp-validation-for="Requester" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="WorkOrder" class="control-label"></label>
<input asp-for="WorkOrder" class="form-control" />
<span asp-validation-for="WorkOrder" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="WorkSite" class="control-label"></label>
<input asp-for="WorkSite" class="form-control" />
<span asp-validation-for="WorkSite" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Equipment" class="control-label"></label>
<input asp-for="Equipment" class="form-control" />
<span asp-validation-for="Equipment" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Operator" class="control-label"></label>
<input asp-for="Operator" class="form-control" />
<span asp-validation-for="Operator" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Remarks" class="control-label"></label>
<input asp-for="Remarks" class="form-control" />
<span asp-validation-for="Remarks" class="text-danger"></span>
</div>
<div class="form-group">
<label class="control-label">Order Items</label>
<table>
<tbody>
#{ int i = 0;}
<tr>
#foreach (var OrderItem in Model.Items)
{
<td>
<input type="hidden" value="OrderItem[#i].ItemID" asp-for="#OrderItem.ItemID"
class="form-control" />
<input type="text" value="OrderItem[#i].Item.PartNumber" asp-for="#OrderItem.Item.PartNumber" class="form-control" />
<input type="text" value="OrderItem[#i].Item.Name" asp-for="#OrderItem.Item.Name" />
</td>
i++;
}
</tr>
</tbody>
</table>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
I have two problems currently with the Edit:
The label for the item populates as follows: OrderItem[0].Item.Name instead of Value
When the code reaches the foreachloop to iterate over items, it is throwing a null exception.
You are getting name only as for newly added child items, you don't have ids. and getting null values as in returned data for relational table ids of child data are not initialized.
you can solve this issue in tow ways as per suitability:
Add child rows from view directly and update ids in model, so you
will always have ids in data and you will not need face null ids
issue.
Alternatively, You may need to add new values of child table first
then use new ids in relational data manually before saving changes.
The problem is model binding, it is based on the name attribute. Change the code like below:
#{ int i = 0;}
<tr>
#foreach (var OrderItem in Model.Items)
{
<td>
<input type="hidden" name="Items[#i].Item.ID" asp-for="#OrderItem.Item.ID"
class="form-control" />
<input type="text" name="Items[#i].Item.PartNumber" asp-for="#OrderItem.Item.PartNumber" class="form-control" />
<input type="text" name="Items[#i].Item.Name" asp-for="#OrderItem.Item.Name" class="form-control" />
</td>
i++;
}
</tr>
I have a form that takes the preference of students as input, namely UserID, name of the course, the category to which it belongs and their priority to that course. These values are then stored in a table in the database.
There is an additional column in the database called Priority points whose value depends on whether or not the student has taken a course in the mentioned category earlier.
For example: If the student has already taken a course in Quality, then the priority point should be set to 10 and if he has not taken a course in Quality, the priority point is set to 40.
This should be automatically set by comparing the input category with the history of the user.
The history is a separate table in the database.
I am working with Asp.net razor pages (C#)
This is what I have done so far:
C#:
public class AddPreferenceModel : PageModel
{
public CUser User { get; set; }
public CStudent Student { get; set; }
[BindProperty]
public CPreference Preference { get; set; }
public COffers Offers { get; set; }
private readonly IHostingEnvironment _environment;
private readonly CDataContext _context;
private readonly IEmailService _emailServices;
private readonly IUserService _UserService;
public AddPreferenceModel(IUserService UserService, CDataContext context, IHostingEnvironment IHostingEnvironment, IUserService IUserService, IEmailService emailService)
{
_environment = IHostingEnvironment;
_UserService = UserService;
_context = context;
_emailServices = emailService;
User = _UserService.GetUser();
}
public CHistory History { get; set; }
public IList<CPreference> Pref { get; set; }
public IList<CHistory> Hist { get; set; }
public IList<COffers> Off { get; set; }
public IList<CStudent> stud { get; set; }
public ActionResult OnGet()
{
if (User.Role != Role.Admin && User.Role != Role.Student)
{
return RedirectToPage("/Denied");
}
return Page();
}
[HttpPost]
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
foreach (var item in stud)
{
foreach(var item_hist in Hist)
{
if(Preference.Category == History.History)
{
General.AddPreference = false;
return RedirectToPage("/Denied");
}
else
{
General.AddPreference = true;
_context.Preference.Add(Preference);
await _context.SaveChangesAsync();
}
}
}
return RedirectToPage("IndexPref");*/
}
}
And on HTML side:
<div class="Custom-Content Custom-Max-Width-800">
<h2 class="h2">Add your preference</h2>
<p>
Please check all the details before submitting
</p>
<form enctype="multipart/form-data" id="orderForm" method="post">
<div class="col-md-12"><hr /></div>
<h4 class="h4 Custom-H4-Less-Margin-Bottom">Preference</h4>
<div class="Custom-Form col-sm-6">
User
<input asp-for="Preference.StudentUser" type="text" value="" required="" />
</div>
<div class='col-md-12'><hr /></div>
<div class="Custom-Form col-sm-4">
Course Name
<input asp-for="Preference.name" type="text" value="" required="" />
</div>
<div class="Custom-Form col-sm-4">
Category
<select asp-for="Preference.Category" required>
<option value="Economics">Economics</option>
<option value="Technology">Technology</option>
<option value="Quality">Quality</option>
<option value="Extracurricular">Extracurricular</option>
</select>
</div>
<div class="Custom-Form col-sm-4">
Priority
<select asp-for="Preference.Priority" required>
<option value="High">High</option>
<option value="Medium">Medium</option>
<option value="Low">Low</option>
</select>
</div>
#if(General.AddPreference == false)
{
<div class="Custom-Form col-sm-4">
<input asp-for="Preference.Priopoints" type="text" value="10" />
</div>
}
#if(General.AddPreference == true)
{
<div class="Custom-Form col-sm-4">
<input asp-for="Preference.Priopoints" type="text" value="40" />
</div>
}
<br />
<div class="Custom-Form col-md-12">
<input type="submit" value="Add" class="Custom-Button" />
</div>
</form>
</div>
I added a class called General with static variables in it to act as Global variable.
This is what it looks like:
public class General
{
public static COffers GOffers = new COffers();
public static COffers Offers
{
get { return GOffers; }
set { GOffers = value; }
}
public static bool GAddPreference = true;
public static bool AddPreference
{
get { return GAddPreference; }
set { GAddPreference = value; }
}
}
I am obviously getting an error message. But I am not sure how else to implement the idea. Any inputs on how to improvise/change would be appreciated. Thanks.
The error is at
foreach (var item in stud)
The error is System.NullReferenceException: 'Object reference not set to an instance of an object.'