代码之家  ›  专栏  ›  技术社区  ›  Ken-F

要用新编辑的MVC索引(增强的视图模型)

  •  1
  • Ken-F  · 技术社区  · 6 年前

    (编辑) 我有一个带有products(products)的表,它显示在一个名为productcatalog的视图中,用户可以在该视图中找到products表中的所有产品。我使用的是标准(索引)视图。

    我现在想要实现的是这样一个场景:可以选择(单击)目录中的单个产品,并显示产品的详细视图(类似于crud中的标准编辑/详细信息方法)。 然而 ,在这个details视图(productdetails)中,我需要增强模型,因为用户将确定变量,如订单数量,这些变量应与产品(主要是productid)中的属性一起发布到新表(lineitems)中,以最终创建订单。

    我一直未能做到这一点。不确定我是否需要使用mutliple视图模型或者做一些其他我不知道的花哨的事情。

    总之,问题是如何从索引到细节,以及如何从细节到将不同的视图模型发布到某个数据库。所有这些都和英孚有关。

    模型:

        public class Products
    {
        public int                                                      ID                                  { get; set; }
        public int                                                      CategoryID                          { get; set; }
        public string                                                   ProductName                         { get; set; }
        public string                                                   ProductDescription                  { get; set; }
        public string                                                   ProductPicturePath                  { get; set; }
        public string                                                   UnitCost                            { get; set; }
        public string                                                   UnitPrice                           { get; set; }
        public string                                                   LowestUnitPrice                     { get; set; }
        public string                                                   SubscriptionPrice                   { get; set; }
        public string                                                   UnitMargin                          { get; set; }
        public string                                                   UnitProfit                          { get; set; }
        public bool                                                     InCatalog                           { get; set; }
    }
    
    
        public class ProductViewModel
    {
        public int                                                      ProductID                           { get; set; }
    
        public string                                                   ProductName                         { get; set; }
    
        public string                                                   ProductDescription                  { get; set; }
    
        public string                                                   ProductPicturePath                  { get; set; }
    
        [RegularExpression(@"^\((\d{3}?)\)$", ErrorMessage              = "Du brauchst die Anzahl nicht ausschreiben - verwende Ziffern.")]
        [Display(Name                                                   = "Bestellmenge")]
        public string                                                   SubscriptionQuantity                { get; set; }
    
        [Display(Name                                                   = "Lieferrhytmus")]
        public string                                                   SubscriptionCadenceCategory         { get; set; }
    
        public string                                                   SubscriptionCadenceValue            { get; set; }
    
        [Display(Name                                                   = "Preis im Abonnement")]
        public string                                                   SubscriptionPrice                   { get; set; }
    
        public bool                                                     IsSingleOrder                       { get; set; }
    
        [RegularExpression(@"^\((\d{3}?)\)$", ErrorMessage              = "Du brauchst die Anzahl nicht ausschreiben - verwende Ziffern.")]
        [Display(Name                                                   = "Bestellmenge")]
        public string                                                   Quantity                            { get; set; }
    
        [Display(Name =                                                 "Preis pro Einheit")]
        public string                                                   UnitPrice                           { get; set; }
    
        public DateTime                                                 ActivityDateTime                    { get; set; }
    
        public string                                                   ActivityLatitude                    { get; set; }
    
        public string                                                   ActivityLongitude                   { get; set; }
    
        public string                                                   ActivityLocation                    { get; set; }
    
    
    }
    

    视图

    @using freshNclean.Models
    @model IEnumerable<freshNclean.Models.Products>
    @{
        ViewBag.Title = "Sortiment";
    }
    <div id="productCatalogContainer" class="container">
        <div id="productCatalogHeaderSection" class="headerSection">
            <h1 id="productCatalogHeaderTitle" class="headerTitle">
                @ViewBag.Title
            </h1>
            <i id="productCatalogHeaderIcon" class="headerIcon fas fa-gem" aria-hidden="true"></i>
        </div>
    <!-- table section -->
        <section id="productCatalogListPartialSection" class="table">
            <div id="productCatalogSeparatorSection" class="separatorSection">
                <hr id="productCatalogSeparator" class="separator" />
            </div>
            <div id="productCatalog" class="productTableSection row">
                 @foreach (var item in Model)
                {
                    if (item.InCatalog == true)
                    {
                        <a id="productCatalogProductArea" class="tableArea col-xs-offset-1 col-xs-10 col-sm-offset-1 col-sm-10 col-md-offset-2 col-md-3 col-lg-offset-2 col-lg-3" href="@Url.Action("ProductDetails", "freshNclean", new { id = item.ID })">
                            @Html.HiddenFor(modelItem => item.ID, new { @class = "tableField col-xs-12 col-sm-12 col-md-12 col-lg-12" })
                            <img id="productCatalogProductImage" class="tableImage col-xs-12 col-sm-12 col-md-12 col-lg-12" src="@Url.Content(item.ProductPicturePath)" alt="Produktbild" />
                            <div id="productCatalogProductNameField" class="tableField col-xs-12 col-sm-12 col-md-12 col-lg-12">
                                @Html.DisplayFor(modelItem => item.ProductName)
                            </div>
                            <div id="productCatalogProductDescriptionField" class="tableField col-xs-12 col-sm-12 col-md-12 col-lg-12">
                                @Html.DisplayFor(modelItem => item.ProductDescription)
                            </div>
    
                            <div id="productCatalogLowestUnitPriceField" class="tableField col-xs-12 col-sm-12 col-md-12 col-lg-12">
                                ab @Html.DisplayFor(modelItem => item.LowestUnitPrice)
                            </div>
                        </a>
                    }
                }
            </div>
    
            <div id="productCatalogListPartialMenuSeparatorSection" class="separatorSection">
                <hr id="productCatalogListPartialMenuSeparator" class="separator" />
            </div>
            @Html.ActionLink("zum Warenkorb", "ShowShoppingCart", "", htmlAttributes: new { @class = "formButton col-xs-offset-1 col-xs-10 col-sm-offset-1 col-sm-10 col-md-offset-3 col-md-6 col-lg-offset-3 col-lg-6" })
        </section>
    </div>
    <!-- link back to menu -->
    <div id="productCatalogReturnToMenuSection" class="linkSection">
        @Html.ActionLink("zurück zum Menü", "Profile", "", htmlAttributes: new { @id = "productCatalogReturnToMenuButton", @class = "link" })
    </div>
    </div>
    @section Scripts {
        @Scripts.Render("~/bundles/jqueryval")
    <!-- Google Places -->
        <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBYK8aBCsb1dFrzXqIgUq07ZwO3w3_fGCs&libraries=places&callback=initAutocomplete" async defer></script>
    }
    

    产品详细信息(视图)

    @model freshNclean.Models.Products
    @{
        ViewBag.Title = "Sortiment";
    }
    <div id="productCatalogContainer" class="container">
    <div id="productCatalogHeaderSection" class="headerSection">
        <h1 id="productCatalogHeaderTitle" class="headerTitle">
            @ViewBag.Title
        </h1>
        <i id="productCatalogHeaderIcon" class="headerIcon fas fa-gem" aria-hidden="true"></i>
    </div>
    <!-- form -->
    <section id="productDetailsForm" class="form">
        @using (Html.BeginForm("ShowProduct", "freshNclean", FormMethod.Post, new { @id = "productDetailsFormContainer", @class = "form-horizontal", @role = "form" }))
        {
            @Html.AntiForgeryToken()
    <!-- user activities (hidden) -->
            @Html.HiddenFor(m => m.ActivityLatitude, new { @id = "productDetailsActivityLatitudeField", @class = "userActivityField" })
            @Html.HiddenFor(m => m.ActivityLongitude, new { @id = "productDetailsActivityLongitudeField", @class = "userActivityField" })
            @Html.HiddenFor(m => m.ActivityLocation, new { @id = "productDetailsActivityLocationField", @class = "userActivityField" })
    <!-- autopopulate user activity fields with location -->
            <script>
                $(document).ready(function defaultValue() {
                    if (!navigator.geolocation) return;
                    navigator.geolocation.getCurrentPosition(function (userCoordinates) {
                        geocoder = new google.maps.Geocoder();
                        userLatLng = new google.maps.LatLng(userCoordinates.coords.latitude, userCoordinates.coords.longitude);
                        document.getElementById('productDetailsActivityLatitudeField').value = userCoordinates.coords.latitude;
                        document.getElementById('productDetailsActivityLongitudeField').value = userCoordinates.coords.longitude;
                        geocoder.geocode({ 'latLng': userLatLng }, function (results, status) {
                            if (status == google.maps.GeocoderStatus.OK) {
                                var result = results[0];
                                locationPlaceholder = "";
                                for (var i = 0, len = result.address_components.length; i < len; i++) {
                                    var addressComponent = result.address_components[i];
                                    if (addressComponent.types.indexOf("locality") >= 0) locationPlaceholder = addressComponent.long_name;
                                }
                                if (locationPlaceholder != '') {
                                    document.getElementById('productDetailsActivityLocationField').value = locationPlaceholder;
                                }
                            }
                        });
                    });
                });
            </script>
    <!-- form: product display -->
            <div id="productDetailsProductDisplaySeparatorSection" class="separatorSection">
                <hr id="productDetailsProductDisplaySeparator" class="separator" />
            </div>
            <div id="productDetailsProductDisplaySection" class="formSection row">
                @Html.HiddenFor(m => m.ProductID, new { @id = "productDetailsProductIDField", @class = "tableField col-xs-12 col-sm-12 col-md-12 col-lg-12" })
                <img id="productDetailsProductImage" class="tableImage col-xs-12 col-sm-12 col-md-12 col-lg-12" src="@Model.ProductPicturePath" alt="Produktbild" />
                <div id="productDetailsProductName" class="tableField col-xs-12 col-sm-12 col-md-12 col-lg-12">
                    @Html.DisplayFor(m => m.ProductName)
                </div>
                <div id="productDetailsProductDescriptionField" class="tableField col-xs-12 col-sm-12 col-md-12 col-lg-12">
                    @Html.DisplayFor(m => m.ProductDescription)
                </div>
    <!-- define subscription quantity -->
                <a id="productDetailsSubscriptionMinusButton" class="tableButton col-xs-offset-2 col-xs-2 col-sm-offset-2 col-sm-2 col-md-offset-3 col-md-2 col-lg-offset-3 col-lg-2">
                    -
                </a>
                @Html.TextBoxFor(m => m.SubscriptionQuantity, new { @id = "productDetailsSubscriptionQuantityField", @class = "tableField col-xs-offset-1 col-xs-2 col-sm-offset-1 col-sm-2 col-md-offset-0 col-md-2 col-lg-offset-0 col-lg-2" placeholder = "0" })
                <a id="productDetailsSubscriptionPlusButton" class="tableButton col-xs-offset-1 col-xs-2 col-sm-offset-1 col-sm-2 col-md-offset-0 col-md-2 col-lg-offset-0 col-lg-2">
                    +
                </a>
                @Html.LabelFor(m => m.SubscriptionCadenceCategory, new { @id = "productDetailsSubscriptionCadenceCategoryLabel", @class = "tableLabel" })
                @Html.TextBoxFor(m => m.SubscriptionCadenceCategory, new { @id = "productDetailsSubscriptionCadenceCategoryField", @class= "tableField" })
                @Html.LabelFor(m => m.SubscriptionCadenceValue, new { @id = "productDetailsSubscriptionCadenceValueLabel", @class = "tableLabel" })
                @Html.TextBoxFor(m => m.SubscriptionCadenceValue, new { @id = "productDetailsSubscriptionCadenceValueField", @class= "tableField" })
    
            </div>
    
            <div id="productCatalogListPartialMenuSeparatorSection" class="separatorSection">
                <hr id="productCatalogListPartialMenuSeparator" class="separator" />
            </div>
            @Html.ActionLink("zum Warenkorb", "ShowShoppingCart", "", htmlAttributes: new { @class = "formButton col-xs-offset-1 col-xs-10 col-sm-offset-1 col-sm-10 col-md-offset-3 col-md-6 col-lg-offset-3 col-lg-6" })
        </section>
    </div>
    <!-- link back to menu -->
    <div id="productCatalogReturnToMenuSection" class="linkSection">
        @Html.ActionLink("zurück zum Menü", "Profile", "", htmlAttributes: new { @id = "productCatalogReturnToMenuButton", @class = "link" })
    </div>
    </div>
    @section Scripts {
        @Scripts.Render("~/bundles/jqueryval")
    <!-- Google Places -->
        <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBYK8aBCsb1dFrzXqIgUq07ZwO3w3_fGCs&libraries=places&callback=initAutocomplete" async defer></script>
    }
    

    控制器

            // GET: /freshNclean/ProductDetails
        public ActionResult ShowProduct(int? id)
        {
            // define variables
            var userID                                                  = User.Identity.GetUserId();
            DateTime nowUTC                                             = DateTime.Now.ToUniversalTime();
            DateTime nowLocal                                           = DateTime.Now.ToLocalTime();
            if (id                                                      == null)
            {
                // track user activity: get method includes activity name and timestamp without location
                var NOPRODUCTID                                         = new UserActivities
                {
                    UserID                                              = userID,
                    ActivityName                                        = "ProductDetails_NoProductID",
                    ActivityTimeStampUTC                                = nowUTC,
                    ActivityLatitude                                    = "n/a",
                    ActivityLongitude                                   = "n/a",
                    ActivityLocation                                    = "n/a"
                };
                DATADB.UserActivityList.Add(NOPRODUCTID);
                DATADB.SaveChanges();
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Products model                                              = DATADB.ProductList.Find(id);
            if(model == null)
            {
                // track user activity: get method includes activity name and timestamp without location
                var PRODUCTSMODELFAILURE                                = new UserActivities
                {
                    UserID                                              = userID,
                    ActivityName                                        = "ProductDetails_ProductsModelFailure",
                    ActivityTimeStampUTC                                = nowUTC,
                    ActivityLatitude                                    = "n/a",
                    ActivityLongitude                                   = "n/a",
                    ActivityLocation                                    = "n/a"
                };
                DATADB.UserActivityList.Add(PRODUCTSMODELFAILURE);
                DATADB.SaveChanges();
                return HttpNotFound();
            }
            // track user activity: get method includes activity name and timestamp without location
            var LOADED                                                  = new UserActivities
            {
                UserID                                                  = userID,
                ActivityName                                            = "ProductDetails_Loaded",
                ActivityTimeStampUTC                                    = nowUTC,
                ActivityLatitude                                        = "n/a",
                ActivityLongitude                                       = "n/a",
                ActivityLocation                                        = "n/a"
            };
            DATADB.UserActivityList.Add(LOADED);
            DATADB.SaveChanges();
            return View(model);
        }
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   Mathieu VIALES Pavel    6 年前

    注意:由于技术原因,我无法运行此代码。因此,它可能包含未捕获的错误。让我知道这些事情。

    所以,这里有一个非常完整的例子。 其思路如下:

    • 我们称之为索引视图
    • 我们从数据库中获取所有用户,并将列表存储在viewmodel中(如果没有它,我们可以只传递 PersonEntity )
    • 我们显示的视图列出了使用html表的所有人员
    • 当我们点击 Details 链接我们被发送到 DetailRow 行动。这个 person.Id 作为get参数传递。
    • 详行 视图我们查询数据库,但只请求 人物实体 符合 Id 作为参数传递。
    • 我们要求实体框架包括 Detail 属性使用 .Include 方法。
    • 我们返回我们得到的结果,这将是一个 人物实体 映射到视图模型之后。
    • 我们显示返回的 Person 详行 看法

    粗体字是我认为直接回答你问题的那些。 在本例中,我假设您使用了实体框架,如果没有,您将需要“转换”对 Context 类到某种类型的DAO,或者如果您是一个肮脏的人,则为内联SQL;-)

    控制器和数据层的代码(理想情况下,每个类都在自己的文件中)

    public class KenFExample : Controller
    {
        // GET
        public IActionResult Index()
        {
            using (Context context = new Context())
            {
                // Get the whole list of basic entries
                IEnumerable<SimplePersonViewModel> rows = context.Persons.Select(r => new SimplePersonViewModel(r));
    
                // Construct a new view model with these entries
                BaseListingViewModel model = new BaseListingViewModel(rows);
    
                return View(model);
            }
        }
    
        public IActionResult DetailRow(int id)
        {
            using (Context context = new Context())
            {
                // Get only the entry that we are interrested in
                PersonEntity row = context.Persons.Include(p => p.Detail).Single(r => r.Id == id);
    
                // Construct a new view model with this entry
                PersonViewModel model = new PersonViewModel(row);
    
                return View(model);
            }
        }
    }
    
    public class Context : DbContext
    {
        public virtual DbSet<PersonEntity> Persons { get; set; }
        public virtual DbSet<DetailEntity> Details { get; set; }
    }
    
    public class PersonViewModel
    {
        public PersonViewModel(PersonEntity entity)
        {
            Id = entity.Id;
            FirstName = entity.FirstName;
            LastName = entity.LastName;
            IsHandsome = entity.Detail.IsHandsome;
            Address = entity.Detail.Address;
            Email = entity.Detail.Email;
            Phone = entity.Detail.Phone;
        }
    
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public bool IsHandsome { get; set; }
        public string Address { get; set; }
        public string Email { get; set; }
        public string Phone { get; set; }
    }
    
    public class PersonEntity
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    
        public virtual DetailEntity Detail { get; set; }
    }
    
    public class DetailEntity
    {
    
        public bool IsHandsome { get; set; }
        public string Address { get; set; }
        public string Email { get; set; }
        public string Phone { get; set; }
        public virtual PersonEntity Person { get; set; }
    
    }
    
    public class SimplePersonViewModel
    {
    
        public SimplePersonViewModel(PersonEntity entity)
        {
            Id = entity.Id;
            FirstName = entity.FirstName;
            LastName = entity.LastName;
        }
    
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    
    public class BaseListingViewModel
    {
        public BaseListingViewModel(IEnumerable<SimplePersonViewModel> list)
        {
            Rows = list;
        }
    
        public IEnumerable<SimplePersonViewModel> Rows { get; set; }
    }
    

    这里是 Index.cshtml 看法

    @model BaseListingViewModel
    
    <table>
        <thead>
        <tr>
            <td>
                Id
            </td>
            <td>
                First name
            </td>
            <td>
                Last name
            </td>
            <td>
                Is handsome
            </td>
            <td>
                Go to details
            </td>
        </tr></thead>
        @foreach (SimplePersonViewModel person in Model.Rows)
        {
            <tr>
                <td>
                    @person.Id
                </td>
                <td>
                    @person.FirstName
                </td>
                <td>
                    @person.LastName
                </td>
                <td>
                    <a href="@Url.Action("DetailRow", new {id = person.Id})">Details</a>
                </td>
            </tr>
        }
    </table>
    

    这里是 DetailRow.cshtml 看法

    @model RelaxationPortal.Controllers.PersonViewModel
    
    Details for @Model.FirstName @Model.LastName (id: @Model.Id)
    <br />
    Address is @Model.Address and can be contacted using @Model.Phone
    
    @if (Model.IsHandsome)
    {
        <span>He <b>IS</b> handsome</span>
    }
    
    else
    {
        <span>He isn't so handsome</span>
    }