I want to make a javascript that can dynamic insert a new "EditorFor" model, but i am having 2 problem.
1: i keep html encoding the string, and i can't figure out how to stop it.
2: How can i tell it what model to use instead of having a instance of it in my model
I have tryed the following but it dose not work :(
MvcHtmlString emodel = Html.EditorFor(model => new Cosplay.Models.Projects.CreatePartModel(), "CreatePartModel", "Parts[NAMEREPLACE]");
MvcHtmlString emodel = Html.EditorFor(model => Cosplay.Models.Projects.CreatePartModel, "CreatePartModel", "Parts[NAMEREPLACE]");
Here is my full javascript.
#model Cosplay.Models.Projects.CreatePartsModel
#{
ViewBag.Title = "AddParts";
}
<script type="text/javascript">
#{
MvcHtmlString emodel = Html.EditorFor(model => Cosplay.Models.Projects.CreatePartModel, "CreatePartModel", "Parts[NAMEREPLACE]");
string editor = emodel.ToString().Trim().Replace("\"", "\\\"");;
}
function getPartHtml(name) {
var html = '#editor';
return html.replace("NAMEREPLACE", name);
}
$(document).ready(function () {
var lastCount = 5;
$("#addPartInput").click(function () {
lastCount++;
$('#edit_part_list').append(getPartHtml(lastCount));
});
});
</script>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<div id="edit_part_list">
#Html.EditorFor(model => Model.Parts)
</div>
<a id="addPartInput">Add another part</a><br />
<input type="submit" value="Save" />
}
Edit:
I have fixed problem 2 with the following, but problem 1 is still there.
Html.Editor("Parts[X]", "CreatePartModel").ToString().Replace("\"", "\\\"").Replace("\r\n", "\\n");
Edit 2:
I have found that the following code will remove all input fields inside the edit model :(
Html.Editor("Parts[X]", "CreatePartModel")
Try like this:
<div id="foo"></div>
<script type="text/javascript">
var html = '#Html.Raw(HttpUtility.JavaScriptStringEncode(Html.EditorFor(x => x.Name).ToHtmlString()))';
$('#foo').html(html);
</script>
This way you don't need any replacing. It will also take care of properly encoding.
Related
I have the following line of code which uses html helper's PagedListPager:
#Html.PagedListPager(Model.kyc_paged_list, page => Url.Action("ClientDetails", new { id = ViewBag.ID, kyc_page = page, transaction_page = Model.transaction_page, active = "kyc" }))
When clicking on the the pager the entire page reloads. But I only want the table with id="kyc-history" to refresh.
I know how to reload the table using a JavaScript function but I don't know how to call it from the PagedListPager.
Any ideas how I can call it? Let's say that the JS function is called reloadTable()
I was having the same problem and this article (done by Anders Lybecker) helped me out and now everything working!
It's very clear with steps, But make sure to make backup before starting!
Take a look:
AJAX paging for ASP.NET MVC sites
In summary you make 2 Action Results in your Controler with 2 Views 1 for your page (in my case it's Index View) the other for the List that contain the PagedListPager control (List View). And put the list View inside the Index View. And write the JQuery code for the PagedListPager in the Index View.
You can read the article for the details!
And this is my code with a little extra things I noticed to help you more:
The List View:
#model IPagedList<StudentRegSys.Models.Student>
#{
Layout = null;
}
#using PagedList.Mvc;
#using PagedList;
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400italic,400,600,700" rel="stylesheet">
#Styles.Render("~/template/css")
<div class="container">
<div class="row">
<!-- The List Code-->
<div class="pagination-control">
#Html.PagedListPager(Model, i => Url.Action("List", "Home", new { i, search = Request.QueryString["search"] }))
</div>
</div>
</div>
#Scripts.Render("~/template/js")
Note: Make sure to make Layout = null; and put the Styles Links & the Scripts manual in this View to avoid design issues.
In the Controller: (it's Home Controller in my case)
// GET: /Home/Index
public ViewResult Index()
{
return View();
}
// GET: /Home/List
public ActionResult List(int? i, string search = "")
{
try
{
var students = _context.Student.Include(s => s.Major)
.OrderBy(s => s.Name)
.Where(s => s.Name.Contains(search) || s.Major.Name.Contains(search) ||
s.Address.Contains(search) || s.Phone.Contains(search))
.ToList().ToPagedList(i ?? 1, 8);
return View(students);
}
catch (Exception)
{
return HttpNotFound();
}
}
The Index View:
#{
ViewBag.title = "Home";
}
<section id="intro">
<!-- Some Code -->
</section>
<section id="maincontent">
#Html.Action("List") <!-- to call the List view -->
</section>
<script>
$(document).ready(function () {
$(document).on("click", ".pagination-control a[href]", function () {
$.ajax({
url: $(this).attr("href"),
type: 'GET',
cache: false,
success: function (result) {
$('#maincontent').html(result);
}
});
return false;
});
});
</script>
Note: Make sure to put the html(result) in the root container of the list in my case was <section id="maincontent">.
Hope this help you out :)
In my MVC Index page, for each element in the viewmodel, it renders via a partial view.
I then need to run a small script on each of these partial views and am trying to use jQuery.Each() however, i cannot seem to get it to iterate as the $get is always returning 1, not the actual number of elements in the $get.
Index.cshtml
#model IEnumerable<ViewModels.DummyVM>
#{
ViewBag.Title = "index";
}
<h2>index</h2>
<div>
#for (int i = 1; i <= 10; i++)
{
#Html.Partial("pv", i);
}
</div>
#section Scripts
{
<script type="text/javascript">
$(function () {
alert($("#LogEntry").length);
})
</script>
}
PV.cshtml
#model int
<div id="LogEntry">
Log Entry : #(Model)
</div>
For the simplified test above, once the page has .ready() then iterates through and dumps to console but I'm only getting Log Entry : 0
id should be unique in same document use classes instead :
#model int
<div class="LogEntry">
Log Entry : #(Model)
</div>
Then use class selector . in your JS code :
$(function () {
alert($(".LogEntry").length);
})
If you could not edit your PV.cshtml page you could use :
$(function () {
alert($("[id='LogEntry']").length);
})
Hope this helps.
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 e.g.:
<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
e.g.:
= '#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.
e.g.
<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.
This is a MVC VB.NET Razor application. I have a partial view which loads in the bottom of a parent view. And in that partial view I have buttons that when click fire a popup dialog modal window which has a partial view attached to it. The user is supposed to be able to edit the form then click update and the information is then posted to the controller. However I am getting the below error message on submit.
I followed the blog here to get everything wired up. When the update button is clicked there error is occuring here:
Below is the PartialView that contains the buttons and javascript that trigger the popup modal
#ModelTYPE IEnumerable(of data_manager.attendance)
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascrip</script>
<table>
<tr>
<th>Conf. Number</th>
<th>Class Title</th>
<th>Status of Class</th>
<td>Edit</td>
</tr>
#For Each x In Model
Dim currentItem = x
#<tr>
<td>#Html.DisplayFor(Function(f) currentItem.conf_number)</td>
<td>#Html.DisplayFor(Function(f) currentItem.courseTitle)</td>
#If currentItem.Completed_Class = "Completed" Then
#<td>#Html.ActionLink("Completed(Print Cert)", "Ind_Cert", "Printing", New With {.firstName = currentItem.firstName, .lastname = currentItem.lastName, .classRef = currentItem.course_ref, .cNumber = currentItem.conf_number}, Nothing)</td>
Else
#<td>#Html.DisplayFor(Function(f) currentItem.Completed_Class)</td>
End If
<td>#Html.ActionLink("Modify", "CourseHistoryEdit", New With {.id = currentItem.id}, New With {.class = "editLink"})</td>
</tr>
Next
</table>
<div id="updateDialog" title="Update Attendance"></div>
<script type="text/javascript">
var linkObj;
$(function () {
$(".editLink").button();
$('#updateDialog').dialog({
autoOpen: false,
width: 400,
resizable: false,
modal: true,
buttons: {
"Update": function () {
$("#update-message").html(''); //make sure there is nothing on the message before we continue
$("#updateAttendance").submit();
},
"Cancel": function () {
$(this).dialog("close");
}
}
});
$(".editLink").click(function () {
//change the title of the dialgo
linkObj = $(this);
var dialogDiv = $('#updateDialog');
var viewUrl = linkObj.attr('href');
$.get(viewUrl, function (data) {
dialogDiv.html(data);
//validation
var $form = $("#updateAttendance");
// Unbind existing validation
$form.unbind();
$form.data("validator", null);
// Check document for changes
$.validator.unobtrusive.parse(document);
// Re add validation with changes
$form.validate($form.data("unobtrusiveValidation").options);
//open dialog
dialogDiv.dialog('open');
});
return false;
});
});
function updateSuccess(data) {
if (data.Success == true) {
//we update the table's info
var parent = linkObj.closest("tr");
parent.find(".Completed_Class").html(data.Object.completed);
parent.find(".carDescription").html(data.Object.Description);
//now we can close the dialog
$('#updateDialog').dialog('close');
//twitter type notification
$('#commonMessage').html("Update Complete");
$('#commonMessage').delay(400).slideDown(400).delay(3000).slideUp(400);
}
else {
$("#update-message").html(data.ErrorMessage);
$("#update-message").show();
}
}
</script>
And this is the partialView that is rendered when the Modify button is clicked next to each one.
#ModelTYPE DataModels.DataModels.AjaxCourseHistoryEdit
#Using (Ajax.BeginForm("CourseHistoryEdit", "Admin", Nothing, New AjaxOptions With {.InsertionMode = InsertionMode.Replace, .HttpMethod = "POST", .OnSuccess = "updateSuccess"}, New With {.id = "updateAttendance"}))
#Html.ValidationSummary(true)
#<fieldset>
<legend>Attendance Update</legend>
#Html.HiddenFor(Function(m) Model.attendId)
<div class="editor-label">
#Html.Label("Course Title")
</div>
<div class="editor-field">
#Html.DisplayFor(Function(m) Model.courseTitle)
</div>
<div class="editor-label">
#Html.Label("Completed Status")
</div>
<div class="editor-field">
#Html.DropDownList("completed", New SelectList(ViewBag.CourseStatuses))
</div>
<div class="editor-label">
#Html.Label("Hours Completed")
</div>
<div>
#Html.EditorFor(Function(m) Model.hoursCompleted)
</div>
</fieldset>
End Using
Below are the javascript libraries that are being loaded in the _layout file for the project.
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"> </script>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
Any help is greatly appreciated. I have went around with this for hours and google searches have turned up several SO posts saying that Unexpected token u is related to an invalid line termination. This helps me none as I cannot find anything that remotely looks like improper html namely tags that arent closed..
I had a csharper bring up the # on the table and fieldset. This is normal in these instances for vb.net below is a screenshot of the rendered html
A comment made by Moeri pointed me in the right direction. It turned out that my model was using a integer value for the hiddenFor value. Which for reasons unknown to me the AJAX post did not like that at all. By changing the type of attendId from Integer to String and further using proper editorFor / labelFor the issue has been resolved. Maybe this will help someone that hits this stumbling block as I have.
Friends,
I am trying to use DyGraph in my application. Please look at the code below -
<head>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7; IE=EmulateIE9">
<title>crosshairs</title>
<script type="text/javascript" src="dygraph-combined.js"></script>
<script type="text/javascript" src="data.js"></script>
</head>
The code uses data.js file containing function to get some static data.
I want data.js to be generated using a controller method so that it will generate data using database.
Can anybody help me out to resolve this issue.
Thanks for sharing your valuable time.
You could define a controller action:
public ActionResult Data()
{
// Obviously this will be dynamically generated
var data = "alert('Hello World');";
return JavaScript(data);
}
and then:
<script type="text/javascript" src="<%= Url.Action("Data", "SomeController") %>"></script>
If you have some complex script that you don't want to generate in the controller you could follow the standard MVC pattern by defining a view model:
public class MyViewModel
{
... put required properties
}
a controller action which would populate this view model and pass it to the view:
public ActionResult Data()
{
MyViewModel model = ...
Response.ContentType = "application/javascript";
return PartialView(model);
}
and finally a view which in this case will be the javascript representation of the view model (~/Views/SomeController/Data.ascx):
<%# Control
Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<MyViewModel>" %>
alert(<%= new JavaScriptSerializer().Serialize(Model.Name) %>);
Full Disclosure
This answer is copy/pasted from another question:
Dynamically generated Javascript, CSS in ASP.NET MVC
This answer is similar to other answers here.
This answer uses cshtml pages rather than ascx controls.
This answer offers a View-Only solution rather than a Controller-Only solution.
I don't think my answer is 'better' but I think it might be easier for some.
Dynamic CSS in a CSHTML File
I use CSS comments /* */ to comment out a new <style> tag and then I return; before the closing style tag:
/*<style type="text/css">/* */
CSS GOES HERE
#{return;}</style>
Dynamic JS in a CSHTML File
I use JavaScript comments // to comment out a new <script> tag and then I return; before the closing script tag:
//<script type="text/javascript">
JAVASCRIPT GOES HERE
#{return;}</script>
MyDynamicCss.cshtml
#{
var fieldList = new List<string>();
fieldList.Add("field1");
fieldList.Add("field2");
}
/*<style type="text/css">/* */
#foreach (var field in fieldList) {<text>
input[name="#field"]
, select[name="#field"]
{
background-color: #bbb;
color: #6f6f6f;
}
</text>}
#{return;}</style>
MyDynamicJavsScript.cshtml
#{
var fieldList = new List<string>();
fieldList.Add("field1");
fieldList.Add("field2");
fieldArray = string.Join(",", fieldList);
}
//<script type="text/javascript">
$(document).ready(function () {
var fieldList = "#Html.Raw(fieldArray)";
var fieldArray = fieldList.split(',');
var arrayLength = fieldArray.length;
var selector = '';
for (var i = 0; i < arrayLength; i++) {
var field = fieldArray[i];
selector += (selector == '' ? '' : ',')
+ 'input[name="' + field + '"]'
+ ',select[name="' + field + '"]';
}
$(selector).attr('disabled', 'disabled');
$(selector).addClass('disabled');
});
#{return;}</script>
No Controller Required (using Views/Shared)
I put both of my dynamic scripts into Views/Shared/ and I can easily embed them into any existing page (or in _Layout.cshtml) using the following code:
<style type="text/css">#Html.Partial("MyDynamicCss")</style>
<script type="text/javascript">#Html.Partial("MyDynamicJavaScript")</script>
Using a Controller (optional)
If you prefer you may create a controller e.g.
<link rel="stylesheet" type="text/css" href="#Url.Action("MyDynamicCss", "MyDynamicCode")">
<script type="text/javascript" src="#Url.Action("MyDynamicJavaScript", "MyDynamicCode")"></script>
Here's what the controller might look like
MyDynamicCodeController.cs (optional)
[HttpGet]
public ActionResult MyDynamicCss()
{
Response.ContentType = "text/css";
return View();
}
[HttpGet]
public ActionResult MyDynamicJavaScript()
{
Response.ContentType = "application/javascript";
return View();
}
Notes
The controller version is not tested. I just typed that off the top of my head.
After re-reading my answer, it occurs to me it might be just as easy to comment out the closing tags rather than use the cshtml #{return;}, but I haven't tried it. I imagine it's a matter of preference.
Concerning my entire answer, if you find any syntax errors or improvements please let me know.