6

我正在用 ASP.NET MVC 创建我的第一个站点,这是一种边学边学。但是我遇到了一个我无法找到解决方案的问题。

我希望我的用户能够创建附有歌曲和标签的专辑。这可以是未指定数量的歌曲和标签。但必须至少有 5 首歌曲和 2 个标签。

但是我无法弄清楚如何通过模型使这成为可能,这就是我能够做到的程度。

public class AlbumCreateModel
{
    [Required]
    [DisplayName("Title")]
    public string Title { get; set; }

    [DisplayName("Description")]
    public string Description { get; set; }

    [DisplayName("Publish")]
    public bool Public { get; set; }

    [DisplayName("Tags")]
    // Min 2 tags no max
    public List<AlbumTagModel> Tags { get; set; }

    [DisplayName("Songs")]
    // Min 5 songs no max
    public List<AlbumSongModel> Songs { get; set; }
}

public class AlbumTagModel
{
    [Required]
    [DisplayName("Tag")]
    // Regex to test no spaces
    // min 2 characters
    // maximum 15 characters
    public string Tag { get; set; }
}

public class AlbumSongModel
{
    [Required]
    [DisplayName("Title")]
    public string Title { get; set; }

    [Required]
    [DisplayName("Artist")]
    public string Artist { get; set; }

    [DisplayName("Description")]
    public string Description { get; set; }

    [DisplayName("Song Length")]
    public double Length { get; set; }

    [DisplayName("Year")]
    public int Description { get; set; }
}

看法:

<%@ Page Title="" Language="C#" MasterPageFile="~/App/Views/Shared/MasterPage.Master" Inherits="System.Web.Mvc.ViewPage<album.App.Models.AlbumCreateModel>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Create
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <% using (Html.BeginForm()) { %>
        <%: Html.ValidationSummary(true, "Committing the album was unsuccessful. Please correct the errors and try again.")%>
        <div>
            <fieldset>
                <legend>Album Information</legend>

                <div class="editor-label">
                    <%: Html.LabelFor(m => m.Title) %>
                </div>
                <div class="editor-field">
                    <%: Html.TextBoxFor(m => m.Title)%>
                    <%: Html.ValidationMessageFor(m => m.Title)%>
                </div>

                <div class="editor-label">
                    <%: Html.LabelFor(m => m.Description) %>
                </div>
                <div class="editor-field">
                    <%: Html.TextAreaFor(m => m.Description)%>
                    <%: Html.ValidationMessageFor(m => m.Description)%>
                </div>

                <!-- Tags here -->

                <!-- Songs here -->

                <p>
                    <input type="submit" value="Commit" />
                </p>
            </fieldset>
        </div>
    <% } %>
</asp:Content>

<asp:Content ID="Content3" ContentPlaceHolderID="MetaData" runat="server">
</asp:Content>

一个可能的解决方案:

模型:

public class PlaylistModel
    {
        [Required]
        [DisplayName("Title")]
        public string Title { get; set; }

        [DisplayName("Description")]
        public string Description { get; set; }

        [DisplayName("Publish")]
        public bool Public { get; set; }

        [DisplayName("Tags")]
        [ListCount(Min = 2)]
        // Min 2 tags no max
        public List<PlaylistTagModel> Tags { get; set; }

        [DisplayName("Songs")]
        [ListCount(Min = 5)]
        public List<PlaylistSongModel> Songs { get; set; }
    }

    public class PlaylistTagModel
    {
        [Required]
        [DisplayName("Tag")]
        // Regex to test no spaces
        // min 2 characters
        // maximum 15 characters
        public string Tag { get; set; }
    }

    public class PlaylistSongModel
    {
        [Required]
        [DisplayName("Title")]
        public string Title { get; set; }

        [Required]
        [DisplayName("Artist")]
        public string Artist { get; set; }

        [DisplayName("Description")]
        public string Description { get; set; }

        [DisplayName("Song Length")]
        public int Length { get; set; }

        [DisplayName("Year")]
        public int Year { get; set; }
    }

看法:

