質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.32%

  • C#

    7714questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • ASP.NET

    561questions

    ASP.NETは動的なWebサイトやWebアプリケーション、そしてWebサービスを構築出来るようにする為、Microsoftによって開発されたウェブアプリケーション開発フレームワークです。

  • ASP.NET MVC Framework

    69questions

    ASP.NET MVC Frameworkは、MVCパターンをベースとした、マイクロソフトのウェブアプリケーション開発用のフレームワークです。

ASP.NET MVC ラジオボタンのView/Controllerのやり取り

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 239

お世話になっております。
前回、ASP.NET MVCでのラジオボタンについてで質問させていただいた件で、
引き続き質問させていただきます。

開発環境:C#, ASP.NET MVC5, SQLServer2014
実現したいこと:社員各個人に複数のアンケートを取るが、各設問の回答を5段階(5:非常に満足、4:満足、3:普通~)で回答してもらう。
UIとして、ラジオボタンで選択させたい。
(複数と書きましたのは、例えばあるアンケートでは設問が10、別のアンケートでは設問が5…と、アンケート内容によって可変となる為)

SQLServer上に用意したテーブルですが、
「QAHead」テーブル
・QA_Id int …アンケートID
・Employee_Id int …社員番号
・Remarks nvarchar(max) …その他備考(フリーワード)

「QADetail」テーブル
・QA_Id int …アンケートID
・Employee_Id int …社員番号
・QA_No smallint …アンケートNo
・QA_Ans smallint …回答
となっております。QAHeadとQADetailは1:nの関係です。

対応するModelとしまして、

public class QAModels
{
    [DisplayName("ID")]
    public int QA_Id { get; set; }

    [DisplayName("社員番号")]
    public int Employee_Id { get; set; }

    [DisplayName("備考")]
    public string Remarks { get; set; }

    public IList<QADetailModels> Details { get; set; }
}

public class QADetailModels
{
    [DisplayName("ID")]
    public int QA_Id { get; set; }

    [DisplayName("社員番号")]
    public int Employee_Id { get; set; }

    [DisplayName("アンケートNo")]
    public short QA_No { get; set; }

// このアンケート内容は別のテーブルから引いてくるものとする
    [DisplayName("アンケート内容")]
    public string QA_Description { get; set; }

    [DisplayName("回答")]
    public short QA_Ans { get; set; }
}


としました。
View上で表示するために、

@model TESTAPP.Models.QAModels
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()

<div class="form-horizontal">
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
<table class="table table-bordered">
    <tr><th>No</th><th>設問内容</th><th>回答</th></tr>
        foreach (var item in Model.Details)
        {
            <tr>
                <td>@item.QA_No</td>
                <td>@item.QA_Description</td>
                <td><div class="editor-field">
                    @for (int x = 1; x <= 5; x++)
                    {
                        <label>
                            @Html.RadioButtonFor(
                                m => item.QA_Ans,
                                x,
                                new { @Name = $"{item.Skill_No}", @id = $"{item.QA_No}_{x}"}
                            )
                            @x
                        </label>
                    }</div>
                </td>
            </tr>
    }
</table>
</div>
<div class="form-group">
    <div class="col-md-offset-2 col-md-10">
        <button name="button" value="Upd" class="btn btn-primary">更新</button>
    </div>
</div>
</div>
}


…これですと、View上への表示は可能ですが、ユーザーが(ラジオボタンをクリックして)選択した値をControllerに返す際、

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateEval([Bind(Include = "QA_Id, Employee_Id, Remarks, Details")] QAModels qam)
{

}


上記の記述ではDetailsがnullとなってしまい、データベースへの更新が出来ない状況です。

単に表示だけでなく、その後のユーザーの回答内容をデータベースに反映させるには、どのようにすればよいか、
アドバイス等何卒よろしくお願いいたします。

