简体   繁体   中英

Razor in Javascript

I don't know how to use razor syntax in Javascript. I want to make Html.ListBoxFor with items from my model. I used to use:

@Html.ListBoxFor(x => x.TagIdList, (MultiSelectList)ViewBag.Tags, new { @class = "chzn-select", data_placeholder = "Tags..." })

As you see I want also use chzn-select class, to have better layout.

For now, I just have this code above in HTML as plain text, but I want have there things from my model.

Any ideas?

There is my code in ASP.NET MVC:

@model Generator.Models.ExamModel

@{
    ViewBag.Title = "Generate";
}

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
    <script src="@Url.Content("~/Multiple_chosen/chosen.jquery.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/ListOfTags.js")" type="text/javascript"></script>
    <script >    
        $(".chzn-select").chosen();
    </script>
}

<link href="@Url.Content("~/Multiple_chosen/chosen.css")" rel="stylesheet" type="text/css" />

<h1>@ViewBag.Title</h1>
<h2>@ViewBag.Message</h2>

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

    <fieldset>
        <legend>Generate</legend>


        <div class="editor-label">Numbers</div>
        <div class="editor-field" id="NumberOfModels">
            @Html.EditorFor(model => model.NumberOfQuestions)
        </div>

        <div class="editor-label">Tags</div>
        <div id="itemsmodel"></div>
        <br>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

And there is javascript file:

var models = document.getElementById("NumberOfQuestions");
var modelsTable = document.getElementById("itemsmodel");

models.addEventListener("change", drawModels, false);

function drawModels() {
    var modelsNum = parseInt(models.value);
    var curModels = modelsTable.childElementCount;

    if (modelsNum > curModels) {
        var delta = modelsNum - curModels;
        for (var i = 0; i < delta; i++) {
            var input = document.createElement("div");
            input.className = "editor-field";
            input.innerHTML = "@Html.ListBoxFor(x => x.TagIdList, (MultiSelectList)ViewBag.Tags, new { @class = \"chzn-select\", data_placeholder = \"Tags...\" })";
            modelsTable.appendChild(input);
        }
    } else {
        while (modelsTable.childElementCount > modelsNum) {
            modelsTable.removeChild(modelsTable.lastChild);
        }
    }
}

drawModels();

My ViewModel: ExamModel.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace ExamGenerator.Models
{
    public class ExaminationModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<int> TagIdList { get; set; }
        public int NumberOfQuestions { get; set; }
        public string Content { get; set; }
    }
}

My ActionResult Generate() in controller:

public ActionResult Generate()
{
    ViewBag.Tags = new MultiSelectList(genKolEnt.TAGS, "Id", "Name", null); 
    return View();
}

While you can generate HTML in Javascript using Razor, if the Javascript is in an MVC view, I find that injecting into JS leads to maintenance problems. You ideally want all your JS in separate files to allow for bundling/caching and the ability to break-point the JS code (which is harder in the view).

Either inject only simple things into JS on the page, or inject elements instead.

You can inject your template Razor list into a dummy script block, so you can extract the html from it later. The type="text/template" means the browser will ignore it eg:

<script id="ListTemplate" type="text/template">
    @Html.ListBoxFor(x => x.TagIdList, (MultiSelectList)ViewBag.Tags, new { @class = "chzn-select", data_placeholder = "Tags..." })
</script>

The view page now looks like this (left out the irrelevant parts):

@section styles{
    <link href="@Url.Content("~/Multiple_chosen/chosen.css")" rel="stylesheet" type="text/css" />
}

<h1>@ViewBag.Title</h1>
<h2>@ViewBag.Message</h2>
<script id="ListTemplate" type="text/template">
    @Html.ListBoxFor(x => x.TagIdList, (MultiSelectList)ViewBag.Tags, new { @class = "chzn-select", data_placeholder = "Tags..." })
</script>
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Generate</legend>


        <div class="editor-label">Numbers</div>
        <div class="editor-field" id="NumberOfModels">
            @Html.EditorFor(model => model.NumberOfQuestions)
        </div>

        <div class="editor-label">Tags</div>
        <div id="itemsmodel"></div>
        <br>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

Script now looks like this (jQuery version with JS as comments):

// ListOfTags.js file

