1

特定の画面で特定の権限をユーザーに付与できる権限画面を実装しようとしています。このために、bool プロパティのコレクションにバインドされた Checkboxfor のコレクションを生成しています。しかし、フォームを送信すると、ビューモデル コンストラクターでこれらのプロパティを true または false として初期化したかどうかに応じて、すべての bool プロパティが true またはすべて false になります。

ViewModel のコードは次のとおりです。

アプローチ I:

public class MyViewModel
{
    public MyModel Model { get; set; }        

    public IEnumerable<ScreenType> Screens { get; set; }
    public IEnumerable<SecurityType> SecurityTypes { get; set; }
    public List<PermissionType> Permissions { get; set; }        

    public MyViewModel()
    {
        LoadScreens();
        LoadSecurityTypes();
        LoadPermissions();
    }

    public void LoadPermissions()
    {
        Permissions = new List<PermissionType>();

        foreach (var screen in Screens)
        {
            foreach (var securityType in SecurityTypes)
            {
                Permissions.Add(
                    new PermissionType
                    {
                        PermissionId= Guid.NewGuid(),
                        ScreenId= screen.Id,
                        SecurityId = securityType.Id,
                        IsAllowed = false
                    });
            }
        }
    }    
}

アプローチⅡ

public class MyViewModel
{
    public MyModel Model { get; set; }        

    public IEnumerable<ScreenType> Screens { get; set; }
    public IEnumerable<SecurityType> SecurityTypes { get; set; }
    public List<bool> AllowedList { get; set; }        

    public MyViewModel()
    {
        LoadScreens();
        LoadSecurityTypes();
        LoadPermissions();
    }

    public void LoadPermissions()
    {
        AllowedList = new List<bool>();

        foreach (var form in Forms)
        {
            foreach (var security in Securities)
            {
                AllowedList.Add(false);
            }
        }
    }    
}

ビューのコードは次のとおりです。

アプローチ I:

    @using (Ajax.BeginForm("Create", "Role", null, new AjaxOptions { UpdateTargetId = "addStatus", InsertionMode = InsertionMode.Replace, OnSuccess = "onFormPostSuccess" }, new { @id = "AddForm" }))
    {  
        <div>
            <span><label>Screen</label></span>
            @foreach (var security in Model.SecurityTypes)
            { 
                <span><label>@security.Name</label></span>                    
            }
            <br />
            @foreach (var screen in Model.Screens)
            {
                <span>@screen.Name</span>
                foreach (var security in Model.SecurityTypes)
                { 
                    <span>@Html.CheckBoxFor(m => m.Permissions.Where(s => s.SecurityId == security.Id && s.ScreenId == screen.Id).Single().IsAllowed, new { @id = HtmlHelper.GenerateIdFromName("Create" + screen.Name + security.Name) })</span>                   
                }
                <br />
            }
        </div>
        <div>
            <span>
                <input type="image" src="/content/images/submit_button.png" value="submit" />
            </span> 
        </div>
        <div>
            <span id="addStatus" class="submitStatus"></span>
        </div>
    }

アプローチ II:

 @using (Ajax.BeginForm("Create", "Role", null, new AjaxOptions { UpdateTargetId = "addStatus", InsertionMode = InsertionMode.Replace, OnSuccess = "onFormPostSuccess" }, new { @id = "AddForm" }))
    {  
        <div>
            <span><label>Screen</label></span>
            @foreach (var security in Model.SecurityTypes)
            { 
                <span><label>@security.Name</label></span>                    
            }
            <br />
            @foreach (int i=0; i < Model.Screens.Count(); i++)
            {
                <span>@Model.Screens.ElementAt(i).Name</span>
                foreach (int j=0; j<Model.SecurityTypes.Count(); j++)
                { 
                    @* The index 5*i+j is because I have 5 security types *@ 
                    <span>@Html.CheckBoxFor(Model.AllowedList[5*i+j], new { @id = HtmlHelper.GenerateIdFromName("Create" + @Model.Screens.ElementAt(i).Name + @Model.SecurityTypes.ElementAt(j).Name) })</span>                   
                }
                <br />
            }
        </div>
        <div>
            <span>
                <input type="image" src="/content/images/submit_button.png" value="submit" />
            </span> 
        </div>
        <div>
            <span id="addStatus" class="submitStatus"></span>
        </div>
    }

コントローラーでアクション メソッドを作成するコードは次のとおりです。

    [Authorize]
    [HttpPost]
    public JsonResult Create(MyViewModel viewModel)
    {   
        if ( ModelState.IsValid)
        {                

            if (service.AddRole(viewModel))
            {                    
                return Json("Role Added !");
            }
            return Json("Role exists !");
        }
        return Json("Please correct errors");
    }

Create アクションメソッドで viewModel を確認すると、すべての IsAllowed プロパティが false です。ビューモデルコンストラクターで初期化されたように。ビューからチェックボックスをオンまたはオフにしても効果はありません。

何か不足していますか?

4

1 に答える 1

8

CheckBoxFor などの厳密に型指定された HTML ヘルパーは、最初の引数として単純な式のみを使用します (プロパティと配列のインデックスへのアクセスは最大)。次の式は、このヘルパーの能力を完全に超えています。