【追記】
上記Viewの部分、以下のように変更してみました。

        foreach (int i = 0; i < Model.Details.Count; i++)
        {
            <tr>
                <td>@Html.DisplayFor(m => m.Details[i].QA_No)</td>
                <td>@Html.DisplayFor(m => m.Details[i].QA_Description)</td>
                <td><div class="editor-field">
                    @for (int x = 1; x <= 5; x++)
                    {
                        <label>
                            @Html.RadioButtonFor(
                                m => m.Details[i].QA_Ans,
                                x,
                                new { @Name = $"{Model.Details[i].QA_Ans}", @id = $"{Model.Details[i].QA_No}_{x}"}
                            )
                            @x
                        </label>
                    }</div>
                </td>
            </tr>
    }


データ表示に関しては表示はされますが、POSTした場合、相変わらずnullのままです。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • SurferOnWww

    2018/10/12 10:10

    質問欄に前のスレッドの URL を書いて、その話の続きであることを明記してください。

    キャンセル

  • masterofpuppets

    2018/10/12 10:15

    大変失礼いたしました。URLを追記させていただきました。

    キャンセル

回答 1

checkベストアンサー

0

先のスレッドで紹介した以下の記事は読んでもらえたでしょうか?

コレクションのデータアノテーション検証
http://surferonwww.info/BlogEngine/post/2014/09/01/validation-of-collection-data-during-model-binding-using-data-annotation.aspx

そこに書いてあるように、コレクションのモデルバインディングがうまく行われるようにするには、レンダリングされる html 要素の name 属性が連番のインデックスを含むようにします。

質問者さんのコードの「回答」で言うと、name="Details[i].QA_Ans" という形で、i は 0 から始まる連番にします。

あと、モデルに定義されているすべての項目の値が POST されるようにします。なので <td>@item.QA_No</td> というような形ではなく、EditorFor を使うか、@item.QA_No とか DisplayFor を使うのであれば HiddenFor を追加してください。

と、言葉で書くだけでは分からないと思いますので、あとでサンプルコードを書いてアップします。お待ちください。

【追記】

以下、サンプルです。

Controller / Action Method / Model

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.ComponentModel.DataAnnotations;

namespace Mvc5App.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        [HttpGet]
        public ActionResult QuestionsAndAnswers()
        {
            // サンプル用の QAModels を作成
            QAModels model = new QAModels
            {
                QA_Id = 1,
                Employee_Id = 12345,
                Remarks = "備考",
                Details = new List<QADetailModels>
                {
                    // 質問数は任意。ここではとりあえず 5 題としています。
                    new QADetailModels { QA_Id = 1, Employee_Id = 12345, QA_No = 1, QA_Description = "質問文その 1" },
                    new QADetailModels { QA_Id = 1, Employee_Id = 12345, QA_No = 2, QA_Description = "質問文その 2" },
                    new QADetailModels { QA_Id = 1, Employee_Id = 12345, QA_No = 3, QA_Description = "質問文その 3" },
                    new QADetailModels { QA_Id = 1, Employee_Id = 12345, QA_No = 4, QA_Description = "質問文その 4" },
                    new QADetailModels { QA_Id = 1, Employee_Id = 12345, QA_No = 5, QA_Description = "質問文その 5" }
                }
            };

            return View(model);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult QuestionsAndAnswers(QAModels model)
        {
            return View(model);
        }
    }

    // Model
    // Controller に含めたのは単に分けるのが面倒だったからです
    // 質問者さんのコードの DisplayName は古いので以下のように変えました
    // それ以外は質問者さんのコードの通りです。
    public class QAModels
    {
        [Display(Name="ID")]
        public int QA_Id { get; set; }

        [Display(Name="社員番号")]
        public int Employee_Id { get; set; }

        [Display(Name="備考")]
        public string Remarks { get; set; }

        public IList<QADetailModels> Details { get; set; }
    }

    public class QADetailModels
    {
        [Display(Name="ID")]
        public int QA_Id { get; set; }

        [Display(Name="社員番号")]
        public int Employee_Id { get; set; }

        [Display(Name="アンケートNo")]
        public short QA_No { get; set; }

        // このアンケート内容は別のテーブルから引いてくるものとする
        [Display(Name="アンケート内容")]
        public string QA_Description { get; set; }

        [Display(Name="回答")]
        public short QA_Ans { get; set; }
    }
}

View