// This is a shortcut DOM ready handler for $(document).ready(function(){ YOUR CODE HERE })
$(function () {
    // Attach an event handler for the "change" event
    $('#NumberOfQuestions').change(function () {

        var $numberOfQuestions = $(this);                       // Convert current DOM element (the counter) to a jQuery element
        var $modelsTable = $('#itemsmodel');                    // document.getElementById("itemsmodel");
        var modelsNum = ~~$numberOfQuestions.val();             // parseInt(models.value);
        var curModels = $modelsTable.children().length;         // modelsTable.childElementCount

        var delta = modelsNum - curModels;

        // While too few, add more
        while (delta > 0) {
            var $input = $('<div>').addClass('editor-field');   // document.createElement("div"); .className = "editor-field";
            var template = $('#ListTemplate').html();           // Fetch the template from a script block (id="ListTemplate")
            $input.html(template);                              // input.innerHTML = 
            $modelsTable.append($input);                        // modelsTable.appendChild(input);
            delta--;
        }

        // While too many, remove the last
        while (delta++ < 0) {
            $modelsTable.children().last().remove();            // modelsTable.removeChild(modelsTable.lastChild);
        }

    }).change();        // Trigger an initial change event so it runs immediately
});

Notes/tips:

  • Place any JS in the page, at the bottom of the view, as it is easier to find. It does not matter where the @section Scripts is as the master page determines where it is injected on the final page.
  • Always use single quotes ( ' ) in Javascript constants by default, so that nested strings can be " which are more often required than ' s. Just a good habit to get into. In fact if you had used them your code may have worked as you have added \\ escaping to the quotes which will mess up the Razor processing

eg:

= '@Html.ListBoxFor(x => x.TagIdList, (MultiSelectList)ViewBag.Tags, new { @class = "chzn-select", data_placeholder = "Tags..." })';
  • If you add a @RenderSection("styles", required: false) to your master page(s) you can do the same thing for CSS as you do for scripts (ensuring all CSS is loaded in the header (for consistency). Just place them in a @section styles block.

eg

<head>
    ...
    @Styles.Render("~/Content/css")
    @RenderSection("styles", required: false)
    ...
</head>
  • ~~ is a handy (and fast) alternative to parseInt to convert values to integers.
  • Use $ as a prefix for jQuery object variables. This makes it easier to remember when to use jQuery methods vs DOM properties.

Test controller code:

    private MultiSelectList TagList()
    {
        var items = new List<KeyValuePair<int, string>>() { 
            new KeyValuePair<int, string>(1, "MVC"), 
            new KeyValuePair<int, string>(2, "jQuery"), 
            new KeyValuePair<int, string>(3, "JS"), 
            new KeyValuePair<int, string>(4, "C#"), 
            new KeyValuePair<int, string>(5, "PHP")
        };
        MultiSelectList list = new MultiSelectList(items, "key", "value", null);
        return list;
    }

    // Get request starts with one list
    public ActionResult Test()
    {
        ExamModel vm = new ExamModel()
        {
            NumberOfQuestions = 1,
            TagIdList = new List<int>()
        };
        ViewBag.Tags = TagList();
        return View(vm);
    }

    [HttpPost]
    public ActionResult Test(ExamModel model)
    {
        ViewBag.Tags = TagList();
        return View(model);
    }

If it's a static JavaScript file and you are not generating it dynamically with razor view engine It won't work because in this case there is no processing performed on a server side. It is the same as accessing static html page/css file/image and etc...

On the other hand if this JavaScript is part of some Razor view, which means that it gets rendered by razor view engine, when you have return View() (or anything like that) in your controller action, than this code should work.

The problem is, java script files are not processed by server, so you won't be able to insert anything in those using ASP.NET MVC. Razor files on the other hand are processed on server so you can insert data into those (either through view bag or model).

One way is:

.cshtml:

<script>
    var someVariable = '@model.data';    
</script>

then use this variable in your javascript file:

function someFunction(){
  var myData = window.someVariable;
}

The other way is to have all javascript in .cshtml file and render it as a partial view.

@Html.Partial("Path/to/javascript/in/razor/view")

edit: seeing your code, this will not help you very much.

If you want to dynamically add/remove dom elements, you will have to do it with javascript: either generate them with "document.createElement()" or load them via ajax if you want some server side processing.

@Html.ListBoxFor

is a server side helper that generates tag and fills it up depending on the parameters. You can do that with javascript as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM