Running JavaScript inside a Handlebars Template - javascript

I want to execute a bit of Javascript code inside my handlebars template. Typically in the application I do this
<script type="text/javascript">
var #Model.JavascriptVariableName;
$(function () {
#Model.JavascriptVariableName = new TagInput()
.withAvailableTags(#Html.Raw(Model.AvailableTagsJson))
.withAppliedTags(#Html.Raw(Model.AppliedTagsJson))
.withMinCharsAutocomplete(#Model.MinCharsAutocomplete)
.allowBackspaceDelete(#Model.DeleteWithBackspace.ToString().ToLowerInvariant())
.allowNewTags(#Model.AllowNewTags.ToString().ToLowerInvariant())
.initialize($('##Model.ElementId'), $('##(Model.ElementId)_hidden'));
#if(Model.OnChangeJavascript.IsNotNullOrEmpty()) {
#:#(Model.JavascriptVariableName).onChange = function () { #Html.Raw(Model.OnChangeJavascript) }
}
});
</script>
But since I am already inside of a handlebars template with I tried to just insert the $(function(){}) that just gets spit out as text which makes sense. So how then can I create a bit of dynamic Javascript inside of handlebars???
<script type="text/x-handlebars-template" id="tagsTemplate">
<div>Tags</div>
var #Model.JavascriptVariableName;
$(function () {
#Model.JavascriptVariableName = new TagInput()
.withAvailableTags(#Html.Raw(Model.AvailableTagsJson))
.withAppliedTags(#Html.Raw(Model.AppliedTagsJson))
.withMinCharsAutocomplete(#Model.MinCharsAutocomplete)
.allowBackspaceDelete(#Model.DeleteWithBackspace.ToString().ToLowerInvariant())
.allowNewTags(#Model.AllowNewTags.ToString().ToLowerInvariant())
.initialize($('##Model.ElementId'), $('##(Model.ElementId)_hidden'));
#if (Model.OnChangeJavascript.IsNotNullOrEmpty())
{
#:#(Model.JavascriptVariableName).onChange = function () { #Html.Raw(Model.OnChangeJavascript) }
}
});
</script>
The above code wont work.

Unless you are using handlebar to compile your webpage server side I don't see why you need to wait every time for the window Ready event: $(function(){}) in fact wrap a function to make it sure it will fire only when the DOM is ready (has been loaded).
You can simply skip that part in your code if you want.
I would strongly discourage to put JS code in a template: why don't you generalize that code and compile with Handlebars some DOM stuff with ids or classes that you can use in your "generic" function instead?

Related

Razor Syntax in External Javascript

So as you might know, Razor Syntax in ASP.NET MVC does not work in external JavaScript files.
My current solution is to put the Razor Syntax in a a global variable and set the value of that variable from the mvc view that is making use of that .js file.
JavaScript file:
function myFunc() {
alert(myValue);
}
MVC View file:
<script language="text/javascript">
myValue = #myValueFromModel;
</script>
I want to know how I can pass myValue directly as a parameter to the function ? I prefer to have explicit calling with param than relying on globals, however I'm not so keen on javascript.
How would I implement this with javascript parameters? Thanks!
Just have your function accept an argument and use that in the alert (or wherever).
external.js
function myFunc(value) {
alert(value);
}
someview.cshtml
<script>
myFunc(#myValueFromModel);
</script>
One thing to keep in mind though, is that if myValueFromModel is a string then it is going to come through as myFunc(hello) so you need to wrap that in quotes so it becomes myFunc('hello') like this
myFunc('#(myValueFromModel)');
Note the extra () used with razor. This helps the engine distinguish where the break between the razor code is so nothing odd happens. It can be useful when there are nested ( or " around.
edit
If this is going to be done multiple times, then some changes may need to take place in the JavaScript end of things. Mainly that the shown example doesn't properly depict the scenario. It will need to be modified. You may want to use a simple structure like this.
jsFiddle Demo
external.js
var myFunc= new function(){
var func = this,
myFunc = function(){
alert(func.value);
};
myFunc.set = function(value){
func.value = value;
}
return myFunc;
};
someview.cshtml
<script>
myFunc.set('#(myValueFromModel)');
myFunc();//can be called repeatedly now
</script>
I often find that JavaScript in the browser is typically conceptually tied to a specific element. If that's the case for you, you may want to associate the value with that element in your Razor code, and then use JavaScript to extract that value and use it in some way.
For example:
<div class="my-class" data-func-arg="#myValueFromModel"></div>
Static JavaScript:
$(function() {
$('.my-class').click(function() {
var arg = $(this).data('func-arg');
myFunc(arg);
});
});
Do you want to execute your function immediately? Or want to call the funcion with the parameter?
You could add a wrapper function with no parameter and inside call your function with the global var as a parameter. And when you need to call myFunc() you call it trough myFuncWrapper();
function myFuncWrapper(){
myFunc(myValue);
}
function myFunc(myParam){
//function code here;
}

How to split jquery source code into multiple files when using the module pattern?

I've got some problems splitting up my jquery source code into more than one file. My real source code is a bit more complicated but the following simple example shows my problem very good. At first I would like to show you a working example with only one javascript file. Afterwards, I will describe what I tried in order to split the javascript into two files.
My html code looks like this ("./jquery" is a symbolic link to my local jquery download):
<html>
<head>
<script src="./jquery"></script>
<script src="./file1.js"></script>
</head>
<body>
<div id="content"></div>
</body>
</html>
The jquery source code in file1.js looks like this:
$(document).ready(function() {
var Test = (function() {
var content = $('#content');
var init = function() {
content.html('<p>test</p>');
};
return {
init: init
}
})();
Test.init();
});
After opening the page, "test" is displayed so that this example works as expected.
But now I want to put the whole Test part into another file file2.js. My html is basically the same but gets an additional line:
<script src="./file2.js"></script>
file1.js now contains only the call of the init function:
$(document).ready(function() {
Test.init();
});
and file2.js contains the definition of Test:
var Test = (function() {
var content = $('#content');
var init = function() {
content.html('<p>test</p>');
};
return {
init: init
}
})();
When I open the page, "test" is not displayed any more. In order to make sure that the init function is called at all, I added a console.log("test"); to the init function which is working fine. Therefore, I suppose that the function might be called before the DOM is ready, but actually I am pretty clueless. Maybe someone can give me a hint how to make that run.
Best regards and thanks in advance!
You can do several things according to your preferences...
1. Move your scripts to the end of the HTML file intead than in header...
<html>
<head>
</head>
<body>
<div id="content"></div>
</body>
<script src="./jquery"></script>
<script src="./file2.js"></script>
<script src="./file1.js"></script>
</html>
Think this problem secuencially... if you don't want to declare a var in each module referring an element in your DOM you need that the element exists first, then you can declare the "global" var to the module content. This way your original file2.js works.
Another way is to declare the content "global" to your module but init this in your init function...
var Test = (function() {
var content;
var init = function() {
content = $('#content');
content.html('<p>test</p>');
};
return {
init: init
}
})();
Now you can use the content variable in all of your module's functions.
Hope this helps, let me know.
file1 depends on file2. Make sure file1 comes ordinally after file2 in your html.
AngularJS offers dependency injection, modules, services, factories and all sorts of other goodness. Takes a bit to get used to, but well worth it IMO: much cleaner abstraction of javascript from DOM, data from presentation etc.
I appreciate your question is JQuery specific, but especially if you're starting a new site, I suggest you give Angular a try.
Modify your file2.js as follows:
var Test = {
content : $('#content'),
init : function() {
Test.content.html('<p>test</p>');
}
//, include other functions here
};
Modify your file1.js as follows:
$(document).ready(function(){
Test.init();
})
Now declare file2.js before your declare file1.js because file1.js is referencing a function from file2.js .

fake jquery document.ready calls on pages without jquery

I wanted to use some data in another JS script which had a JQuery call $(document).ready(function() inside the block, but i did not want to use JQuery nor init the script, because i just wnat to use some of the predefined variables.
So simply using the script from external source ( as it might get changed ) but skipping the ready() call and just do some data manipulation afterwards.
I got it working with
<script>
var fakeJquery = function(fn) {
this.ready = function () {
return true;
}
};
var $ = function(fn) { return new fakeJquery();}
</script>
But i wonder if there is a better, easier way to do that?

Jquery and javascript namepsace

In trying to namespace my js/jquery code, I have come up against the following problem.
Basically, I used to write all my JS code in each html/php file, and I want to abstract that away to a single js file with namespaces.
So, in my html file I have:
<script type="text/javascript">
$(document).ready(productActions.init());
</script>
And in my js file I have:
var productActions = {
init: function() {
alert('initialsed');
$('#field_id').change(function() {
alert('ok!');
});
}
The productActions init function is definitely running, because I get the first alert (initialised). However, it seems that none of the jquery binding functions do anything at all. Stepping through the init function shows that the above change function is being registered, but actually changing the value in the field does absolutely nothing.
Am I missing something obvious here?
$(document).ready(productActions.init());
This code calls init() immediately and passes its return value to ready(...). (just like any other function call)
Instead, you can write
$(document).ready(productActions.init);
To pass the function itself. Howeverm this will call it with the wrong this; if you need this, write
$(document).ready(function() { productActions.init() });

Revealing module pattern with jQuery not working

I've been playing around with the revealing module patter. I originally started using the Singleton pattern but from reading around the module pattern seems to be the better option.
So i've tested the following:
var test = (function() {
var init = function () {
alert('hello');
};
return {
init: init
};
})();
and this works fine when calling
<script>test.init();</script>
However, i want to use jQuery so i tried:
var test = (function($) {
var init = function () {
$("#samplediv").html('test');
alert('hello');
};
return {
init: init
};
})(jQuery);
but this doesn't work. It does work when using:
<script>$(function() { test.init(); });</script>
How can i get it to work without the added jQuery when calling it?
Usually, anything that touches the DOM needs to done in the $(document).ready(fn) callback. $(fn) is a shortcut for this.
So the reason it doesn't work is because you are searching for DOM elements that don't exist yet. So you have 2 choices. Either you must use the $() wrapper, or find another way to run your code only after the DOM elements exist. Another way to do this is to move your script tag the bottom of the body tag so that the DOM elements can exist when it's ran.
<body>
<!-- your page elements here -->
<script>test.init();</script>
</body>

Categories