-1

I am wondering if anyone could help me I am trying to put DropDownList inside a java script function which creates new rows in a table. I am wondering is this possible. I need to do this because I am using BeginCollectionItem to collect the values of the children of a master record.

Update: The Purpose of this is to collect the children of the orders with BeginCollectionItem and using JavaScript to calculate the calculations and create new table rows so that they can be captured by BeginCollectionItem to be added to the order.

Image enter image description here

JavaScript (Its in a separate File called Invoicescript.js)

    function generateTableRow() {
        var emptyColumn = document.createElement('tr');

        emptyColumn.innerHTML = '<td><a class="cut">-</a><span contenteditable></span></td>' +
            '<td><span contenteditable></span></td>' +
            '<td><span data-prefix>$</span><span contenteditable>0.00</span></td>' +
            '<td><span contenteditable>0</span></td>' +
            '<td><span contenteditable>0</span></td>' +
            '<td><span contenteditable>0</span></td>' +
            '<td><span contenteditable>0</span></td>' +
            '<td><span contenteditable>0</span></td>' +
            '<td><span data-prefix>$</span><span>0.00</span></td>';

        return emptyColumn;
    }

    function onContentLoad() {
        updateInvoice();

        var
        input = document.querySelector('input'),
        image = document.querySelector('img');

        function onClick(e) {
            var element = e.target.querySelector('[contenteditable]'), row;

            element && e.target != document.documentElement && e.target != document.body && element.focus();

            if (e.target.matchesSelector('.add')) {
                document.querySelector('table.inventory tbody').appendChild(generateTableRow());
            }
            else if (e.target.className == 'cut') {
                row = e.target.ancestorQuerySelector('tr');

                row.parentNode.removeChild(row);
            }

            updateInvoice();
        }

        function onEnterCancel(e) {
            e.preventDefault();

            image.classList.add('hover');
        }

        function onLeaveCancel(e) {
            e.preventDefault();

            image.classList.remove('hover');
        }

        function onFileInput(e) {
            image.classList.remove('hover');

            var
            reader = new FileReader(),
            files = e.dataTransfer ? e.dataTransfer.files : e.target.files,
            i = 0;

            reader.onload = onFileLoad;

            while (files[i]) reader.readAsDataURL(files[i++]);
        }

        function onFileLoad(e) {
            var data = e.target.result;

            image.src = data;
        }

        if (window.addEventListener) {
            document.addEventListener('click', onClick);

            document.addEventListener('mousewheel', updateNumber);
            document.addEventListener('keydown', updateNumber);

            document.addEventListener('keydown', updateInvoice);
            document.addEventListener('keyup', updateInvoice);

            input.addEventListener('focus', onEnterCancel);
            input.addEventListener('mouseover', onEnterCancel);
            input.addEventListener('dragover', onEnterCancel);
            input.addEventListener('dragenter', onEnterCancel);

            input.addEventListener('blur', onLeaveCancel);
            input.addEventListener('dragleave', onLeaveCancel);
            input.addEventListener('mouseout', onLeaveCancel);

            input.addEventListener('drop', onFileInput);
            input.addEventListener('change', onFileInput);
        }
    }
    window.addEventListener && document.addEventListener('DOMContentLoaded', onContentLoad);

Partial View

    @using HtmlHelpers.BeginCollectionItem
@{
    Layout = "";
}

@using (Html.BeginCollectionItem("OrderLines"))
{
    <tbody>
        <tr>
            <td>
                <a class="cut">-</a><span contenteditable>
                    @Html.DropDownList("StockCode", new SelectList(ViewBag.StockCodeList, "Value", "Text"),
                    new
                     {
                         @class = "form-control selectpicker",
                         @Value = @Model.Description,
                         onchange = "this.form.submit();",
                         data_show_subtext = "true",
                         data_live_search = "true"
                     })
                </span>
            </td>
            <td><span contenteditable>Test</span></td>
            <td><span data-prefix>$</span><span contenteditable>150.00</span></td>
            <td><span contenteditable>4</span></td>
            <td><span contenteditable>0</span></td>
            <td><span contenteditable>0.00</span></td>
            <td><span contenteditable>0.00</span></td>
            <td><span contenteditable>0</span></td>
            <td><span data-prefix>$</span><span>0.00</span></td>
        </tr>
    </tbody>
}