m => m.Permissions.Where(s => s.SecurityId == security.Id && s.ScreenId == screen.Id).Single().IsAllowed

ドメイン モデルとビュー モデルの間でマッピングを行う場合は、実際のビュー モデルを使用し、これをコントローラー レベルで実行することをお勧めします。


アップデート:

どうやら、ビューモデルに関する私の回答は十分に明確ではなかったようです。

したがって、このビューに必要なロジックを実装するために必要なビュー モデルを定義することから始めます。

public class CreateViewModel
{
    public IEnumerable<ScreenViewModel> Screens { get; set; }
}

public class ShowCreateViewModel: CreateViewModel
{
    public IEnumerable<SecurityTypeViewModel> SecurityTypes { get; set; }
}

public class ScreenViewModel
{
    public string ScreenName { get; set; }
    [HiddenInput(DisplayValue = false)]
    public int ScreenId { get; set; }
    public IEnumerable<SecurityTypeViewModel> SecurityTypes { get; set; }
}

public class SecurityTypeViewModel
{
    public string SecurityName { get; set; }
    [HiddenInput(DisplayValue = false)]
    public int SecurityTypeId { get; set; }
    public bool IsAllowed { get; set; }
}

次に、リポジトリなどからドメイン モデルを取得し、ビュー モデルにマップするコントローラー アクションを作成できます。

public class HomeController : Controller
{
    public ActionResult Create()
    {
        // The information will obviously come from a domain model that 
        // we map to a view model, but for the simplicity of the answer
        // I am hardcoding the values here
        var securityTypes = new[]
        {
            new SecurityTypeViewModel { SecurityTypeId = 1, SecurityName = "security 1" },
            new SecurityTypeViewModel { SecurityTypeId = 2, SecurityName = "security 2" },
            new SecurityTypeViewModel { SecurityTypeId = 3, SecurityName = "security 3" },
        };

        // The information will obviously come from a domain model that 
        // we map to a view model, but for the simplicity of the answer
        // I am hardcoding the values here
        return View(new ShowCreateViewModel
        {
            SecurityTypes = securityTypes,
            Screens = new[]
            {
                new ScreenViewModel 
                {
                    ScreenId = 1,
                    ScreenName = "Screen 1",
                    SecurityTypes = securityTypes
                },
                new ScreenViewModel 
                {
                    ScreenId = 2,
                    ScreenName = "Screen 2",
                    SecurityTypes = securityTypes
                },
            }
        });
    }

    [HttpPost]
    public ActionResult Create(CreateViewModel model)
    {
        // The view model passed here will contain all the necessary information
        // for us to be able to perform the actual Save: 
        // a list of the screen ids along with a list of the selected permission ids

        return Content(
            "Thank you for selecting the following allowed permissions:<br/>" + 
            string.Join("<br/>", model.Screens.Select(s => string.Format(
                "screen id: {0}, permission ids: {1}", 
                s.ScreenId, 
                string.Join(",", s.SecurityTypes.Where(st => st.IsAllowed).Select(st => st.SecurityTypeId))
            )))
        );
    }
}

あとは、ビューと対応するエディター/表示テンプレートを定義するだけです。

~/Views/Home/Create.cshtmlメイン ビュー ( )から始めましょう。

@model ShowCreateViewModel

<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>

@using (Ajax.BeginForm("Create", "Home", null, new AjaxOptions { UpdateTargetId = "addStatus", InsertionMode = InsertionMode.Replace, OnSuccess = "onFormPostSuccess" }, new { @id = "AddForm" }))
{  
    <table>
        <thead>
            <tr>
                <th>Screen/Security type</th>
                @Html.DisplayFor(x => x.SecurityTypes)
            </tr>
        </thead>
        <tbody>
            @Html.EditorFor(x => x.Screens)
        </tbody>
    </table>    

    <div>
        <input type="submit" value="submit" />
    </div>

    <div id="addStatus" class="submitStatus"></div>
}

次に、ScreenViewModelモデル ( ~/Views/Shared/EditorTemplates/ScreenViewModel.cshtml) のエディター テンプレートがあります。

@model ScreenViewModel
<tr>
    <td>
        @Html.DisplayFor(x => x.ScreenName)
        @Html.EditorFor(x => x.ScreenId)
    </td>
    @Html.EditorFor(x => x.SecurityTypes)
</tr>

SecurityTypeViewModel次に、モデルのエディター テンプレート( ~/Views/Shared/EditorTemplates/SecurityTypeViewModel.cshtml):

@model SecurityTypeViewModel
<td>
    @Html.CheckBoxFor(x => x.IsAllowed)
    @Html.EditorFor(x => x.SecurityTypeId)
</td>

SecurityTypeViewModel最後に、モデルの表示テンプレート( ~/Views/Shared/DisplayTemplates/SecurityTypeViewModel.cshtml):

@model SecurityTypeViewModel
<th>
    @Html.DisplayFor(x => x.SecurityName)
</th>

そして、それはほとんどそれです:

ここに画像の説明を入力

実際のドメイン モデルとここで定義されたビュー モデルとの間のマッピングを残しました。

于 2012-07-13T12:26:52.190 に答える