1

I have use-case as below where I am populating the innerHTML as below. Now the trouble is I want to resolve the template literal inside the context of forloop. Any clues ?

var blog_entries_dom = 'blog_entries';
var blog_entries = [
    {
        "title": "Hello World",
        "id": 2,
        "body": "<p>Hello World</p>"
    },
    {
        "title": "World Hello",
        "id": 3,
        "body": "<p>World Hello</p>"
    }
];

var blog_entry_template = `<div class="post-preview">
                        <a href="post.html">
                            <h2 class="post-title">
                                ${item.title}
                            </h2>
                            <h3 class="post-subtitle">
                                ${item.body}
                            </h3>
                        </a>
                        <p class="post-meta">Posted by <a href="#">Start Bootstrap</a> #Created At </p>
                    </div>
                    <hr>`;


var populate_children = function (element_id, objects_list, template) {

    var element = document.getElementById(element_id);

    var html = '';

    for (var i = 0; i < list.length; i++) {
        var item = list[i]
        html += template;
    }

    element.innerHTML = html;
};

populate_children(blog_entries_dom, blog_entries, blog_entry_template);

enter image description here

nehem
  • 12,775
  • 6
  • 58
  • 84
  • What would be the recommended way to achieve this ? I have a template & list of objects at hand and I need to stitch them. – nehem Jan 07 '17 at 12:27

2 Answers2

2

Template literals are exactly that: Literals. They're evaluated as of where they appear, like all literals. They aren't objects, you can't pass them around.

What you can do instead is put them inside a function and pass the function around. Have the function accept the things the template needs, and return the result of evaluating it.

In your case, you just change blog_entry_template to an arrow function, and call it:

var blog_entries_dom = 'blog_entries';
var blog_entries = [
    {
        "title": "Hello World",
        "id": 2,
        "body": "<p>Hello World</p>"
    },
    {
        "title": "World Hello",
        "id": 3,
        "body": "<p>World Hello</p>"
    }
];

var blog_entry_template = item => `<div class="post-preview">
    <a href="post.html">
        <h2 class="post-title">
            ${item.title}
        </h2>
        <h3 class="post-subtitle">
            ${item.body}
        </h3>
    </a>
    <p class="post-meta">Posted by <a href="#">Start Bootstrap</a> #Created At </p>
</div>
<hr>`;


var populate_children = function (element_id, objects_list, template) {

    var element = document.getElementById(element_id);

    var html = '';

    for (var i = 0; i < list.length; i++) {
        html += template(list[i]);
    }

    element.innerHTML = html;
};

Usage:

populate_children("some-id", blog_entries, blog_entry_template);
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • *(Doh! Editing error, if you see `var formatItem =...` in there anywhere, hit refresh.)* – T.J. Crowder Jan 07 '17 at 12:30
  • So how do I keep the template outside this method ? Because this method will be generic and will accept the template-literal as a parameter. – nehem Jan 07 '17 at 12:31
  • @itsneo: That's not what's in your question. The answer is: Template literals are exactly that: Literals. They aren't objects, you can't pass them around. What you can do instead is put them inside a function and pass the function around. You have the function accept the things the template needs, and return the result of evaluating it. – T.J. Crowder Jan 07 '17 at 12:34
  • Here is my intended signature of the method `var populate_children = function (element_id, objects_list, template) {}` Looks like I need to reinvent my own template parser – nehem Jan 07 '17 at 12:35
  • @itsneo: I've updated the answer to add a bit showing how to use a formatter function for the template part. – T.J. Crowder Jan 07 '17 at 12:37
  • @Crowder Appreciate the answer, Still some gaps from what want to achieve probably I haven't explained them properly what I wanted. Anyway I have updated the question, see if that can make sense. The end result should facilitate a method invocation like this `populate_children(blog_entries_dom, blog_entries, blog_entry_template);` That way this method can be reused to populate blog entry, shopping cart items and any type of lists. – nehem Jan 07 '17 at 12:48
  • 1
    @itsneo: I've updated the answer to reflect the updated question, which is much clearer. (While I was doing that, you accepted the answer, which suggests you figured out how it applied. :-) ) – T.J. Crowder Jan 07 '17 at 13:00
  • @Crowder That's right, The concept is what I was hunting for. Works like a charm. – nehem Jan 07 '17 at 13:52
0

I have other solution, using the prototype might be a good idea!

String.prototype.replaceTemp = function(replaceObj) {
   return this.replace(/{\$(\w{1,20})}/g, function(matchStr, agr) {
     if (replaceObj[agr] instanceof Object || replaceObj[agr] instanceof Function) {
       var _tempPool = (window.TEMP_POOL = window.TEMP_POOL || []);
       var _length = _tempPool.length;
       _tempPool[_length] = replaceObj[agr];

       if (replaceObj[agr] instanceof Function)
         return "TEMP_POOL[" + _length + "](this);"
       else
         return "TEMP_POOL[" + _length + "]";

     } else {
       return replaceObj[agr] || "";
     }

   });
 };


    var blog_entries_dom = 'blog_entries';
    var blog_entries = [
        {
            "title": "Hello World",
            "id": 2,
            "body": "<p>Hello World</p>"
        },
        {
            "title": "World Hello",
            "id": 3,
            "body": "<p>World Hello</p>"
        }
    ];

    var blog_entry_template = `<div class="post-preview">
                            <a href="post.html">
                                <h2 class="post-title">
                                    {$title}
                                </h2>
                                <h3 class="post-subtitle">
                                    {$body}
                                </h3>
                            </a>
                            <p class="post-meta">Posted by <a href="#">Start Bootstrap</a> #Created At </p>
                        </div>
                        <hr>`;


    var populate_children = function (element_id, objects_list, template) {

        var element = document.getElementById(element_id);

        var html = '';

        for (var i = 0; i < list.length; i++) {
            html += template.replaceTemp(list[i]);
        }

        element.innerHTML = html;
    };
Draco
  • 29
  • 3
  • 2
    No, extending the builtin prototypes is not considered a good idea. And that global `TEMP_POOL` certainly is a bad one. – Bergi Mar 07 '17 at 04:44
  • Build in prototype will easy for use, and global `TEMP_POOL` for binding event, otherwise you have to create DOM element first then binding event, but it just simply replace variables in template. – Draco Mar 08 '17 at 03:57