Main View

@model Accounts.Models.OrderView
@using HtmlHelpers.BeginCollectionItem

<link href="~/Content/Invoicestyle.css" rel="stylesheet" />

                        <header>
                        <h1>Invoice</h1>
                    </header>

                <article>
                    <h2>Recipient</h2>
                    <address >
                        <p>Some Company<br>c/o Some Guy</p>
                        <p>Jonathan Neal</p>
                        <p>101 E. Chapman Ave<br>Orange, CA 92866</p>
                        <p>(800) 555-1234</p>
                    </address>

                    <table class="meta">
                        <tr>
                            <th><span >Invoice #</span></th>
                            <td><span >101138</span></td>
                        </tr>
                        <tr>
                            <th><span >Date</span></th>
                            <td><span >January 1, 2012</span></td>
                        </tr>
                        <tr>
                            <th><span >Amount Due</span></th>
                            <td><span id="prefix" >£</span><span>600.00</span></td>
                        </tr>
                    </table>


                                <table class="inventory">
                                    <thead>
                                        <tr>
                                            <th width="180"><span>Code</span></th>
                                            <th width="265"><span>Description</span></th>
                                            <th><span>Price</span></th>
                                            <th><span>Quantity</span></th>
                                            <th><span>Discount %</span></th>
                                            <th><span>Discount £</span></th>
                                            <th><span>Net £</span></th>
                                            <th><span>Tax %</span></th>
                                            <th><span>VAT £</span></th>
                                        </tr>
                                    </thead>


                                    @{
                                        if (Model.OrderLines == null)
                                        {
                                            Model.OrderLines = new List<Accounts.Models.OrderLines>();
                                            Accounts.Models.OrderLines Line = new Accounts.Models.OrderLines();
                                            Line.CustomerID = Model.CustomerID;
                                            Model.OrderLines.Add(Line);
                                        }

                                        foreach (var item in Model.OrderLines)
                                        {
                                            Html.RenderPartial("orderline", item);
                                        }
                                    }

                                </table>


                            <a class="add" onclick="NewRow()">+</a>
                            <table class="balance">
                                <tr>
                                    <th><span >Net Items</span></th>
                                    <td><span data-prefix>£</span><span>600.00</span></td>
                                </tr>
                                <tr>
                                    <th><span >Net Discount</span></th>
                                    <td><span data-prefix>£</span><span>000.00</span></td>
                                </tr>
                                <tr>
                                    <th><span >Net Tax</span></th>
                                    <td><span data-prefix>£</span><span>000.00</span></td>
                                </tr>
                                <tr>
                                    <th><span >Amount Paid</span></th>
                                    <td><span data-prefix>£</span><span >0.00</span></td>
                                </tr>
                                <tr>
                                    <th><span >Balance Due</span></th>
                                    <td><span data-prefix>£</span><span>600.00</span></td>
                                </tr>
                            </table>
                </article>
                    <aside>
                        <h1><span >Additional Notes</span></h1>
                        <div >
                            <p>A finance charge of 1.5% will be made on unpaid balances after 30 days.</p>
                        </div>
                    </aside>


