0

When the user clicks a button, the following HTML gets added to the page by Javascript, and the value of IncrNumber increases each time the button is clicked, so the first click would produce divs with the following ID's: (start0, end0, comm0) and the second click would produce ID's (start1, end1, comm1) and so on.

Start: <input type="text" class="form-control" id="start' + IncrNumber + '"/>

End: <input type="text" class="form-control" id="end' + IncrNumber + '" />

Comment:  <textarea rows="4" class="form-control" id="comm' + IncrNumber + '"></textarea>

Since these divs are being dynamically added with Javascript, I cannot use Razor and therefore cannot convert these text boxes into "HTML.TextBoxFor(x.start)..."

Since clicking a button will generate a variable, unlimited number of these fields, I cannot add them to my Model, anyways.


How can I get the values of startX, endX, and commX in my controller?

EDIT:

enter image description here

There exists a Many-To-One relationship between DBO.Televiewers[ID, Date] and DBO.Annotations[ID, TeleviewerID, Start, End, Comment]. The user can add as many Annotation objects as they want per Televiewer.

VIEW:

<div class="panel panel-form-5">
<div class="panel-heading">
    <h3 class="panel-title">Edit Televiewer</h3>
</div>
<div class="panel-body">

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


        @Html.Label("Hole Name")
        @Html.EditorFor(m => m.HoleName, new { htmlAttributes = new { @class = "form-control" } })
        <br />
        @Html.Label("Televiewer Date")
        @Html.EditorFor(m => m.TeleviewerDate, new { htmlAttributes = new { @class = "form-control" } })
        <br />
        <div class="panel panel-default">
            <div class="panel-body">
                <div class="row">
                    <div class="col-lg-10">
                        <h4>Annotations</h4>
                    </div>
                    <div class="col-lg-2">                           
                        <div id="addbuttondiv">
                            <button onclick="addAnswer(); return false;" class="btn btn-default">Add New</button>
                        </div>

                    </div>
                </div>
                <hr />
                <div id="annotations">


                </div>
            </div>
        </div>
        <br />
                @Html.ActionLink("Back", "Index", "Televiewer", null, new { @class = "btn btn-default" })
                <input type="submit" value="Calculate & Save" class="btn btn-primary" />
       }

            </div>
        </div>
        <script>
            var answers = 0;

            function addAnswer() {
                var write = document.getElementById('annotations');
                write.innerHTML += ' <div class="row"><div class="col-lg-3 col-lg-offset-1">Start Depth: <input type="text" class="form-control" id="start' + answers + '"/></div><div class="col-lg-3 col-lg-offset-4" >End Depth: <input type="text" class="form-control" id="end' + answers + '" /></div></div><br /><div class="row"><div class="col-lg-1 col-lg-offset-2"><br />Comment:</div><div class="col-lg-6"><textarea rows="4" class="form-control" id="comm' + answers + '"></textarea></div></div><hr/>';
                answers++;
            }
        </script>
