I've been using JSON to handle AJAX functionality in my rails applications ever since I heard about it, because using RJS/rendering HTML "felt" wrong because it violated MVC. The first AJAX-heavy project I worked on ended up with 20-30 controller actions tied directly to specific UI-behaviors and my view code spread over controller actions, partials and rjs files. Using JSON allows you to keep view specific code in the view, and only talk to view agnostic/RESTful controller actions via AJAX to get needed data.
The one headache I've found from using pure JSON is that you have to 'render' HTML via JS, which in the case of AJAX that has to update DOM-heavy elements, can be a real pain. I end up with long string building code like
// ...ajax
success: function(records){
$(records).each(function(record){
var html = ('<div id="blah">' + record.attr +
etc +
')
})
}
where etc is 10-15 lines of dynamically constructing HTML based on record data. Besides of the annoyance, a more serious draw back to this approach is the duplication of the HTML structure (in the template and in the JS).* Is there a better practice for this approach?
(My motivation for finally reaching out is I am now tasked with updating HTML so complex it required two nested loops of Ruby code to render in the first place. Duplicating that in Javascript seems insane.)
One thing I've considered is loading static partial files directly from the file system, but this seems a bit much.
I like the idea of templating. In my experience it can really clean up that messy string manipulation!
There are many solutions, for example, check out John Resig's (creator of jQuery):
http://ejohn.org/blog/javascript-micro-templating/
I would go with creating an HTML structure that contains placeholders for the elements you'll need to update via AJAX. How much structure it applies will depend on what you're updating; if you know the number of elements you'll have ahead of time, it would be something to the effect of
<div id="article1">
<div id="article1Headline"></div>
<div id="article1Copy"></div>
<div id="article1AuthorInfo"></div>
</div>
<div id="article2">
<div id="article2Headline"></div>
<div id="article2Copy"></div>
<div id="article2AuthorInfo"></div>
</div>
You then write code that references the id of each element directly, and inserts into the .innerHTML property (or whatever syntactically more sugary way jquery has of doing same thing). IMHO, it's not really so terrible to have to assign the contents of each element, the part that you don't want to have to sprinkle through your AJAX functions is the HTML structure itself; in your app the content is volatile anyway.
However, it looks like you might have a list of an unknown number of elements, in that case it may be that you'd need to just put in a placeholder:
<div id="articleList"></div>
In that case I don't really see a way to avoid building the HTML structure in the javascript calls, but a reasonable amount of decomposition of your javascript should help that:
function addArticle( headline, copy, authorInfo, i ){
createDiv( "article" + i + "Headline", headline );
createDiv( "article" + i + "Copy", copy);
createDiv( "article" + i + "AuthorInfo", authorInfo );
}
(not working code of course, but you get the idea,)
You could use the load function in jQuery;
This loads the content of a page into a div like this:
$('#content').load("content/" + this.href.split('#')[1] + ".html", '', checkResponse);
Just make a dynamic view and you are good to go...
Just happened to find exactly what I was looking for: Jaml
Related
I'm working on a little home brew project and I've found I've been spoiled by working with RoR and more specifically RoR partials.
My project is entirely client side. So I'm using javascript and HTML5. Now what I would like to do is this: have a home screen template with a container in which I could do something like the
classic <%= yeild %> tag does in RoR.
I have thought about using iframes but that seems messy. I have also thought about using xmlHTTP requests to get the file in my javascript and then update the innerHTML of my content div with the file stream. I have done this before for personal projects, but it's hacky and I have to flag browsers like Chrome with a --allow-file-access-from-files tag. Which obviously I can't advise end users to do.
My other thought was to write the html as a javascript string, and then just put different strings as the value of content.innerHTML but this sounds stupid hard to maintain and just not clean at all.
Ultimately I am even up for writing my own solution (which I would then post here as the answer for anyone else looking) but I wanted to know if there was already a solution out there.
The ultimate end goal behavior would follow this flow:
Main_page:
<div id="main_content">
<!-- this is the yield area -->
</div>
App starts and the file menu.html is loaded into the yield area:
<div id="main_content">
<!-- this is the content of menu.html, notice it's like a partial,
there is no body or head or doc type tags, just a list -->
<ul>
<li>Menu item</li>
</ul>
<!-- this is the end of the menu.html content -->
</div>
And finally when they click on Menu item it loads the contents of menu_item.html into the content div replacing the current content (menu.html).
Some research I already did:
Div like an iframe?
Div src attribute plugin (looks interesting but runs on webserver)
Stack question about updating div with javascript
Found a link somewhere that led to Pure This looks like it could do the trick, but seems like it would be difficult to implement for a lot of content (since I don't have anything generating the json, I would have to do it all by hand.)
This talks about the seamless attribute of iframes, looks promising but only chrome has implemented and even then its bugs have been abandoned.See here for status of webkit development of the seamless attribute.
If you're using jQuery, you could use jQuery.load(): http://api.jquery.com/load/
Example:
$("#main_content").load("menu_item.html");
If you're worried about the safety of the <div> (which is a good thing to be worried about), then what you'd be better off doing is using an intermediary <div> which is not attached to the DOM.
function parse_html (html) {
var div = document.createElement("div");
div.innerHTML = html;
var scripts = div.getElementsByTagName("script"),
i = scripts.length;
while (i > 0) {
div.removeElement(scripts[i]);
i -= 1;
}
return div.firstChild;
}
Then if you grabbed an HTML file via XHR:
var xhr = new XMLHttpRequest();
xhr.open("GET", "/templates/menu.html", true);
You could put your var child_el = parse_html(xhr.responseText); in your XHR callback.
Then just append that element to your div.
That function is assuming that your partial has one root element (assuming that you could have multiple back-to-back partials, but each template would start from a single element.
If that's not the case, then you'd create a document fragment in the function (document.createDocumentFragment();) and append each of the elements in order, to the fragment, returning the fragment at the end of the function, which you would then append to the div (of course, this would make managing the nodes inside the div that much harder, after the fact).
I'm doing something similar to this for a few pet projects, using a simple template system built on {% %} for injecting data-properties ({% item.title %}) or arbitrary JS/return values from functions, within the html.
This is only a basic implementation, and there are lots of things you could do with this...
Supporting arbitrary nesting levels, for templates inside of templates is going to be more work.
Example code
var jqxhr=$.getJSON("http://search.twitter.com/search.json?callback=?",{q:query},
function(data) {
... question.
});
Question
Now i need to create for each tweet result something like this (for example...)
<article class="tweet">
<header>
<img class ="tweet_img"src="data.profile_image_url"/>
</header>
<p class="tweet-text">data.text</p>
</article>
Well, i know several ways to append each result to the document:
Creating a big HTML string and add the data from JSONP and append this to some container.
Create a p element, a header element... work with them and after that append a final Element to some container.
Now the question is: with your experience what is the correct way to do this?
I mean the correct way using good principles.
Please dont ask about the html, it's dumb example.
Thanks.
Well, best practices will tell you not to use the innerHTML property of a DOM element, which is what you'd be doing with option 1. But unless you are concerned about immediately operating on the code with Javascript, attaching events, or security concerns around tag injection (I don't know how much this is an issue anymore) then creating an HTML string and inserting it using innerHTML is going to be a lot quicker and easier to update.
There are several valid approaches that each have their own advantages...
The technique of just generating the HTML as a string in your java code and adding it with .innerHTML is known to be one of the fastest performing approaches...but it provides very little validation of your HTML.
Alternatively, you can build the HTML using DOM methods directly, creating tags and appending them to build the structure. This is generally safer in that you have more validation going on, but the DOM methods are extremely wordy, which makes them a bit painful to type...and the code is even more verbose as you have to add attributes one at a time as well.
My personal preference, especially since you're already using JQuery, would be to build the tags and text nodes using JQuery, and put them together using JQuery, which allows you to do so in bite-sized, more human-verifiable units, without becoming overly verbose.
This also has the advantage that JQuery's methods of producing new tags give you additional support for older browsers that did not adhere to DOM standards. Hopefully you don't actually have to care whether your page works for those older browsers, but more compatibility never hurts either.
In that approach, you'd write something like the following:
var article = $('<article class="tweet"></article>');
var header = $('<header></header>');
var image = $('<img class="tweet_img" src="' + data.profile_image_url + '"></img>');
var tweet = $('<p class="tweet-text">' + data.text + '</p>');
header.append(image);
article.append(header, tweet);
$("#id_of_content_area_to_add_the_tweet_to").append(article);
The cleanest way I know how is to use a template system like Mustache, instead of "HTML in JS"
var template = html_string, //HTML from a string or from AJAX
data = {...data...}, //structured data
html = $(Mustache.render(template,data)); //wrap in jQuery to operate
html.appendTo(somewhere_in_DOM);
If you want to attach some event handlers to the elements then you should generate them separately.
But if you don't want to attach any event handler then i will recommend first method
$("body").append('<article class="tweet"><header><img class ="tweet_img" src="'+data.profile_image_url+'"/></header><p class="tweet-text">'+data.text+'</p></article>')
I will recommend you to use some Template engine like Handlebars.js Which is the right solution for your problem.
Which is having many more options which has many more conditional options which can be useful in feature. Just visit the above link you will have some idea.
In JSF, what would be the "right" and "clean" way to integrate JavaScript i.e. into a composite-compenent? I am a fan of Unobtrusive JavaScript, and separating HTML from JS from CSS. What would be a good way to have as little quirks as possible? This is what I like the best so far:
<composite:interface>
// ...
</composite:interface>
<composite:implementation>
// ...
<script> initSomething('#{cc.clientId}'); </script>
</composite:implementation>
What I don't like is, to use language A to generate language B. Basically the same goes for event handlers and stuff. My favorite would be to attach those handlers via <insert favorite DOM JavaScript library here>. Is this possible? How do you do this kind of integration?
I'd say that what you've there is the best you can get, provided that you're absolutely positive that the HTML element's nature is so unique that you absolutely need to select it by an ID, every time again.
If the HTML representation is not that unique (I can imagine that a Facelets template or include file might contain application-wide unique HTML elements like header, menu, footer, etc, but a composite component which can be reused multiple times in a single view? I can't for life imagine that), then you could also consider using an unique classname and hook initialization on it.
E.g. /resources/composites/yourComposite.xhtml
<cc:implementation>
<h:outputScript library="composites" name="yourComposite.js" target="head" />
<div id="#{cc.clientId}" class="your-composite">
...
</div>
</cc:implementation>
with in the /resources/composites/yourComposite.js (assuming that you're using jQuery)
var $yourComposites = $(".your-composite");
// ...
You can if necessary extract the autogenerated HTML element ID for each of them in a jQuery.each():
$yourComposites.each(function(index, yourComposite) {
var id = yourComposite.id;
// ...
});
I'm writing an application with some client-side JS that I use to update the DOM reasonably often.
The thing is that doing something like:
$('#id').html('<div class="' + class + '">' + content + '</div>');
multiple times and leaving HTML lying randomly round your JavaScript isn't very pretty and difficult to maintain.
Is there a JavaScript equivalent (or alternate solution) to the way Lithium handles this in it's view helpers.
See:
http://li3.me/docs/lithium/template/Helper::_render()
http://li3.me/docs/lithium/util/String::insert()
For examples.
Basically in the PHP version you would make an associate array of common strings and a simple method to replace to replace certain marked parts of those strings with variables of the same name.
Another silly example (psuedo-code:)
var strings = {
'div': '<div {:attr}>{:content}</div>'
};
console.log(render('div', {id: 'newElem'}, 'Hello, World!'));
// Output: <div id="newElem">Hello, World!</div>
If you have any better suggestions on how you handle storing HTML in your JavaScript and keep it from going all over the place then I'd very much like to hear it.
Yes, use jQuery templates or Underscore templates (my favorite) or any other JS templating engine out there.
Also, check this question for a discussion on performance of templating engines: Improve jQuery template performance
If you don't want to use a templating system, and have many html snippets that must be created many times, then you can use another technique :
Create a div in your main html file, give it a specific css class or id
Using css, make this div invisible
Inside is div, create the "template" divs, each one will contain a "snippet", each one with proper class or id
Using js (jquery, whatever) when you need it, clone the "template" div, append it where you need it, and then customize it.
Using jquery this is very easy, and your template "divs" are asy accessible to any html designer.
I'm on a mobile device now, and posting code snippets is a bit difficult, but let me know if want some examples.
jQuery encourages you to dynamically construct your DOM nodes instead of doing string concatenation:
$("#id").html(
$("<div />").addClass(class).text(content))
In general, you can use the jQuery attribute methods to construct such nodes, and many of these methods take mappings as you say you like. For example:
$("#id").append(
$("<div />")
.attr({id: "newElem"})
.css({width: "100%", color: "red"}))
I am building app with php and jquery and it has many ajax loads and functionalities. What i would like to know is what is the most widely used and acceptable way of accessing dom elements with jquery and adding events to it using jquery and javascript and if there is any rule of thumb to follow for the following instances.(and how i do them now)
A query generates a list of records and each record has to be edited and deleted.
The generated records look like this
Record1
Record1
Record2
Record2
Record3
Record3
and my jquery code handle them would be
$(".edit").click(function() {
var currentElementId = $(this).attr("id").replace("edit-id-","");
$("#ajaxdiv").load("ajaxpage.php","editid="+currentElementId);
});
is this type of stuff ok? i mean is there any other way to do stuff like this especially when this gets more complicated like have to add 3 more identifiers to id and then exploding them and finding out each of the identifiers separately. Any guidelines here to follow.?
If you simply need to have something that can be cleaner, maybe you can consider bind the record into one
meaningful div, and use .live() to bind the event handlers
i.e.,
<div data-rec-id = '300'>
...
<span class='link del' data-act='del'>Delete</span>
<span class='link edit' data-act='edit'>Edit</span>
</div>
<div data-rec-id = '301'>
...
<span class='link del' data-act='del'>Delete</span>
<span class='link edit' data-act='edit'>Edit</span>
</div>
$('.edit').live('click', function(){
var id = $(this).closest('[data-rec-id]').attr('data-rec-id');
$("#ajaxdiv").load("ajaxpage.php","editid="+id);
});
or even generic:
$('.link').live('click', function(){
var id = $(this).closest('[data-rec-id]').attr('data-rec-id');
var action = $(this).attr('data-act');
$("#ajaxdiv").load("ajaxpage.php","id="+id+"&act="+action);
});
Please don't use something like:
<a href="javascript:;" ...
or I would suggest you use some robust framework. e.g., http://documentcloud.github.com/backbone/
I think the way you've done it is acceptable. You're mostly concerned about manually doing string processing to fetch the element ID? That is a little messy but not too bad.
If you don't want to do it that way, you might consider attaching a separate event handler to each a element when you create them, and have that event handler know the ID as a closure variable. But that will consume more memory, so it will depend on how many of those links you have.
I've found using using the web server to server up static web pages with the only dynamic content being json passed back and forth, is the best overall approach. We post JSON, and get JSON back from the server. This is used to design the page. This makes the web server become a data communication rather than heavy lifting the page constructing. This also add the ability for an API structure you can give out to ppl and they can write apps for it. For example, the web site and the mobile application use the same API interface.