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.
Related
I have a model as List<Collaborateur> coming from my Controller Action to my JSP. And I have the following function in JQuery/Javascript:
function showCollaborateurEmail(index) {
alert("${collaborateurs[1].email}"); // Works
alert("${collaborateurs[2].email}"); // Works
//....
alert("${collaborateurs[index].email}"); // Not working
}
So I want to show the email of my Collaborateur model dynamically by calling the function showCollaborateurEmail(index).
Thank you for your help!
The problem that you face is the exchange of data between code on server side and code on client side. There are multiple ways to achieve your goal. You can...
store the list-elements in a hidden form-field (escaping of delimiter might be a challenge) or
generate javascript - code on server side in JSP that creates a javascript array with the list-elements or
use ajax.
1 and 2 are rather ad hoc - solutions for a small problem. 3 is rather a bigger solution (depending on your technical environment) which additionally provides the possibility to avoid page reloads.
Two approaches to explore:
1) generate a javascript array with all the elements of the list, and access the at will on the client side (javascript)
2) access the "collaborateurs" via an ajax request
I would not recommend to mix server and client code like this through jsp.
Instead you should create a servlet and call what you need with ajax.
If you want to use a dirty/quick trick I think you can build back the array from the server side to the client side inside a <script> tag
For example:
<script>
var emailArray = [
<%for (int i = 0; i > collaborateurs.length; i++){ %>
"<%=collaborateurs[i].email %>"
<%
if(i < collaborateurs.length - 1){
%>, <%
}
}%>
];
</script>
(I wrote this by hand so I'm not sure the syntax is correct, but I hope you got the idea)
One more option you will have is converting your List object to json and then set it to a variable in jsp while compiling phase and the on client side parse json and make an object and iterate over it. This way you can access your list even after loading your page also.
You can use API like gson,Jackson etc.
Example :-
List<Object> list1=new ArrayList<>();
// Add values to list here
JSONArray obj=new JSONArray(list1);
In JSP :
var collaborateursList=${collaborateurs};
function showCollaborateurEmail(index) {
alert(collaborateursList[1].email);
alert(collaborateursList[2].email);
alert("collaborateursList[index].email);
}
Please verify the index is passing correctly to the function and try using this
alert("${collaborateurs[" + index + "].email}");
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';
}
I have a custom template tag that returns suppose name of a student and roll number if passed as an argument id of the student.
#register.inclusion_tag('snippet/student_name.html')
def st_name_tag(profile, disp_roll=True, disp_name=True):
#some calculations
return {'full_name':student.name,
'roll':student.roll_number,
}
The template(included) consists of some Html file which is written in a single line(to avoid unterminated string literal error from js).
I simply want to call the st_name_tag from inside the JS function.
My JS looks like:
{% load profile_tag %}
<script type = "text/javascript">
eventclick : function(st){
var div = ('<div></div>');
var st_id = st.id;
if (st.status == 'pass'){
div.append('<p>Student Name:{% st_name_tag '+st_id+' %}</p>');
}
}
So far I tried the above method along with removing the + and '' signs from st_id varaible. That hasnt helped me at all. Help Please!
You are trying to render a template based on the interaction by user. The first happens on the server (server-side as it is often referred to), and the latter happens on the user's browser.
The order that these happen is first to render the template on server, send and present in browser, then user interacts with js. Because of this fact, as I mentioned in the comment, it is not possible to affect the template rendered within javascript.
I would recommend you to use ajax in order to accomplish this. Whenever an iteraction occurs, you asynchronously make a request to the server to present you with new data.
I have a website entirely coded with PHP. Let's consider the simple example in which a PHP script generates HTML/CSS code from data about a list of fruits, fetched in a MySQL data base :
<?php
function displayFruit( $fruit ) {
echo('<div>');
echo('<span class="fruit-color">Coulor : '.$fruit['color'].'</span>');
...
// display some other parameters of the fruit, with maybe some complex styles, etc.
...
echo('</div>');
}
// Get infos from dabase
$fruits = getAllFruitsFromDatabase();
// Display infos
foreach( $fruits as $fruit ) {
displayFruit( $fruit );
}
?>
Now, I want to add interactivity and to allow the user to filter according to some fruits characteristics (color, etc.) so that only the corresponding fruits are displayed.
So I add some controls and link them to Javascript AJAX queries. These queries will return the same type of data (even though not the same format) as what getAllFruitsFromDatabase() returned, and I want this data to be displayed the same way displayFruit() displayed it.
However, the issue here is that the display/styling process will now have to occur on the client side and not anymore on the server side.
Is there a technical way to factorize the PHP code (the one of displayFruit() ) and the Javascript code that will have to be used, so that there only exists one place where the HTML display code is written (and not once in PHP, and once again in JS) ?
What you describe is a very common problem for interactive web apps and there is not really one 'correct' solution for it. : )
It really depends on what you want in terms of PHP-vs-JS balance, but here are two possible solutions that avoid duplication of the HTML rendering code:
1. Always create the mark-up in Javascript, even on the initial page load
This works well if you want the bulk of your code to be on the client-side. In this solution, you wouldn't have a displayFruit($fruit) function in PHP, but you'd have the equivalent in your Javascript. You would render your page 'frame' on page load, and then do your AJAX call on the document-ready event.
2. Always create the mark-up in PHP, even on the filter-AJAX calls
This is perhaps a more balanced approach and has the advantage that you could design your code such that it's possible to run even if the client has Javascript disabled.
One way to do this is to pass an optional parameter to your page rendering function (in the PHP code) that will make it render only the data part, e.g. something like this:
function renderPage($filters = false, $dataOnly = false)
{
if (!$dataOnly) {
// Output the top of the page
}
// Output the data
// If $filters is not false, the getFruitsFromDatabase call will return filtered data
$fruits = getFruitsFromDatabase($filters);
foreach( $fruits as $fruit ) {
displayFruit($fruit);
}
if (!$dataOnly) {
// Output the bottom of the page
}
}
When your PHP script is called via AJAX, you pass your filters to the first argument of the renderPage function and true to the second argument.
Note on front-end frameworks:
exussum and Michael Chaney have mentioned Javascript frameworks. While I haven't used any of those myself, they look fantastic!
So If you're ready to invest time in re-writing your display logic in JS, then go for it. However, if you don't have much time and would like to re-use as much of your original PHP code as possible, then I would go for solution number 2. The decision probably also depends on the size and design of your existing code.
EDIT: had forgotten to add a $filters argument to the example function
EDIT 2: comment on front-end frameworks mentioned by others in comments
As the title says, I am trying to dynamically load content in a view using ajax requests. I know this can be done if you are using html elements e.g. ("#div_place").html(<p>...). The problem lies when I would like to load some php/blade objects into a div for instance. Is this possible or is there a different way to go about achieving the result I want.
This is pretty straightforward. Assuming you're using jQuery...
create a route that will respond to the AJAX call and return an HTML fragment
Route::get('test', function(){
// this returns the contents of the rendered template to the client as a string
return View::make("mytemplate")
->with("value", "something")
->render();
});
in your javascript:
$.get(
"test",
function (data) {
$("#my-content-div").html(data);
}
);
After some digging some more I found this which helped me to solve the problem.
You have to use the .load("url/page/...") via jquery and then set a route in the routes.php file that displays a view with the data that needs to be loaded e.g.
Route::get('/url/page/...', function(){
return View::make('test');
});
This returns the necessary php code which needed to be loaded dynamically, cheers.