上のアクションメソッド QuestionsAndAnswers をベースに Visual Studio 2015 のスキャフォールディング機能を使って自動生成させたものです。Template は Create を、Model class は QAModels を設定。それに <table> ... </table> のコードを追加した以外は手を加えていません。

@model Mvc5App.Controllers.QAModels

@{
    ViewBag.Title = "QuesttionsAndAnswers";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>QuesttionsAndAnswers</h2>


@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>QAModels</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.QA_Id, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.QA_Id, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.QA_Id, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Employee_Id, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Employee_Id, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Employee_Id, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Remarks, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Remarks, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Remarks, "", new { @class = "text-danger" })
            </div>
        </div>

        <table class="table">
            <tr>
                <th>
                    @Html.DisplayNameFor(model => model.Details[0].QA_No)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.Details[0].QA_Description)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.Details[0].QA_Ans)
                </th>
            </tr>


            @for (int i = 0; i < Model.Details.Count; i++)
            {
            <tr>
                @Html.HiddenFor(model => model.Details[i].QA_Id)
                @Html.HiddenFor(model => model.Details[i].Employee_Id)
                <td>                    
                    @Html.EditorFor(model => model.Details[i].QA_No, new { htmlAttributes = new { @class = "form-control" } })
                </td>
                <td>
                    @Html.EditorFor(model => model.Details[i].QA_Description, new { htmlAttributes = new { @class = "form-control" } })
                </td>
                <td>
                    @*@Html.EditorFor(model => model.Details[i].QA_Ans, new { htmlAttributes = new { @class = "form-control" } })*@
                    @Html.RadioButton("Details[" + i + "].QA_Ans", 5, false)<label>非常に満足&nbsp;&nbsp;</label>
                    @Html.RadioButton("Details[" + i + "].QA_Ans", 4, false)<label>満足&nbsp;&nbsp;</label>
                    @Html.RadioButton("Details[" + i + "].QA_Ans", 3, false)<label>普通&nbsp;&nbsp;</label>
                    @Html.RadioButton("Details[" + i + "].QA_Ans", 2, false)<label>不満&nbsp;&nbsp;</label>
                    @Html.RadioButton("Details[" + i + "].QA_Ans", 1, false)<label>非常に不満&nbsp;&nbsp;</label>
                </td>

            </tr>
            }
        </table>

            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit" value="Create" class="btn btn-default" />
                </div>
            </div>
</div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

以下のように RadioButton を設定して[Create]ボタンをクリックするとデータがサーバーに送信されて、

イメージ説明

以下の通り public ActionResult QuestionsAndAnswers(QAModels model) の model にモデルバインディングされます。

イメージ説明

以上ですが、分からないところがあったら聞いてください。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/12 12:46

    SurferOnWww様
    サンプル及び解説、誠にありがとうございます。
    早速この後試してみます。
    先ずは御礼申し上げます。

    キャンセル

  • 2018/10/12 13:30

    SurferOnWww様
    Viewの記述ですが、
    <label>@Html.RadioButtonFor(m => m.Details[i].QA_Ans, 1)1&nbsp;&nbsp;</label>
    に変更すると、元々データベース上にあったデータも表示されました。
    また、POSTした際も値が返っておりました。

    キャンセル

  • 2018/10/12 14:23

    それで良いと思います。

    というか、特に、モデルにある QA_Ans の値で初期画面のラジオボタンの選択を設定したい場合は RadioButtonFor を使うのが正解と思います。

    キャンセル

  • 2018/10/12 18:29

    SurferOnWww様
    この度はご丁寧なご回答・解説、誠にありがとうございました。
    Viewへのデータ表示→POSTで返す部分含めて、一連の処理が理解出来ました。
    また、当初の質問で私のせいで混乱を招くようなことになってしまい、改めてお詫び申し上げます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.32%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C#

    7714questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • ASP.NET

    561questions

    ASP.NETは動的なWebサイトやWebアプリケーション、そしてWebサービスを構築出来るようにする為、Microsoftによって開発されたウェブアプリケーション開発フレームワークです。

  • ASP.NET MVC Framework

    69questions

    ASP.NET MVC Frameworkは、MVCパターンをベースとした、マイクロソフトのウェブアプリケーション開発用のフレームワークです。