<%@ Page Title="" Language="C#" MasterPageFile="~/App/Views/Shared/MasterPage.Master" Inherits="System.Web.Mvc.ViewPage<playlist.App.Models.PlaylistModel>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Create
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <% using (Html.BeginForm()) { %>
        <%: Html.ValidationSummary(true, "Committing the playlist was unsuccessful. Please correct the errors and try again.")%>
        <div>
            <fieldset>
                <legend>Playlist Information</legend>

                <div class="editor-label">
                    <%: Html.LabelFor(m => m.Title) %>
                </div>
                <div class="editor-field">
                    <%: Html.TextBoxFor(m => m.Title)%>
                    <%: Html.ValidationMessageFor(m => m.Title)%>
                </div>

                <div class="editor-label">
                    <%: Html.LabelFor(m => m.Description) %>
                </div>
                <div class="editor-field">
                    <%: Html.TextAreaFor(m => m.Description)%>
                    <%: Html.ValidationMessageFor(m => m.Description)%>
                </div>

                <br />
                <%: Html.ValidationMessageFor(m => m.Tags)%>
                <div class="editor-label">
                    <%: Html.LabelFor(m => m.Tags)%>
                </div>
                <div class="editor-field">
                    <%: Html.EditorFor(m => m.Tags) %>
                    <%: Html.Editor("Tags[" + (Model == null ? 0 : Model.Tags.Count) + "]", "PlaylistTagModel")%>
                </div>

                <br />
                <%: Html.ValidationMessageFor(m => m.Songs)%>
                <div class="editor-label">
                    <%: Html.LabelFor(m => m.Songs)%>
                </div>
                <div class="editor-field">
                    <%: Html.EditorFor(m => m.Songs)%>
                    <%: Html.Editor("Songs[" + (Model == null ? 0 : Model.Songs.Count) + "]", "PlaylistSongModel")%>
                </div>

                <p>
                    <input type="submit" value="Commit" />
                </p>
            </fieldset>
        </div>
    <% } %>
</asp:Content>

<asp:Content ID="Content3" ContentPlaceHolderID="MetaData" runat="server">
</asp:Content>

两个模板:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<playlist.App.Models.PlaylistSongModel>" %>

<fieldset>
    <legend>Song Information</legend>
    <%: Html.ValidationSummary(true, "Committing this song was unsuccessful. Please correct the errors and try again.")%>

    <div class="editor-label">
        <%: Html.LabelFor(m => m.Title) %>
    </div>
    <div class="editor-field">
        <%: Html.TextBoxFor(m => m.Title)%>
        <%: Html.ValidationMessageFor(m => m.Title)%>
    </div>

    <div class="editor-label">
        <%: Html.LabelFor(m => m.Artist)%>
    </div>
    <div class="editor-field">
        <%: Html.TextBoxFor(m => m.Artist)%>
        <%: Html.ValidationMessageFor(m => m.Artist)%>
    </div>

    <div class="editor-label">
        <%: Html.LabelFor(m => m.Description)%>
    </div>
    <div class="editor-field">
        <%: Html.TextAreaFor(m => m.Description)%>
        <%: Html.ValidationMessageFor(m => m.Description)%>
    </div>

    <div class="editor-label">
        <%: Html.LabelFor(m => m.Length)%>
    </div>
    <div class="editor-field">
        <%: Html.TextBoxFor(m => m.Length)%>
        <%: Html.ValidationMessageFor(m => m.Length)%>
    </div>

    <div class="editor-label">
        <%: Html.LabelFor(m => m.Year)%>
    </div>
    <div class="editor-field">
        <%: Html.TextBoxFor(m => m.Year)%>
        <%: Html.ValidationMessageFor(m => m.Year)%>
    </div>
</fieldset>

标签:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<playlist.App.Models.PlaylistTagModel>" %>

<span class="tag"><%: Html.TextBoxFor(m => m.Tag)%></span> <%: Html.ValidationMessageFor(m => m.Tag)%>

最后是我的自定义验证器列表:

