Using java script code in MVC5 - where to put it - asp.net

Im having MVC5 application and in the view index.cshtml I need to use
some java script code ,currently I put the script code inside the view and its working fine.
My question is where should I put this code (from best practice) and how should
I refer to it from the view?please provide an example.

The approach I've written down below is my way of extracting JavaScript completely from your views.
better to maintain (js issues -> look in js files and not in views)
modular approach
clear separation
better to understand by design
In HTML5, use the data attribute to pass along variables from the Model.
This helps tremendously in porting variables from MVC (your viewmodel) to javascript.
This also allows you to keep javaScript stored in separate files as you probably would like in an MVC environment.
1.1 Binding c# to HTML
<div class="news" data-js-params="websiteName=#LocationWebsiteHelper.CurrentLocationWebsiteName()&languageName=#languageName&page=0&itemsPerPage=#Model.MaxNumberOfItems">
1.2 JS Helper functions to convert data into object literals
Although built on jQuery, I've written 2 small functions which can help porting querystring variables into object literals and back. I use these throughout my js files:
// #param (qs): a query string of key value pairs (without ?)
// #param (keyDelimiter): string : character between values and keys
// #param (valDelimiter): string : character between keys and values
// #return (obj): an object literal
// #example: key1=val1&key2=val2&key3=val3
convertQsToLiteral: function (qs, keyDelimiter, valDelimiter) {
var arrParams, obj = {};
if (qs && qs.length) {
keyDelimiter = keyDelimiter || '&';
valDelimiter = valDelimiter || '=';
arrParams = qs.split(keyDelimiter);
$.each(arrParams, function (i, pair) {
var arrPair = pair.split(valDelimiter),
key = arrPair[0],
val = arrPair[1];
obj[key] = val;
});
}
return obj;
},
// #param (literal): an object literal key value paired of one level deep
// #param (keyDelimiter): string character between values and keys
// #param (valDelimiter): string : character between keys and values
// #return (string): array string representation
// #example: { key1: val1, key2: val2, key3: val3 }
convertLiteralToQs: function (literal, keyDelimiter, valDelimiter) {
var arrQs = [],
arrPairs, key;
keyDelimiter = keyDelimiter || '&';
valDelimiter = valDelimiter || '=';
for (key in literal) {
if (literal.hasOwnProperty(key)) {
arrPairs = [];
arrPairs.push(key, literal[key]);
arrQs.push(arrPairs.join(valDelimiter));
}
}
return arrQs.join(keyDelimiter);
},
1.3 Convert HTML data into js object literals
With these functions in mind you can pass any query string like variables into an object literal.
var dataParams = convertQsToLiteral($('.news').data('js-params')); // get data attr
var urlParams = convertQsToLiteral(window.location.search.substr(1)); // get url query string
1.4 Example: JS modular setup to extend and override object literals
Combined with jQuery's $.extend() function you can now override javascript objects in a modular approach (considering all closures a js file/module looks like this):
window.ProjectName = (function($, projectname){
// default object literal
var cfg = {
// your default options
idea: 'great'
};
// #param (options): something like the cfg object
projectname.Module = function (options) {
this.settings = $.extend(true, {}, cfg, options); // deep copy
this.init();
};
projectname.Module.prototype = {
init: function(){
this.idea = this.settings.idea;
console.log(this.idea);
}
};
return projectname;
}(window.jQuery, window.ProjectName));
1.5 Initializing a js module
var module = new ProjectName.Module({ idea: 'even better' });
2.1 Adding scripts/css to your views
You have a couple options for attaching scripts to your views/pages/blocks:
section defined in the baselayout (only for partial views, directly included into the baselayout)
c# ClientResources (not the best approach in MVC but still doable, allows you to include external files into a partial view -> view in view)
bundles (good or minification and modular approach)
2.2.1 baselayout setup for sections
#RenderSection("AdditionalJS", false)
2.2.2 usage partial view
#section AdditionalJS
{
<script>
var module = new ProjectName.Module({ idea: #Model.idea });
</script>
}
2.3.1 baselayout setup for view in view
#Html.Raw(Html.RequiredClientResources(RenderingTags.Header))
2.3.2 usage view in view
ClientResources.RequireScript("/Design/js/projectname.module.js").AtHeader();
2.4.1 BundleConfig setup for scripts
/// <summary>
/// Register the Javascript bundles
/// Separated in libJs, projectJs and polyfillJs
/// </summary>
/// <param name="bundles"></param>
private static void RegisterScripts(BundleCollection bundles)
{
// usage for libraries
bundles.Add(new ScriptBundle(
"~/bundles/libJs").Include(
"~/Design/js/lib/*.js"
));
// project object
bundles.Add(new ScriptBundle(
"~/bundles/projectJs").Include(
"~/Design/js/project.dev.js",
"~/Design/js/classes/*.js",
"~/Design/js/components/*.js"
));
// usage for browser support
bundles.Add(new ScriptBundle(
"~/bundles/polyfillJs").Include(
"~/Design/js/polyfills/*.js"
));
}
/// <summary>
/// Render scripts inside conditional comments
/// http://stackoverflow.com/questions/12865939/mvc4-bundling-minification-with-ie-conditional-comments
/// </summary>
/// <param name="ie"></param>
/// <param name="paths"></param>
/// <returns></returns>
public static IHtmlString RenderConditionalScripts(string ie, params string[] paths)
{
var tag = string.Format("<!--[if {0}]>{1}<![endif]-->", ie, Scripts.Render(paths));
return new MvcHtmlString(tag);
}
2.4.2 baselayout setup
...
<head>
...
#BundleConfig.RenderConditionalScripts("lte IE 9", "~/bundles/polyfillJs")
#Scripts.Render("~/bundles/libJs")
<head>
<body>
...
#Scripts.Render("~/bundles/projectJs")
</body>

You better use Razor #section for this.
In your Layout:
<html>
<head>
<title>My Title</title>
.....
#RenderSection("Scripts", false)
</head>
<body>
#RenderBody
</body>
</html>
In your View:
<div id="container">
<h3>Welcome!</h3>
...
</div>
#section Scripts
{
<script type="text/javascript">
// your script goes here
</script>
}
Some would prefer to place #RenderSection("Scripts") just before the </body> tag instead.

If you have and HTML 5 template it really doesn't matter where you place the JavaScript code, If you haver a XHTML template you should put your code inside the <head></head> tags.
Now with the best practices, right now the best practice is to put all your JS code just before your </body> closing tag. This way you make sure that your html elements tags have been parse by the browser.
When going to a production environment the best is to concatenate all your JS into a single .js file and then have it minify, that way you would have an only small js file that the client browser needs to fetch.
Minify your code
The term minify in frontend code (css/js) stands for a process where you trim all your spaces and line breaks, and also the function scope variables get replace with a shorter name, usually just a vowel.
function foo(someVar){
return someVar;
}
when minified gets replaced to:
function foo(e){return e}
In MVC4 there was the Bundling and Minification feature that could help you with this. In MVC5 I'm not that sure.
Further Reading: http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification

Related

accessing nested fields in handlebars when rendering from vert.x

I'm trying to render a page with an object having named fields:
{
"context":{
"greeting":"hello"
}
}
And I'm rendering this with a very simple template:
<html>
<body>
<div class="page">
{{#with context}}
<h1>{{greeting}} or {{this.greeting}}</h1>
{{/with}}
<h1>{{greeting}} or {{context.greeting}}</h1>
</div>
<div>the context is actually: {{context}} </div>
</body>
</html>
As you can see above, I'm currently trying out several ways of rendering the greeting value. in earlier versions of this template I have tried them all one at a time too.
At the end of the template, I'm rendering the entirety of the context variable, just to ensure that the data I pass in is actually present. here is a screenshot of the result:
Having read the docs here and a tutorial here I really can't see what I'm doing wrong, can someone clarify?
I should add that I'm using io.vertx:vertx-web-templ-handlebars:3.3.3 to render this
Below is the java method that returns this template. I'm using the Vert.x rendering engine.
private void handleStatus(RoutingContext ctx, TemplateEngine engine, String template)
{
JsonObject json = new JsonObject();
json.put("greeting", "hello");
ctx.put("context", json);
engine.render(ctx, template, res ->
{
if (res.succeeded())
{
ctx.response().end(res.result());
}
else
{
ctx.fail(res.cause());
}
});
}
and here is where that method gets called from:
TemplateEngine engine = HandlebarsTemplateEngine.create();
statusHandler = new StatusHandler(vertx);
statusHandler.start();
deploymentHandler = new DeploymentHandler(vertx);
router.get("/monitor/apistatus").handler(ctx -> handleStatus(ctx, engine, "templates/apistatus.hbs"));
Prior to 3.4.0, the Vert.x Web Handlebars template engine did not work well with JsonObject and JsonArray.
Upgrade to 3.4/3.5, or as a workaround, you could convert your JsonObject to a Map.

Meteor - TRIPLE template tag is not allowed in an HTML attribute error

I got error message when trying to run existing meteor project.
$meteor
=> Started proxy.
=> Started MongoDB.
=> Errors prevented startup:
While building the application:
client/coinmx.html:169: TRIPLE template tag is not allowed in an HTML attribute
...title="Totals: {{{get...
^
In Meteor 0.8, it's possible to return a Javascript object which is directly rendered into HTML attributes versus earlier versions, where you had to render it yourself.
Old version:
<input name={{name}} title={{title}}>
helpers:
Template.foo.name = "fooName";
Template.foo.title = "fooTitle";
New version:
<input {{attributes}}>
helpers:
Template.foo.attributes = {
name: "fooName",
title: "fooTitle"
};
All of these can be functions, and reactive, etc. Because the object is rendered directly into attributes, there is no need for you to SafeString some manually rendered content as before. This is the recommended way to go if need to render HTML attributes.
See also the following for how conditional attributes work under this scheme:
https://github.com/meteor/meteor/wiki/Using-Blaze#conditional-attributes-with-no-value-eg-checked-selected
The error is pretty much explanatory: you cannot use {{{something}}} inside a HTML attribute, you need to use {{something}} instead. Depending on what the something is (it's not known from your question as you didn't provide the code), that's either all you need to do, or you can achieve similar functionality by returning new Handlebars.SafeString("result") from your helper instead of just "result". However, if you do, you need to be super sure that the thing you'll return won't break the HTML structure.
Hugo's answer above gave me the missing piece I needed for the same issue-- triple stashes in 0.8 no longer supported. Here is an example that hopefully helps.
Where you might have had {{{resolve}}} in your template, you would now do:
<template name='thing'>
<ol>
{{#each all}}
{{resolve}}
{{/each}}
</ol>
<template>
The helper code then makes use of Spacebars.SafeString which looks to be preferred with Blaze:
Template.thing.helpers({
all: function () {
return Things.find();
},
resolve: function () {
var result = "<li>";
for (var i = 0; i < this.arrayOfClassNames.length; ++i)
result += <'div class='" + this.arrayOfClassNames[i] + "'></div>";
result += "</li>";
return new Spacebars.SafeString(result);
}
});
The key here is to return the 'new Spacebars.SafeString(result)' to wrap your HTML (which must be well formed).

Handlebars : register helper based on template with 'body content'

I'm trying to implement some kind of 'macro' mechanism in a nodejs application using Handlebars (similar to the #bodyContent system in velocity.)
In my main template, I want to be able to write something like this :
{{#foobar who = user }}
<p>My body content</p>
{{/foobar}}
In a "views/helpers/foobar.html", I would have a file with a template, and some way to reference the "body content"
<p>Hello {{ who }}<p>
{{ bodyContent }}
<p>Bye !</p>
Based on the convention that the templates in "views/helpers" corresponds to a helper called with a single hash parameter, I want to automatically register them ; so I have something like this :
var helpers = "./views/helpers/";
fs.readdirSync(helpers).forEach(function (file) {
var source = fs.readFileSync(helpers + file, "utf8"),
helperName = /(.+)\.html/.exec(file).pop();
var helperTemplate = Handlebars.compile(source);
// We assume all helpers in the folder
// would take a hash as their first param
// We'll provide them with all the required context
Handlebars.registerHelper(helperName, function (options) {
var hash = options.hash || {};
// I want to somehow 'pass' the body Content ;
// The closest I have is 'this', but the markup is
// encoded, so I get a string with '<p>My body content</p>'
hash.bodyContent = options.fn(this);
console.log("Body Content", hash.bodyContent);
// Render the source as an handlebar template
// in the context of a hash
return helperTemplate(hash);
});
});
This does not work, as the tags are escaped, and so bodyContent is a String containing the markup, instead of the markup.
Is there a way I can fix my helper registration, or a built in mechanism in Handlebars to deal with this ?
Thanks
You need to use the {{{triple stashes}}} to unescape the HTML injection. So your template should look like:
<p>Hello {{ who }}<p>
{{{ bodyContent }}}
<p>Bye !</p>
You can read more here

Does handlebars.js support promises?

Some properties of my model should be loaded asynchronously (implemented by promises). I don't want to wait it - I want to render it right now and partially update it when promises will be resolved. Does handlebars.js support it?
Handlebars does not support async. This makes it impossible to use Promises for expressions and helpers. You could use await but this suspends execution, so no rendering takes place until the Promise is settled. Other templating frameworks like dust.js offer async functionality.
I think i can propose a workaround to get the async (or promises) to (seem to) work. Below is the example. Essentially this is what i am doing
First return a div with an unique Id (i am using namespace + auto increment here)
Do your processing (ajax or what ever slowly). Once done replace the innerHTML of the div in step1 with new data by accessing it via the unique id
code below. Observe the tripple bracket ({{{ }}}). This is to make sure generated HTML is not escaped
<script id="handlebars-demo" type="text/x-handlebars-template">
<div>
-->> {{{functionName "functionParam" }}} <<--
</div>
</script>
<div id="output" />
<script>
window.id=0;
Handlebars.registerHelper('functionName', function(funcParam ){
let tempId = "my_namespace_" + window.id++;
$.post("https://something.com/abc").done(function(data){
$("#"+tempId ).html(data.something);
}
)
return '<div id='+tempId+'>loading</div>'
});
var template = document.getElementById("handlebars-demo").innerHTML;
var templateScript = Handlebars.compile(template);
var context = { };
var html = templateScript(context);
document.getElementById("output").innerHTML= html;
</script>

MVC Mini Profiler includes not respecting application's path

I've got the MVC Mini Profiler set up as described on its project page, and the includes are indeed being written on the page.
Problem is, my application sits at http://localhost:8080/web, and the markup written by the profiler includes looks like this:
<link rel="stylesheet/less" type="text/css" href="/mini-profiler-includes.less?v=2.0.4177.17902">
<script type="text/javascript" src="/mini-profiler-includes.js?v=2.0.4177.17902"></script>
<script type="text/javascript"> jQuery(function() { MiniProfiler.init({ id:'fb4dc30e-c1aa-4be6-902c-ef2812dd1fe2', renderDirection:'left' }); } ); </script>
These all of course give 404 errors, but if I navigate to /web/mini-profiler-includes.less?, it loads fine.
The source that creates that string can be found here:
// MiniProfilerHandler.cs
/// <summary>
/// Understands how to route and respond to MiniProfiler UI urls.
/// </summary>
public class MiniProfilerHandler : IRouteHandler, IHttpHandler
{
internal static HtmlString RenderIncludes(MiniProfiler profiler, RenderPosition? position = null, bool showTrivial = false, bool showTimeWithChildren = false)
{
const string format =
#"<link rel=""stylesheet/less"" type=""text/css"" href=""{0}mini-profiler-includes.less?v={1}"">
<script type=""text/javascript"" src=""{0}mini-profiler-includes.js?v={1}""></script>
<script type=""text/javascript""> jQuery(function() {{ MiniProfiler.init({{ id:'{2}', path:'{0}', renderDirection:'{3}', showTrivial: {4}, showChildrenTime: {5} }}); }} ); </script>";
var pos = position ?? (MiniProfiler.Settings.RenderPopupButtonOnRight ? RenderPosition.Right : RenderPosition.Left);
var result = profiler == null ? "" : string.Format(format,
EnsureEndingSlash(HttpContext.Current.Request.ApplicationPath),
MiniProfiler.Settings.Version,
profiler.Id,
pos.ToString().ToLower(),
showTrivial ? "true" : "false",
showTimeWithChildren ? "true" : "false");
return new HtmlString(result);
}
// rest of the code
}
Why isn't Request.ApplicationPath returning my application's path? Am I doing something wrong, or should I file an issue on the mvc-mini-profiler page?
EDIT: To make things even weirder, I put a breakpoint on the MiniProfiler.RenderIncludes() call, and checked what the value of HttpContext.Current.Request.ApplicationPath was at that moment, and it was "/web"! Very mysterious.
EDIT 2: Looks like they may have added support for virtual paths in the latest version (2 hours ago :)), and the NuGet package (which is how I installed it) is not completely up to date. Investigating...
Pulling the latest source (this commit being the most recent at time of this post), building the project, grabbing the DLL and referencing that instead of using the project's NuGet package fixed the problem.
EDIT: As of right now, the NuGet package is now up to date with the latest commit, so NuGet away!

Resources