<script src="@Url.Content("~/Content/Invoicescript.js")"></script>
Jimbo Jones
  • 901
  • 3
  • 12
  • 35
  • Where is this code? Is it in a javascript file, or in the page markup (cshtml) itself? – Eric King Jul 24 '16 at 15:36
  • Within its own java script file. I have added how I am calling this in my questain – Jimbo Jones Jul 24 '16 at 15:46
  • As Alexei mentioned in his answer, you cannot run Razor code in a standalone js file, because the javascript is run in the browser, long after the Razor code has had a chance to be run (on the server). – Eric King Jul 24 '16 at 15:52
  • There is nothing in your code that makes sense. The reason for using `BeginCollectionItem` is to be able to add a new row using ajax which calls a server method that returns a partial view of the new table row (and then you add it in the success callback. –  Jul 25 '16 at 02:01
  • But you not even binding anything to your model and there is nothing that could post back so its unclear what your actually wanting to achieve with this. Suggest you look at the answers [here](http://stackoverflow.com/questions/29161481/post-a-form-array-without-successful/29161796#29161796) and [here](http://stackoverflow.com/questions/28019793/submit-same-partial-view-called-multiple-times-data-to-controller/28081308#28081308) –  Jul 25 '16 at 02:01

3 Answers3

2

Update

My original answer was complete crap. That method will work for simple values, even for data objects serialized to JSON, but Html helpers output formatted HTML with line breaks.

A solution is to render the template in a specific node and reference that from your script. Here's a working example.

Index.cshtml

<table id="target-table">
    <thead>
        <tr>
            <th>Row Num</th>
            <th>Dropdown</th>
        </tr>
    </thead>
    <tbody></tbody>
</table>

<button id="add-row-btn" type="button">Add Row</button>

<!-- Make sure to use type="text/html" so the browser doesn't try to run it -->
<script id="dropdown-template" type="text/html">
    @Html.DropDownList("SomeField", new SelectList(ViewBag.SomeList, "Value", "Text"), new { @class = "some-class" })
</script>
<script src="@Url.Content("~/Scripts/example.js")"></script>

example.js

(function () {
    var dropdownTemplate = document.getElementById('dropdown-template').innerText,
        table = document.getElementById('target-table'),
        tbody = table.getElementsByTagName('tbody')[0],
        button = document.getElementById('add-row-btn');

    function addRow() {
        var rowNum = tbody.rows.length + 1;
        var newRow = generateRow(rowNum);
        tbody.appendChild(newRow);
    }

    function generateRow(rowNum) {
        var emptyRow = document.createElement('tr');

        emptyRow.innerHTML = '<td>' + rowNum + '</td>' +
            '<td>' + dropdownTemplate + '</td>';

        return emptyRow;
    }

    button.addEventListener('click', addRow);
})();

Original Answer

If there code is in a .js file you won't be able to do what you want (I'm sure there is a way to have razor parse your JS, but not by default). One solution would be to set a variable in your view that could use the helper, and reference that in your JavaScript.

.cshtml

<script>
    window.dropdown_template = "@Html.DropDownList(...)";
</script>

.js

emptyColumn.innerHTML = '<td><a class="cut">-</a><span contenteditable>' +
window.dropdown_template +
'</span></td>' +  ...

As always, best practice would be to namespace your window variable, but I'll leave that to your implementation.

Robert Dennis
  • 141
  • 1
  • 4
  • I don't have enough rep to comment on anything else, so I hope you this here. From your updated answer i don't understand where your JavaScript is being called. – Robert Dennis Jul 24 '16 at 16:05
  • I get "undefined" in the column – Jimbo Jones Jul 24 '16 at 16:36
  • I'm guessing that you are including your script before the `window.dropdown_template` gets set. You need to make sure that the template gets set before the script runs. – Robert Dennis Jul 24 '16 at 17:25
  • I did this – Jimbo Jones Jul 24 '16 at 18:10
  • I've been such a fool! That's the end of answering code questions from my phone! My original answer won't work because of the html will be output with line breaks, which will break the JavaScript assignment. I'll update my answer with a solution that will actually work. – Robert Dennis Jul 24 '16 at 21:06
  • Your original answer was perfectly fine - whoever would use it just needed to add encoding code linked from my answer. For you new version it is probably better option to just go with existing templating engine than inventing your own, but that's personal choice. – Alexei Levenkov Jul 25 '16 at 16:42
1

You can't put Razor markup in standalone JS file (as it normally will not be processed by server side, also you can generate JS responses that look like JS file to client).

You can't put Razor markup in JavaScript inline on CSHTML page and expect it to produce different result every time JS is executed.

What you can do is inline result of Razor helper function to JavaScript generated by a page once for all client side calls on that page. You need to be careful to properly encode output of Razor @ or helper methods. Encoding techniques are covered in Using Razor within JavaScript.

Note: for what you are doing using some templating engine like handlebarsJS may be better option as you will not need to think about encoding HTML multiple times to fit in JavaScript.

Community
  • 1
  • 1
Alexei Levenkov
  • 94,391
  • 12
  • 114
  • 159
0

I found a working solution helped by largely by Robert Dennis. I pulled the whole new row back to the main view

Table within the main view

                                    <table class="inventory">
                                    <thead>
                                        <tr>
                                            <th width="180"><span>Code</span></th>
                                            <th width="265"><span>Description</span></th>
                                            <th><span>Price</span></th>
                                            <th><span>Quantity</span></th>
                                            <th><span>Discount %</span></th>
                                            <th><span>Discount Amt</span></th>
                                            <th><span>Net £</span></th>
                                            <th><span>Tax %</span></th>
                                            <th><span>VAT Amt</span></th>
                                        </tr>
                                    </thead>

                                    @{
                                        if (Model.OrderLines == null)
                                        {
                                            Model.OrderLines = new List<Account.Models.OrderLines>();
                                            Account.Models.OrderLines Line = new Account.Models.OrderLines();
                                            Line.CustomerID = Model.CustomerID;
                                            Model.OrderLines.Add(Line);
                                        }

                                        foreach (var item in Model.OrderLines)
                                        {
                                            Html.RenderPartial("orderline", item);
                                        }
                                    }

                                </table>

Next within the main view the new row and button to create it. The new row is wrapped within <script id="NewTable-Code" type="text/html"> allowing me to use all the razor I want and BeginCollectionItem plugin. BeginCollectionItem Allows me to capture the new rows which are posted and added into a db. The Code for this Post (Form) is not included as it would be out of the scope of the questain.

                         <a class="add" onclick="NewRow()">+</a>                  

                        <script id="NewTable-Code" type="text/html">
                      @using (Html.BeginCollectionItem("OrderLines"))
                      {

                            <td><a class="cut">-</a><span contenteditable>
                        @Html.DropDownList("StockCode", new SelectList(ViewBag.StockCodeList, "Value", "Text"),
                         new
                            {
                                @class = "form-control selectpicker ",
                                onchange = "this.form.submit();",
                                data_show_subtext = "true",
                                data_live_search = "true"
                            })
                                </span></td>

                        <td><span contenteditable>
                                    @Html.DropDownList("StockCode", new SelectList(ViewBag.AllStockList, "Value", "Text"),
                         new
                            {
                                @class = "form-control selectpicker ",
                                onchange = "this.form.submit();",
                                data_show_subtext = "true",
                                data_live_search = "true"
                            })
                         </span></td>

                            <td><span data-prefix>$</span><span contenteditable>0.00</span></td>

                            <td><span contenteditable>0</span></td>

                            <td><span contenteditable>0</span></td>

                            <td><span contenteditable>0</span></td>

                            <td><span contenteditable>0</span></td>

                            <td><span contenteditable>0</span></td>

                            <td><span data-prefix>$</span><span>0.00</span></td>
                            }
                        </script>
                        <table class="balance">
                            <tr>
                                <th><span>Net Items</span></th>
                                <td><span data-prefix>£</span><span>600.00</span></td>
                            </tr>
                            <tr>
                                <th><span>Net Discount</span></th>
                                <td><span data-prefix>£</span><span>000.00</span></td>
                            </tr>
                            <tr>
                                <th><span>Net Tax</span></th>
                                <td><span data-prefix>£</span><span>000.00</span></td>
                            </tr>
                            <tr>
                                <th><span>Amount Paid</span></th>
                                <td><span data-prefix>£</span><span>0.00</span></td>
                            </tr>
                            <tr>
                                <th><span>Balance Due</span></th>
                                <td><span data-prefix>£</span><span>600.00</span></td>
                            </tr>
                        </table>

Next the Partial View to handle excising records

  using HtmlHelpers.BeginCollectionItem
@{
    Layout = "";
}

@using (Html.BeginCollectionItem("OrderLines"))
{
    <tbody>
        <tr>
            <td>
                <a class="cut">-</a><span contenteditable>
                    @Html.DropDownList("StockCode", new SelectList(ViewBag.StockCodeList, "Value", "Text"),
                    new
                     {
                         @class = "form-control selectpicker",
                         @Value = @Model.Description,
                         onchange = "this.form.submit();",
                         data_show_subtext = "true",
                         data_live_search = "true"
                     })
                </span>
            </td>
            <td><span contenteditable>
                    @Html.DropDownList("StockCode", new SelectList(ViewBag.AllStockList, "Value", "Text"),
                                 new
                                    {
                                        @class = "form-control selectpicker ",
                                        onchange = "this.form.submit();",
                                        data_show_subtext = "true",
                                        data_live_search = "true"
                                    })
                </span></td>
            <td><span data-prefix>$</span><span contenteditable>150.00</span></td>
            <td><span contenteditable>4</span></td>
            <td><span contenteditable>0</span></td>
            <td><span contenteditable>0.00</span></td>
            <td><span contenteditable>0.00</span></td>
            <td><span contenteditable>0</span></td>
            <td><span data-prefix>$</span><span>0.00</span></td>
        </tr>
    </tbody>
}

    <script src="@Url.Content("~/Content/Invoicescript.js")"></script>
<script>
    $("#addItem").click(function () {
        $.ajax({
            url: this.href,
            cache: false,
            success: function (html) { $("#editorRows").append(html); }
        });
        return false;
    });

</script>

Finally the JavaScript within it own file

    function onContentLoad() {
    updateInvoice();


    var NewRowForTable = document.getElementById('NewTable-Code').innerText;
   // alert(dropdownStockCode.toString());

    var
    input = document.querySelector('input'),
    image = document.querySelector('img');

    function onClick(e) {
        var element = e.target.querySelector('[contenteditable]'), row;

        element && e.target != document.documentElement && e.target != document.body && element.focus();

        if (e.target.matchesSelector('.add')) {
            document.querySelector('table.inventory tbody').appendChild(generateTableRow(NewRowForTable));
        }
        else if (e.target.className == 'cut') {
            row = e.target.ancestorQuerySelector('tr');

            row.parentNode.removeChild(row);
        }

        updateInvoice();
    }

    function onEnterCancel(e) {
        e.preventDefault();

        image.classList.add('hover');
    }

    function onLeaveCancel(e) {
        e.preventDefault();

        image.classList.remove('hover');
    }

    function onFileInput(e) {
        image.classList.remove('hover');

        var
        reader = new FileReader(),
        files = e.dataTransfer ? e.dataTransfer.files : e.target.files,
        i = 0;

        reader.onload = onFileLoad;

        while (files[i]) reader.readAsDataURL(files[i++]);
    }

    function onFileLoad(e) {
        var data = e.target.result;

        image.src = data;
    }

    if (window.addEventListener) {
        document.addEventListener('click', onClick);

        document.addEventListener('mousewheel', updateNumber);
        document.addEventListener('keydown', updateNumber);

        document.addEventListener('keydown', updateInvoice);
        document.addEventListener('keyup', updateInvoice);

        input.addEventListener('focus', onEnterCancel);
        input.addEventListener('mouseover', onEnterCancel);
        input.addEventListener('dragover', onEnterCancel);
        input.addEventListener('dragenter', onEnterCancel);

        input.addEventListener('blur', onLeaveCancel);
        input.addEventListener('dragleave', onLeaveCancel);
        input.addEventListener('mouseout', onLeaveCancel);

        input.addEventListener('drop', onFileInput);
        input.addEventListener('change', onFileInput);
    }
}

    function generateTableRow( NewRowForTable) {
    var emptyColumn = document.createElement('tr');

    emptyColumn.innerHTML = NewRowForTable;

    return emptyColumn;
}
Jimbo Jones
  • 901
  • 3
  • 12
  • 35