public class ListCountAttribute : ValidationAttribute
    {
        public int Min { get; set; }
        public int Max { get; set; }

        public override bool IsValid(object value)
        {
            if (Min == 0 && Max == 0)
                return true;

            if (value == null)
                return false;

            if (!(value is ICollection))
                throw new InvalidOperationException("ListCountAttribute requires underlying property to implement ICollection");

            ICollection countable = value as ICollection;
            if (Min == 0 && Max != 0)
                return countable.Count <= Max;
            else if (Max == 0 && Min != 0)
                return countable.Count >= Min;
            return (countable.Count >= Min) && (countable.Count <= Max);
        }

        public override string FormatErrorMessage(string name)
        {
            if (Min == 0 && Max != 0)
                return "The field set " + name + " can not be larger then " + Max;
            else if (Max == 0 && Min != 0)
                return "The field set " + name + " need to have atleast a count of " + Min;
            return "The field set " + name + " need to between or equal to " + Min + " and " + Max;
        }
    }
4

3 回答 3

7

在 /views/shared 中创建一个名为“EditorTemplates”的文件夹。必须准确地称呼它。

在此文件夹中,创建名为 AlbumTagModel.ascx 和 AlbumSongModel.ascx 的文件,这些文件对其各自的模型进行了强类型化。

在这些文件中添加输入字段,但不要将它们包装在表单标签中。

回到您的视图页面中:

<%: Html.EditorFor(m => m.Tags)%>

<%: Html.EditorFor(m => m.Songs)%>

现在瞧!

当您渲染输入标签时,将下标以匹配您的列表。EditorFor将自行循环和渲染。当您发布强类型 AlbumViewModel 时,您的列表将正确绑定回其原始位置。

要添加新歌曲/标签,请将以下内容添加到您的 AlbumViewModel:

public AlbumTagModel NewTagModel {get;set;}

并为其添加一个额外的 EditorFor() 。

当您的模型发布时,如果 NewTagModel 有效,则将其添加到列表中并重新显示视图。

于 2010-08-14T20:37:08.970 回答
1

您必须编写自定义验证属性来支持该要求。

这是一个不完全是您正在寻找的示例,但应该让您指向正确的方向。后面有关于如何根据环境调整它的注释。

public class AtLeastOneRequiredAttribute : ValidationAttribute
{
  public override bool IsValid(object value)
  {
    if (value == null)
      return false;

    if (!(value is ICountable))
      throw new InvalidOperationException("AtLeastOneRequiredAttribute requires underlying property to implement ICountable");

    ICountable countable = value as ICountable;
    return countable.Count >= 1;
  }
}

我们使用我们自己的“子模型”而不是通用列表,并让它们实现 ICountable,这是我们环境中的实用程序接口。您只需检查以确保您的值实现了 IList,然后调用(value as IList).Count.

对于一般最小值而不是“至少一个”,定义一个最小值属性。

希望这能让您朝着正确的方向前进,如果您有其他问题,请发布。

于 2010-08-14T20:27:56.450 回答
0

尝试使用模板编辑器编辑对象集合时,我似乎无法使其工作。这是我所拥有的:

public class Candidate
    {
        public Candidate()
        {
            this.References = new List<Reference>();
        }
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime? DateOfBirth { get; set; }
        [UIHint("Reference")]  
        public List<Reference> References { get; set; }

    }

public class Reference
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Institution { get; set; }
        public string Email { get; set; }
        public string Phone { get; set; }
        public string Position { get; set; }
    }

我在 Views/Shared/TemplateEditors 中创建了一个模板,并创建了一个强类型的局部视图 Register.ascx。

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Models.Reference>" %>
<%:Html.TextBoxFor(model=>model.Name) %>
<%:Html.TextBoxFor(model=>model.Email) %>
<%:Html.TextBoxFor(model=>model.Institution) %>
<%:Html.TextBoxFor(model=>model.Position) %>
<%:Html.TextBoxFor(model=>model.Phone) %>

鉴于我有候选人,我使用下面的代码来呈现参考列表。

 <%: Html.EditorFor(m => m.References)%>

当我运行项目时,我得到

 InvalidOperationException: The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[Models.Reference]', but this dictionary requires a model item of type 'Models.Reference'.

你们看到实施有什么问题吗?

谢谢。

于 2010-10-20T17:43:44.263 回答