web-dev-qa-db-fra.com

ASP.NET MVC - en utilisant le même formulaire pour créer et éditer

Quelle est la meilleure approche pratique pour créer un formulaire utilisé à la fois pour créer de nouveaux modèles et pour modifier des modèles existants?

Y at-il des tutoriels que les gens peuvent me diriger dans la direction?

42
iasksillyquestions

Ne pas utiliser le même contrôleur action. Nouveau = HTTP PUT; edit = HTTP POST, donc ce sont deux choses différentes. Les deux actions peuvent et devraient être sur le même contrôleur, cependant.

J'aime l'idée d'utiliser un contrôle utilisateur pour des fonctionnalités communes (par exemple, des éditeurs) et de le placer dans des vues spécifiques à une action pour des éléments qui ne devraient apparaître que sur new ou edit, mais pas les deux. 

8
Craig Stuntz

(NerdDinner } _ va vraiment montrer le chemin.

Create.aspx

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<NerdDinner.Models.Dinner>" MasterPageFile="~/Views/Shared/Site.Master"  %>
<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
    Host a Nerd Dinner
</asp:Content>
<asp:Content ID="Create" ContentPlaceHolderID="MainContent" runat="server">
    <h2>Host a Dinner</h2>
    <% Html.RenderPartial("DinnerForm"); %>
</asp:Content>

Edit.aspx

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<NerdDinner.Models.Dinner>"
    MasterPageFile="~/Views/Shared/Site.Master" %>
<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
    Edit: <%:Model.Title %>
</asp:Content>
<asp:Content ID="Edit" ContentPlaceHolderID="MainContent" runat="server">
    <h2>Edit Dinner</h2>
    <% Html.RenderPartial("DinnerForm"); %>
</asp:Content>

DinnerForm.ascx

<%@ Language="C#" Inherits="System.Web.Mvc.ViewUserControl<NerdDinner.Models.Dinner>" %>
<script src="/Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<script src="/Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
<script src="/Scripts/MicrosoftMvcValidation.js" type="text/javascript"></script>
<% Html.EnableClientValidation(); %>
<%: Html.ValidationSummary("Please correct the errors and try again.") %>
   <% using (Html.BeginForm())
      { %>
   <fieldset>
       <div id="dinnerDiv">
           <%:Html.EditorForModel() %>
           <p>
               <input type="submit" value="Save" />
           </p>
       </div>
       <div id="mapDiv">
           <%: Html.EditorFor(m => m.Location) %>
       </div>
   </fieldset>
   <% } %>

Tenez compte du fait que ce formulaire utilise Html.EditorForModel(), une méthode innovante pour générer tous les champs à la fois, et vous devez en étudier les inconvénients avant de l’utiliser. Mais vous pouvez facilement prendre le reste de l'exemple pour séparer votre formulaire commun des vues de création et de modification.

Enfin, vous pouvez voir le code du contrôleur ici si vous êtes intéressé.

37
Mariano Desanze
11
E Rolnicki

Hypothèses

  1. C'est bien pour l'utilisateur de voir différentes URL pour différentes actions dans le navigateur. Par exemple, '/ pages/create' et '/ pages/edit/1'.

  2. C'est bien pour le développeur de n'avoir qu'une paire action + vue pour créer et éditer des pages, car elles sont généralement très similaires. (En outre, il est bon d’avoir un contrôleur par entité.)

Solution

L'enregistrement des routes par défaut est '{controller}/{action}/{id}' Nous pouvons ajouter deux règles supplémentaires avant celle-ci:

