Server Side Templating Engine to render javascript - javascript

My use case is that I have portions of js code that I would like to render based on some user permissions, with server side language being PHP.
Now I am currently using the Fat Free Framework, but even in other frameworks like laravel, I have only been able to find templates that template HTML.
I know I can use the templating engines that exist to just have conditional template logic to print a <script> tag given certain user permissions, but I haven't found any examples of how to do the following in a .js file
Example:
PHP:
$visible = ['section1' => true, 'section2' => true];
//this just renders the js file
render('test.js', ['visible' => $visible]);
test.js:
`//this is some kind of templating format`
{{if(isset($visible['section1']))}}
var a = 'Gin';
function sectionA(){
//do something
}
{{endif}}
{{if(isset($visible['section3']))}}
var b = 'Rum';
function sectionB(){
//do something
}
{{endif}}
The result would be that test.js would be rendered as
var a = 'Gin';
function sectionA(){
//do something
}

Alright, so after some playing around, I've figured out a solution. when building the html, you can echo a script that has a src of whatever resource you want. on the server side, respond to the javascript src route by rendering a js file (using the templating engine) that has the templating markups in it, and things will work as I wanted them to :)

Related

EJS not rendering ejs code on client side

I tried rendering EJS code that I sent from home.js to home.ejs as a string but it didn't work. I'm trying this now but nothing seems to work. My code is:
home.js
var templates = {};
templates.onScroll = fs.readFileSync('views/partials/onScrollAppendPost.ejs', 'utf-8');
res.render("users/home", {templates: templates});`
home.ejs
var template = new EJS({text: <%- JSON.stringify(templates.onScroll) %>}).update('#mainContainer', {postArr: postArr});
Edit:
What im trying to do is I want to detect when a user gets to the bottom of the page. With this code:
$(window).scroll(function () {
if ($(window).scrollTop() == $(document).height() - $(window).height()) {
//get post data and append post
}});
When the user gets to the bottom of the page i want to append ejs code to the page. Basically i want to communicate between ejs and js.
Based on the example at http://ejs.co, you first need to include a browser build of ejs in your page:
<script src="ejs.js"></script>
Then you can use EJS client-side like this:
var html = ejs.render('your template here', {postArr: postArr});
$('#mainContainer').html(html);
Here's I'm assuming postArr is set appropriately, possibly as the result of an AJAX request.
As you're using EJS to generate EJS it all gets a lot more difficult to understand but the relevant code in your home.ejs would be something like this:
var html = ejs.render(<%- JSON.stringify(templates.onScroll) %>, {postArr: postArr});
$('#mainContainer').html(html);
This assumes that the onScroll template doesn't include any other templates. You haven't specified whether home.ejs is being used to generate HTML or JavaScript so it's unclear precisely what other escaping considerations might apply but it's possible that won't be a problem.

Accessing a local js variable in razor block

I am trying to write razor code inside javascript where I am trying to use a local variable inside the razor code. Here is the sample code:
<script type="text/javascript">
for (i = 0; i < data.result.length; i++) {
$("#member-table tbody").append("<tr>");
var id = data.result[i].MemberId;
var actions = $("<td>" + #Html.ActionLink("Detay", "Edit", new { id }) + "</td>)");
}
</script>
the problem is that id is not recognized by the razor code (i.e. it does not exist in the current context). How can I achieve that ? Is there any way ?
It's not possible to access a javascript variable in a razor block.
That's because razor is executed in the server, and javascript is executed in the browser.
However, by looking at your code it seems like you are using javascript to populate a table and that's bad, there are two patterns for solving this problem, one that solves everything in the server, and another one that solves everything in the browser.
Solving everything in the server:
If you decide that you want to solve everything in the server, your javascript should request the contents from the server and load them into a placeholder without changing them, something like:
$("#myButton").click(function(){
$("#myDinamicDiv").load("/Path/ToView");
});
and then you use razor's foreach loop to generate the table's html:
#foreach (var x in ViewBag.MyData)
{
<tr>
<td>Generate contents here, including links </td>
</tr>
}
Solving everything in the client:
As pointed out in another answer, if you are using the default routing, you can just create direct strings in the javascript code and add them to your page, keep in mind however, that when using this solution, as your page gets complex, your javascript will became less and less maintainable, having a for loop that iterates over data is a sign that maybe you can benefit from javascript UI frameworks like Angular.js and Knockout.js, in fact, what you are doing is the core of Knockout.js's third lesson in its tutorial (Single page applications)
If you're just using default routing, then simply just don't bother with the Razor #Html.ActionLink. Stick with an explicit tag:
var actions = $('<td>Detay</td>');
...obviously with whatever your current controller name is substituted for [your-controller-here].
(And I'm assuming your 'id' isn't necessarily URL-encoded, hence the 'escape'.)
You are mixing server side and client side here. You cannot create #Html.ActioLink using client side variables. Html.ActionLink is rendered on the server, it does not have any clue at all about your client side variables.
If you want to use a client side variable, like "id", render a plain html link (a) tag.
This worked for me once
if ('#ViewBag.DownloadLink' != '') {
window.location.href = '#ViewBag.DownloadLink';
}

how to load a file and parse tags using jquery

I have a file which contains lots of tags like follows
<script type="text/template" id="template-1">
</script>
<script type="text/template" id="template-2">
</script>
I want to load the file and than load all the content inside the script tags in memory.
I am trying the below code but its not working.
tpl = {
// Hash of preloaded templates for the app
templates : {},
loadTemplates : function(name) {
var that = this;
$.get(name, function(data) {
$(data).find('script').each(function (_, entry) {
that.templates[$(this).attr('id')] = $(this).text();
});
});
},
// Get template by name from hash of preloaded templates
get : function(name) {
return this.templates[name];
}
};
any help?
call is made like this
tpl.loadTemplates('/templates/templates-home.html');
In general you seem like you're on the right track. The browser will load (but ignore) script tags marked with type=text/template and you can later select the contents of those tags and process them with javascript.
I think your problem is likely with the order of your procedure.
You haven't posted the javascript that uses your templates so I can only assume. I suspect your trying to load the templates before the document is ready, thus, the script tags aren't actually on the page when you load them. To fix, your can move your javascripts below the templates in the document OR execute your code in a window.onLoad handler.
EDIT
Okay, now I have a better idea of what you're trying to do. You still haven't told me what part of this is broken, but my gut tells me that this bit is the problem: $(data).find('script'). jQuery expects to be traversing the DOM. At this point in time, data is just a string returned from the server, it's not actually loaded in the DOM. So jQuery won't actually find ANY script tags. Try appending your result to the body before querying the DOM for script elements. Maybe something like this:
$('body').append(data);
$('script[type="text/template"]').each ...
I'm not really thrilled about that though. Can you just inject them into the page on the server side? Why do you need to delay the loading?
EDIT 2
If you don't want your script tags to be visible in the html document, then I suggest you don't use them. Instead you can have your template endpoint just return a bundle of javascript and evaluate it directly. Something like:
$.get(name, function(data) {
// data is a string that sets up your window.template variable
eval(data);
});

Transferring javascript from a view to a seperate JS file

I am working on a legacy application and I want to move some JS code onto a separate JS file.
I will have to refractor some of the code to do this. I can put #Url.Content statements into data attributes in the HTML.
But how would I replace this line of code?
var array = #Html.Raw(Json.Encode(ViewBag.JobList));
A separate JS file will not know what #Html.Raw means.
Server side code like that cannot run in a seperate javascript file. My solution for such problems is having a short javascript part in the head that runs on the onload event. There you can set variables that you can use in a seperate javascript file:
in the head:
array = #Html.Raw(Json.Encode(ViewBag.JobList));
in the seperate javascript file:
var array;
Then, in the seperate javascript file you can do with your array whatever is necessary.
The ViewBag.JobList data is only known at HTML page generation time. To include it in an external JavaScript file, you have to have another ASP.NET resource that recalculated ViewBag.JobList and then served as part of a dynamic JavaScript file. This is pretty inefficient.
Instead, do what you're doing with the URLs: pass the data through the DOM. If you're writing into normal DOM instead of a script block, you don't need the raw-output any more (*), normal HTML escaping is fine:
<script
id="do_stuff_script" src="do_stuff.js"
data-array="#Json.Encode(ViewBag.JobList)"
></script>
...
var array = $('#do_stuff_script').data('array');
// jQuery hack - equivalent to JSON.parse($('#do_stuff_script').attr('data-array'));
(Actually, the raw-output might have been a security bug, depending on what JSON encoder you're using and whether it chooses to escape </script to \u003C/script. Writing to HTML, with well-understood HTML-encoding requirements, is a good idea as it avoids problems like this too.)
I think you need to create action with JavaScriptResult
public ActionResult Test()
{
string script = "var textboxvalue=$('#name').val();";
return JavaScript(script);
}
But, before proceeding please go through following links
Beware of ASP.NET MVC JavaScriptResult
Working example for JavaScriptResult in asp.net mvc
I would also follow MelanciaUK's suggestion :
In your javascript file, put your code inside a function :
function MyViewRefactored( array ){
... your code ...
}
In your view, leave a minimal javascript bloc :
<script>
var array = #Html.Raw(Json.Encode(ViewBag.JobList));
MyViewRefactored( array );
</script>

ASP.NET MVC templates for both client and server

Is this possible? For an example of what I want to achieve, take the Facebook commenting system. Existing comments are rendered on the server, but if I leave a new comment, it is created using AJAX on the client. Ideally, I'd like to store the template for the comment in only one place, and have access to it on both the server (rendered by Razor) and on the client (rendered in Javascript using JSON returned by the server).
Any ideas?
EDIT: I guess another option is to stick with purely server side rendering, and when the user posts a new comment, return the rendered HTML to the browser to be stuffed into the DOM. This isn't quite as nice, but I'd be interested to know if this is possible too.
I would oppose rendering server-side and then sending it back to your JS-script for bandwith and performance. Rather you should use a templating engine that works on both the server and the client. When the client wants to refresh the comments, it requests only the data for the comments and then replaces the old comments html with the new html rendered from the data using the same template that is being used on the server.
I've been using Mustache templating engine to achieve this using PHP and JS. There is a .NET version which I guess works for ASP.NET, and I'm guessing you're using ASP.NET.
So what I do is I make sure I have data formatted in the same way in PHP and JS and then render using a Mustache template.
http://mustache.github.com/
Mustache is simple to use. You take one object and one template and you get the HTML back.
Example object:
object->user = "Kilroy"
object->comment = "was here"
object->edited = true
Example template:
{{user}} {{comment}} {{#edited}}(This comment has been edited){{//edited}}
Result:
Kilroy was here (This commment has been edited)
The approach I've used is having a hidden HTML template with wildcards and/or class names, then on document ready loaded the contents via AJAX/JSON call and finally refreshed or added new items using the same template in javascript.
<ul id="template">
<li>
<span class="message"></span>
<span class="date"></span>
</li>
</ul>
<ul id="comments"></ul>
<script type="text/javascript">
$().ready(function() {
loadComments();
});
function loadComments() {
$.post('#Url.Action("GetComments", "Forum")', {}, function(comments) {
for (i = 0; i < comments.length; i++){
loadComment(comments[i]);
}
}, 'json');
}
function loadComment(comment) {
var template = $('#template li').clone();
template.find('.message').text(comment.message);
template.find('.date').text(comment.date);
$('#comments').append(template);
}
</script>
For new messages, you can post the message to the server and then add it to the list using the loadComment function, or refresh the whole comments list. It's not a complete sample, but hope you get the idea.
I haven't worked with razor or ASP.NET MVC much, but the way I usually approach it using Monorail and NVelocity is this:
Have a template for the page.
For the comments, have a partial template that you include in your main template.
For the AJAX request, use that partial template to render the markup for the comments part. Replace it client side with your preferred method.
This way, will let you have the markup on one place, regardless on if it's a regular request or an ajax request.

Categories

Resources