Pergunta

I consistently come across this code smell where I am duplicating markup, and I'm not really sure how to fix it. Here's a typical use case scenario:

Let's say we'd like to post comments to some kind of article. Underneath the article, we see a bunch of comments. These are added with the original page request and are generated by the templating engine (Freemarker in my case, but it can be PHP or whatever).

Now, whenever a user adds a comment, we want to create a new li element and inject it in the current page's list of comments. Let's say this li contains a bunch of stuff like:

  1. The user's avatar
  2. Their name
  3. A link to click to their profile or send them a private message
  4. The text they wrote
  5. The date they wrote the comment
  6. Some "edit" and "delete" links/buttons if the currently logged in user has permission to do these actions.

Now, all of these things were already written in our template that originally generated the page... so now we have to duplicate it inside of Javascript!

Sure, we can use another templating language - like Jquery's Template plugin - to ease the pain generating and appending this new li block... but we still end up with duplicate html markup that is slightly different because we can't use macros or other conveniences provided to us by the templating language.

So how do we refactor out the duplication? Is it even possible, or do we just put up with it? What are the best practices being used to solve this problem?

Foi útil?

Solução

This is a common problem and becomes more obvious as the UI complexity increases, and changes have to be done on both the server and client templates. This problem is fixable by using a the same template markup on both the client and server sides. The template processors must be written in both JavaScript and the server side language.

Two other solutions that are cleaner than the above approach, but both have their own problems:

  • Do everything client side
  • Do everything server side

If all markup generation is done on the client side, then the server acts more or less like a web service which only sends back data in whatever formats suits the application. JSON, and XML are really popular formats for most web services nowadays. The client always generates the necessary HTML and JS. If going with this approach, the boundary between the client and server must be well defined. Since the client has limited knowledge of what happens on the server, this means that proper error codes must be defined. State management will become harder since most/all server interaction will be happening asynchronously. An example of adding a comment with this approach may look like:

$('#add-comment').click(function() {
    var comment = $('#comment-box').text();
    $.ajax('http://example.com/add', {
        success: function() {
            addCommentRow(comment);
        },
        ...
    });
});

function addCommentRow(comment) {
    var user = currentUser().name;

    var html = "<li><b>{user}</b> says {comment}</li>";
    html = html.replace("{user}", user).replace("{comment}", comment);

    var item = $('<li>').html(html);
    $('#comments').append(item);
}

The other approach is to do everything server side. Whenever a change happens, shoot a request to the server, and ask it for the updated view. With a fast backend, response times under a second, and proper indicators of network activity, the application should seem very responsive despite everything happening on the server. The above example would be simplified to:

$('#add-comment').click(function() {
    $.ajax('http://example.com/add', {
        success: function(response) {
            $('#comments').html(response);
        },
        ...
    });
});

Although this seems a lot more cleaner on the client side than the previous approach, we have just moved the markup generation up to the server. However, if the application is not very AJAXy like Google Maps, then this approach may be easier to work with. Again, it's a matter of how complicated the application is, and perhaps maintaining state client side is a necessity for you, in which case you may want to go with the previous approach.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top