{controller}/create (doit pointer sur l'action 'CreateOrEdit')

{controller}/edit/{id} (doit également pointer sur l'action 'CreateOrEdit')

Nous pouvons maintenant avoir quelque chose comme ça:

public static void RegisterRoutes(RouteCollection routes)
{
  routes.MapRoute(
    name: "Create",
    url: "{controller}/create",
    defaults: new { controller = "Default", action = "CreateOrEdit" }
  );

  routes.MapRoute(
    name: "Edit",
    url: "{controller}/edit/{id}",
    defaults: new { controller = "Default", action = "CreateOrEdit" }
  );

  routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Default", action = "Index", id = UrlParameter.Optional }
  );
}

Alors maintenant, les requêtes de création et d'édition seront gérées par l'action 'CreateOrEdit'. D'autres iront par défaut.

Nous devons ensuite ajouter l'action 'CreateOrEdit' pour HttpGet et HttpPost dans notre contrôleur:

[HttpGet]
public ActionResult CreateOrEdit(int? id)
{
  return this.View(new CreateOrEditViewModelBuilder(this).Build(id));
}

[HttpPost]
public ActionResult CreateOrEdit(CreateOrEditViewModel сreateOrEditViewModel)
{
  if (this.ModelState.IsValid)
  {
    Page page = new CreateOrEditViewModelMapper(this).Map(сreateOrEditViewModel);

    if (сreateOrEditViewModel.Id == null)
      this.UnitOfWork.GetRepository<IPageRepository>().Create(page);

    else this.UnitOfWork.GetRepository<IPageRepository>().Edit(page);

    this.UnitOfWork.Save();
    return this.RedirectToAction("Index");
  }

  return this.View(сreateOrEditViewModel);
}

Et le dernier nous devons ajouter la vue nommée 'CreateOrEdit'. Nous pouvons utiliser 'this.Model.Id == null' pour savoir si nous créons ou modifions.

Résultat

Maintenant, nous n'avons pas de code en double et nous pouvons avoir des URL évidentes comme ceci:

/ pages (pour voir toutes les pages)

/ pages/create (pour créer une nouvelle page)

/ pages/edit/1 (pour éditer une page existante)

/ pages/delete/1 (pour supprimer une page existante)

J'espère que ça va aider quelqu'un!

5
Dmitry Sikorsky

Il pourrait s'agir (devrait être IMO) d'un contrôleur mais d'actions différentes. Assurez-vous également que les verbes HTTP appropriés sont associés aux actions appropriées. Suivez le tutoriel posté par E Rolnicki et vous serez sur votre chemin!

Bonne codage !!

1
Perpetualcoder

ce n'est pas toujours la meilleure pratique car cela dépend du cas, voici comment je l'ai fait

1/i a combiné les actions du contrôleur pour créer et éditer

public PartialViewResult Creedit(string id = null)
{
    if (id == null)
    {
        // Create new record (this is the view in Create mode)
        return PartialView();
    }
    else
    {
        // Edit record (view in Edit mode)
        Client x = db.ClientSet.Find(id);
        if (x == null) { return PartialView("_error"); }
        // ...
        return PartialView(x);
    }
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Creedit(Client x)
{
    if (x.id == null)
    {
        // insert new record
    }
    else
    {
        // update record
    }
}

2/i a combiné l'édition et la création de vues en une seule vue que j'appelle Creedit

// if you need to display something unique to a create view
// just check if the Model is null
@if(Model==null){
}

j'ai donc 1 vue et 2 actions (1 post et 1 get) au lieu de 2 vues et 4 actions.

1
Chtiwi Malek

Si l'entité possède une sorte de clé privée interne (par exemple, un membre "id" toujours> 0), vous pouvez utiliser/Edit/0 au lieu de/Create

0
Serge Wautier

Je mets le formulaire lui-même dans un contrôle utilisateur - par exemple, Views/Shared/WidgetForm.ascx. J'ai mis tous les champs de formulaire dans ce contrôle utilisateur, mais PAS les balises de formulaire elles-mêmes.

Les vues, par exemple Views/Widgets/New.aspx et Views/Widgets/Edit.aspx, contiennent les balises de formulaire et tous les "environs" - instructions pour remplir le formulaire, le titre de la page, etc. contrôle utilisateur à l'intérieur des balises de formulaire.

Le contrôle utilisateur prend simplement un objet Widget et affiche un formulaire basé sur les résultats. Il est donc important de définir des valeurs par défaut sensibles dans les nouvelles options de widget, mais vous le faites quand même, n'est-ce pas? ;)

0
Keith Williams

J'ai un système qui, à mon avis, fonctionne assez bien. Dans mes vues partagées, j'ai 2 formes génériques, Edit.aspx et New.aspx

Ensuite, dans mon dossier de vue spécifique, j'ai un contrôle nommé EditItems.ascx

Dans mon formulaire d'édition, j'ai les balises de formulaire et des boutons spécifiques pour l'édition et dans le nouveau formulaire, j'ai les balises de formulaire et les boutons spécifiques pour les nouveaux. Dans chacun, j'ai Html.RenderPartial ("EditItems.ascx")

De cette façon, votre contrôle utilisateur peut être fortement typé et pourtant vous réutilisez l'apparence des pages d'édition et des nouvelles pages.

Dans certains cas, votre nouvelle page peut avoir une présentation différente de celle de la page Modifier. Dans ce cas, ajoutez simplement "Edit.aspx" à votre dossier de vue spécifique.

Je trouve que cela me donne la meilleure combinaison de réutilisation tout en permettant une personnalisation complète si j'en ai besoin. Et comme pour les actions du contrôleur, oui, elles devraient être des actions séparées.

0
Trevor de Koekkoek

J'utilise quelque chose comme

[HttpGet]
public ActionResult EntityEdit(Guid id)
{
    return View();
}

et

[HttpGet]
public ActionResult EntityCreate()
{
   return View("EntityEdit");
}

Cela semble fonctionner correctement.

0
ChrisFox