There is a simple customer database. It has 2 tables, one of which is a client with information and 2 is a table with its mailboxes. The foreign key links these tables by client id . There is such a code to add to the backend in the database

 public async Task<Unit> Handle(CreateCommand request, CancellationToken cancellationToken) { var entity = new Customer { CompanyName = request.CompanyName, Country = request.Country, City = request.City, Address = request.Address, ZipCode = request.ZipCode, }; _context.Customers.Add(entity); await _context.SaveChangesAsync(cancellationToken); foreach (var createCustEmail in request.Emails) { _context.CustomerEmails.Add(new CustomerEmail() { CustomerId = entity.CustomerId, Email = createCustEmail.Email, }); } await _context.SaveChangesAsync(cancellationToken); return Unit.Value; } 

The question is how to add client-side emails to Razor Pages?

  • Why do you need this? - Yaroslav
  • @Yaroslav is all logical, one user may have several emails. I try to implement it on a simple site - retreatretreat
  • It's not entirely clear what your question is. You apparently know how to save to the database, you know, using MediatR. And for you the question is how to pack it into working with the Razor Page? - AK
  • @retreatretreat, maybe, but why in the Razor Page I can not understand. - Yaroslav
  • @AK how to make this addition in Razor pages, on the client side. The problem is that how to add this foreach code in cshtml - retreatretreat

1 answer 1

I have several different solutions that work out different aspects of multiforms, the question is really quite voluminous, so I recommend that you do the same: before you make the real application do a few test cases to play around and see some aspects.

First, you can start with the application in which your email model will represent a simple line:

 public class Customer { public int Id { get; set; } public string Title { get; set; } public string[] Phones { get; set; } } 

This is sometimes very convenient, in this case I recommend to look at how to store such structures in the database using the Entity Framework.

We will start with two full-fledged models (i.e., the phone needs to store not only the number itself, but also let's say type - work / home / fax)

Contact model (you can mentally replace with Customer):

 public class Contact { public Contact() { this.Phones = new List<Phone>(); } public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public virtual ICollection<Phone> Phones { get; set; } } 

Phone model (only key fields are shown):

 public class Phone { public int Id { get; set; } public int ContactId { get; set; } public virtual Contact Contact { get; set; } public string Number { get; set; } } 

Next, we stupidly create a CRUD controller using scaffolding. You have probably seen such preparations many times, there is nothing difficult in them, but alas, they do not know how to multi-elements.

What should be done. First, add a block to the form to display a div with phone numbers:

  <div id="phoneList"> @Html.EditorFor(x => x.Phones) <p> <a href="javascript:void(0);" class="addRowPhone"><span class="glyphicon glyphicon-plus"></span>Добавить номер</a> </p> </div> 

Entire file form Create:

 @model WebApplication1.Models.Contact @{ ViewBag.Title = "Create"; } <h2>Create</h2> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h4>Contact</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) <div class="form-group"> @Html.LabelFor(model => model.FirstName, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.FirstName, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.LastName, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.LastName, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.LastName, "", new { @class = "text-danger" }) </div> </div> <div id="phoneNumbers"> @Html.EditorFor(x => x.Phones) </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Create" class="btn btn-default" /> </div> </div> </div> } <div> @Html.ActionLink("Back to List", "Index") </div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") } 

For this construction to work, you need to add the Views \ Contacts \ EditorTemplates folder and place the Phone.cshtml file in it (which will determine how to display our editing element:

 @model WebApplication1.Models.Phone <div class="phoneRow"> @Html.HiddenFor(x => x.Id) @Html.HiddenFor(x => x.ContactId) <p> <label>Phone Number</label> @Html.TextBoxFor(x => x.Number) </p> <a href="javascript:void(0);" class="remRowPhone"><span class="glyphicon glyphicon glyphicon-trash"></span>Удалить</a> </div> 

Total, something turns out like:

project structure

And it looks something like this (it didn’t decorate with css styles, as the project is educational, so it looks ugly):

Edit form

Read the primer on this topic to understand how it works: Overriding display and editing templates

In asp.net core, it is customary to create forms using tag-helpers and analogs for Html. Editor I don’t know, but when I mixed two styles and wrote Html. Editor, it worked. Well, it's just ugly, too dissimilar style.

But the main problem is related to the fact that the DisplayTemplates / EditorTemplates templates for razor pages do not work , even in 2.1 — at least when I tried various options in / Pages and / Views / Shared; none, alas; (

That's why I started to show on classic asp, especially as the projects are ready;)

What pitfalls are still waiting for you. When you receive data from the form - everything is fine with you, but when you start saving phones while editing - you need to determine which of the phones were deleted, which were added and which changed - and accordingly, the deleted ones - removed from the database, new ones added, and change others.

This is a separate big topic that drinks lots of blood. I will send you to this and this issue here, but be prepared to spend a few days on this insidious topic, in this thread I simply denote the problem.

With GraphDiff, you can reduce the code to something like this:

  // POST: Contacts/Edit/5 [HttpPost] [ValidateAntiForgeryToken] public ActionResult Edit(Contact contact) { if (!this.ModelState.IsValid) return this.View(contact); var entity = db.Contacts.Include(x=>x.Phones).SingleOrDefault(x => x.Id == contact.Id); if (entity == null) return this.HttpNotFound(); this.db.UpdateGraph(contact, map => map.OwnedCollection(p => p.Phones)); this.db.SaveChanges(); return this.RedirectToAction("Index"); } 

And on vanilla c # code will be four times more.

Another point will be how to take your classes to MediatR, see for yourself on a specific project.

And one more thing I missed - javascript on the add / delete buttons. I did not do it in a test project, and it differs for vanilla js / jQuery / vue / etc - so write yours in place.