J'ai une page avec un rendu côté serveur à l'aide de rasoir, où vous pouvez ajouter quelques éléments de différentes listes, remplir certains champs et créer une demande à partir de cette page.
Chaque fois qu'un élément est ajouté/extrait d'une liste, j'envoie un message avec le bouton d'envoi à une action spécifique, par exemple. "Sélectionné par le client". Je le fais parce que je dois recréer des composants de vue supplémentaires pour l'élément ajouté. Dans ces méthodes, je voudrais ajouter des objets ajoutés au contexte de la base de données, donc sur soumettre, je peux simplement dire SaveChanges()
et ne pas avoir à tout assembler de la même manière. Mais dans .net, le contexte de base de données est défini par demande et il est conseillé de le conserver de cette façon. Dans ce cas, comment puis-je stocker ces objets d'entité temporaires entre les demandes, si plus tard, si quelqu'un décide de les soumettre, je peux dire SaveChanges()
ou les ignorer autrement?
J'aimerais avoir quelque chose comme ça:
public IActionResult CustomerAdded(int customerId)
{
var customer = _context.Customers.First(c => c.IdCustomer == customerId);
var message = _context.Messages.First(m => m.IdMessage = _idMessage);
message.Customers.Add(customer);
return View();
}
public IActionResult ItemAdded(int itemId)
{
var item = _context.Items.First(c => c.IdItem == itemId);
var message = _context.Messages.First(m => m.IdMessage = _idMessage);
message.Items.Add(item);
return View();
}
public IActionResult Submit()
{
_context.SaveChanges();
return View();
}
Si cela n’est pas possible, je pensais alors à ajouter des éléments individuels dans chaque méthode et à les enregistrer là-bas, puis à soumettre le dernier élément final. Mais si quelqu'un ferme son navigateur sans le soumettre, j'ai des données incomplètes dans ma base de données. Je devrais exécuter une sorte de travail pour les supprimer et cela semble être trop pour une tâche aussi simple.
Ce n'est pas une bonne idée d'utiliser les ressources du serveur pour suivre les changements dans de tels scénarios. Dans des scénarios tels que le panier, la liste ou l'édition par lot, il est préférable de suivre les modifications côté client.
Votre exigence de générer des vues côté serveur ne signifie pas que vous deviez suivre les modifications dans DbContext
. Obtenez la vue d'index et créez la vue à partir du serveur, mais suivez les modifications sur le client. Ensuite, pour enregistrer, publiez toutes les données sur le serveur pour enregistrer les modifications en fonction des informations de suivi dont vous disposez.
Le mécanisme de suivi des modifications côté client dépend des exigences et du scénario. Par exemple, vous pouvez suivre les modifications à l'aide d'entrées HTML, vous pouvez suivre les modifications à l'aide d'un cookie, vous pouvez suivre les modifications à l'aide d'objets JavaScript dans la mémoire du navigateur, comme des scénarios angulaires.
Voici ce post, je vais montrer un exemple en utilisant des entrées HTML et la liaison de modèle. Pour en savoir plus sur ce sujet, jetez un oeil à cet article de Phill Haack: Model Binding To A List .
Dans l'exemple suivant, je décris un scénario d'édition de liste pour une liste de clients. Pour faire simple, je suppose:
Pour mettre en œuvre le scénario ci-dessus, vous devez ensuite créer les modèles, actions et vues suivants:
Modèle <T> pouvant être suivi
Cette classe est un modèle qui nous aide dans le suivi côté client et l’édition de listes:
public class Trackable<T>
{
public Trackable() { }
public Trackable(T model) { Model = model; }
public Guid Index { get; set; } = Guid.NewGuid();
public bool Deleted { get; set; }
public bool Added { get; set; }
public T Model { get; set; }
}
Modèle client
Le modèle client:
public class Customer
{
[Display(Name ="Id")]
public int Id { get; set; }
[StringLength(20, MinimumLength = 1)]
[Required]
[Display(Name ="First Name")]
public string FirstName { get; set; }
[StringLength(20, MinimumLength = 1)]
[Required]
[Display(Name ="Last Name")]
public string LastName { get; set; }
[EmailAddress]
[Required]
[Display(Name ="Email Name")]
public string Email { get; set; }
}
Vue Index.cshtml
La vue Index est chargée de rendre List<Trackable<Customer>>
. Lors du rendu de chaque enregistrement, nous utilisons la vue RowTemplate
. La même vue que nous utilisons lors de l'ajout d'un nouvel élément.
Dans cette vue, nous avons un bouton d'envoi pour enregistrer et un bouton pour ajouter de nouvelles lignes qui appelle l'action Créer à l'aide de ajax.
Voici la vue d'index:
@model IEnumerable<Trackable<Customer>>
<h2>Index</h2>
<form method="post" action="Index">
<p>
<button id="create">New Customer</button>
<input type="submit" value="Save All">
</p>
<table class="table" id="data">
<thead>
<tr>
<th>
Delete
</th>
<th>
@Html.DisplayNameFor(x => x.Model.FirstName)
</th>
<th>
@Html.DisplayNameFor(x => x.Model.LastName)
</th>
<th>
@Html.DisplayNameFor(x => x.Model.Email)
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
await Html.RenderPartialAsync("RowTemplate", item);
}
</tbody>
</table>
</form>
@section Scripts{
<script>
$(function () {
$('#create').click(function (e) {
e.preventDefault();
$.ajax({
url: 'Create',
method: 'Get',
success: function (data) {
$('#data tbody tr:last-child').after(data);
},
error: function (e) { alert(e); }
});
});
});
</script>
}
Vue RowTemplate.cshtml
Cette vue est responsable du rendu d'un enregistrement client. Dans cette vue, nous rendons d'abord la Index
dans une zone masquée, puis définissons un préfixe [index]
pour les champs, puis nous rendons les champs, y compris les index, les ajouts, les suppressions et les ID de modèle:
Voici la vue RowTemplate:
@model Trackable<Customer>
<tr>
<td>
@Html.HiddenFor(x => x.Index)
@{Html.ViewData.TemplateInfo.HtmlFieldPrefix = $"[{Model.Index}]";}
@Html.HiddenFor(x => x.Index)
@Html.HiddenFor(x => x.Model.Id)
@Html.HiddenFor(x => x.Added)
@Html.CheckBoxFor(x => x.Deleted)
</td>
<td>
@Html.EditorFor(x => x.Model.FirstName)
@Html.ValidationMessageFor(x => x.Model.FirstName)
</td>
<td>
@Html.EditorFor(x => x.Model.LastName)
@Html.ValidationMessageFor(x => x.Model.LastName)
</td>
<td>
@Html.EditorFor(x => x.Model.Email)
@Html.ValidationMessageFor(x => x.Model.Email)
</td>
</tr>
CustomerController
public class CustomerController : Controller
{
private static List<Customer> list;
}
Il aura les actions suivantes.
[GET] Index Action
Dans cette action, vous pouvez charger des données à partir de la base de données et les transformer en List<Trackable<Customer>>
, puis les transmettre à la vue Index
.
[HttpGet]
public IActionResult Index()
{
if (list == null)
{
list = Enumerable.Range(1, 5).Select(x => new Customer()
{
Id = x,
FirstName = $"A{x}",
LastName = $"B{x}",
Email = $"A{x}@B{x}.com"
}).ToList();
}
var model = list.Select(x => new Trackable<Customer>(x)).ToList();
return View(model);
}
[GET] Create Action
Cette action est responsable du retour du nouveau modèle de ligne. Il sera appelé par un bouton dans la vue index en utilisant ajax:
[HttpGet]
public IActionResult Create()
{
var model = new Trackable<Customer>(new Customer()) { Added = true };
return PartialView("RowTemplate", model);
}
[Action POST] d'indexation
Cette action est responsable de la réception de l'élément suivi du client et de sa sauvegarde. Le modèle qu'il reçoit est List<Trackable<Customer>>
. Il supprime d'abord les messages d'erreur de validation pour les lignes supprimées. Supprime ensuite ceux qui sont à la fois supprimés et ajoutés. Ensuite, vérifie si l'état du modèle est valide, tente d'appliquer les modifications à la source de données.
Les éléments ayant la propriété Deleted
en tant que true sont supprimés, les éléments ayant Added
en tant que true et Deleted
en tant que false sont de nouveaux éléments et le reste des éléments est modifié. Ensuite, sans avoir besoin de charger tous les éléments de la base de données, en utilisant simplement une boucle for, appelez db.Entry
pour chaque élément et définissez leur état, puis enregistrez les modifications.
[HttpPost]
public IActionResult Index(List<Trackable<Customer>> model)
{
//Cleanup model errors for deleted rows
var deletedIndexes = model.
Where(x => x.Deleted).Select(x => $"[{x.Index}]");
var modelStateDeletedKeys = ModelState.Keys.
Where(x => deletedIndexes.Any(d => x.StartsWith(d)));
modelStateDeletedKeys.ToList().ForEach(x => ModelState.Remove(x));
//Removing rows which are added and deleted
model.RemoveAll(x => x.Deleted && x.Added);
//If model state is not valid, return view
if (!ModelState.IsValid)
return View(model);
//Deleted rows
model.Where(x => x.Deleted && !x.Added).ToList().ForEach(x =>
{
var i = list.FindIndex(c => c.Id == x.Model.Id);
if (i >= 0)
list.RemoveAt(i);
});
//Added rows
model.Where(x => !x.Deleted && x.Added).ToList().ForEach(x =>
{
list.Add(x.Model);
});
//Edited rows
model.Where(x => !x.Deleted && !x.Added).ToList().ForEach(x =>
{
var i = list.FindIndex(c => c.Id == x.Model.Id);
if (i >= 0)
list[i] = x.Model;
});
//Reditect to action index
return RedirectToAction("Index");
}
Qu'en est-il des formulaires dynamiques avec javascript et utilisant type="hidden"
ou visibility
Ou en utilisant TempData avec des redirections et en réutilisant ces données dans d'autres vues (formulaire) en tant que input type="hidden"
Couler:
Form1 ->
La méthode du contrôleur enregistre les données dans TempData et redirige vers Form2 View/Or ViewData et renvoie Form2 View? ->
Form2 a TempData inséré dans le formulaire sous des entrées masquées ->
Soumettre les deux à la fois