objectively C
  • 732
  • 6
  • 23
  • How is the info getting back to the server? Form submit? Ajax call? – nurdyguy Mar 28 '17 at 19:34
  • @nurdyguy an calls an ActionResult method in the Controller. – objectively C Mar 28 '17 at 19:38
  • Ok so it is a form submit. Basically, you want to have a `List`. That way these really are part of the model. Sure, you don't know how many there will be but that's fine, the `List` can have as many entries as you need. Are the inputs being generated inside of the form tags? – nurdyguy Mar 28 '17 at 19:46
  • @nurdyguy If I convert from to @-textboxfor(x => x.list)..., I will be unable to generate @-textboxfor using javascript each time the button is clicked, right? the textfields are enclosed in @-using (Html.BeginForm()){ } – objectively C Mar 28 '17 at 19:54
  • You still want the javascript to handle adding more boxes but in the end, if we do it right, MVC should recognize what is there and bundle it all up together. Honestly, it is a bit easier to do that manually using AJAX, but it can be done either way. Can you post what the html is and what your `Televiewer` and `Annotation` objects look like? With that info I think i can point you in the right direction. – nurdyguy Mar 28 '17 at 19:57
  • @nurdyguy I will edit my question to include those things. Can you post how to use AJAX to solve my problem? I think that is exactly what I need if I can figure out how to use AJAX with MVC. – objectively C Mar 28 '17 at 20:00
  • One big difference between form submit and AJAX is that after form submit you can navigate away from the page easily. You can do that with AJAX but you have to do it manually. Not a big different but worth noting. – nurdyguy Mar 28 '17 at 20:02
  • @nurdyguy Question has been edited. Please respond in an Answer to avoid extended discussion in comments. – objectively C Mar 28 '17 at 20:06
  • A form posts back its name/value pairs and you have not given your controls a `name` attribute. In order to bind to a collection, the `name` attributes need to be correctly indexed. To generate you form controls correctly, refer some options [here](http://stackoverflow.com/questions/28019793/submit-same-partial-view-called-multiple-times-data-to-controller/28081308#28081308) and for a complete example using `BeginCollectionItem`, refer [this answer](http://stackoverflow.com/questions/40539321/a-partial-view-passing-a-collection-using-the-html-begincollectionitem-helper/40541892#40541892) –  Mar 28 '17 at 21:30
  • As @StephenMuecke mentioned, form Collection is one option, another is this: http://stackoverflow.com/questions/19964553/mvc-form-not-able-to-post-list-of-objects. If you next the list of annotations inside your model and then alter the names of the items you create via javascript, adding the index field, that will then bind them to the list on submit. – nurdyguy Mar 28 '17 at 21:41
  • The answer you accepted is a poor and inflexible approach,- will never allow you to delete items, or get any client side validation etc. etc. Strongly suggest you read the links in my previous comment. –  Mar 29 '17 at 00:46

2 Answers2

1

There is no problem in sending back the newly created elements on the client as long as you support them on your model.

        $("#submit").on('click', function ()
        {
            var i = 0;
            $('input[name ^= \'start\']').remove();
            $('input[name ^= \'start\']').each(function ()
            {
                $("<input type='hidden' value='" + $(this).attr("id") + "' />").attr("id", "start_" + i + "_"+i).attr("name", "start[" + i + "]."+i).appendTo("form");
                i++;
            });

This will take care, on the client, to generate the object for your Model. You will need a list of start, end and comment lists for this or you can create a list that contains all three objects but you will need to change the object you send back from the client.

When you need to render those items:

<h5>Elements assigned here</h5>
<ul id="xxx" class="droptrue dropZone selectorBox list">
@foreach(var item in Model.startObjects)
{
  <li class="ui-state-default" id="@item.startID"><span class="sName2"><i class="fa fa-flask" aria-hidden="true"></i>&nbsp;@item.startName</span></li>
}
</ul>

Hope this helps!

ProgrammerV5
  • 1,849
  • 2
  • 10
  • 21
1

Taking an approach similar to this story: MVC Form not able to post List of objects

Here are the basic models:

public class Televiewer
{

    public string HoleId { get; set; }

    public DateTime Date { get; set; }

    public List<Annotation> Annotations { get; set; }
}

public class Annotation
{
    public int AnnotationId { get; set; }
    public string HoleId { get; set; } //---- foreign key
    public int StartDepth { get; set; }
    public int EndDepth { get; set; }
    public string Comment { get; set; }
}

In the html, for your 'Annotations" section:

<div id="Annotations">
    @foreach (var ann in Model.Annotations)
    {
       <div class="annotation">
         <input type="text" class="form-control startDepth" name="Annotations[@i].StartDepth" value="@Model.Annotations[i].StartDepth" />
         <input type="text" class="form-control endDepth" name="Annotations[@i].EndDepth" value="@Model.Annotations[i].EndDepth" />
         <textarea rows="4" class="form-control comment"name="Annotations[@i].Comment" >@Model.Annotations[i].Comment</textarea>
       </div>
     }
</div>

In the javascript, when you click the "Add" button:

function addAnswer()
{
    var index = $('#Annotations').find('.annotation').length;
    var html = '<br><div class="annotation">'
            + '<input type="text" class="form-control startDepth" name="Annotations[' + index + '].StartDepth" value="" />'
            + '<input type="text" class="form-control endDepth" name="Annotations[' + index + '].EndDepth" value="" />'
            + '<textarea rows="4" class="form-control comment"name="Annotations[' + index + '].Comment" ></textarea>'
            + '</div>';
    $('#Annotations').append(html);
}

That should roughly plug in to your form submit. You may have to iron out a few kinks (can't say what because I don't have enough of your code).

Notes: 1. View models are your friend. If you need to wrap up some data, make a model for it. 2. Creating the html in the javascript the way I did is ok but using html templates would be better. I just didn't want to open that can or worms here. 3. The models I used are obviously stripped down. I'm assuming you are using EF though so you'll have to adjust accordingly.

Community
  • 1
  • 1
nurdyguy
  • 2,615
  • 3
  • 20
  • 29