unkn0wn unkn0wn - 29 days ago 5
Javascript Question

How do i pass a received array into underscore template

I am getting a long array from PHP containing various data objects.

[{"commid":"1","uid":"0","pid":"3","comment":"comm","parid":null,"date":"2016-10-27 15:03:10"},
{"commid":"2","uid":"0","pid":"10","comment":"Ana","parid":null,"date":"2016-10-27 15:03:51"},
{"commid":"3","uid":"0","pid":"5","comment":"asss!","parid":null,"date":"2016-10-27 15:05:50"},
{"commid":"4","uid":"0","pid":"10","comment":"Lawl?","parid":null,"date":"2016-10-27 17:03:59"},
{"commid":"5","uid":"0","pid":"14","comment":"sd","parid":null,"date":"2016-11-06 00:25:04"},
{"commid":"6","uid":"0","pid":"14","comment":"sds","parid":null,"date":"2016-11-06 00:25:50"},
{"commid":"7","uid":"0","pid":"14","comment":"WOW!","parid":null,"date":"2016-11-08 15:06:18"},
{"commid":"8","uid":"0","pid":"13","comment":"Hello!?","parid":null,"date":"2016-11-08 15:14:30"}]


My Backbone View which will be rendering the data is

var WorkPage = Backbone.View.extend({
el: $('#indexcontent'),
render: function() {
Backbone.history.navigate('work');
var _this = this;

this.$el.html(workHTML);
$.ajax({
type: "GET",
url: "includes/server_api.php/work",
data: '',
cache: false,
success: function(html) {
console.log(html);
var compiledTemplate = _.template($('#content-box').html(), html);
_this.$el.html(compiledTemplate);
},
error: function(e) {
console.log(e);
}
});
return false;
}
});


My workHTML which will be rendered by Underscore is

<script type="text/template" id="content-box">
<div class="workhead">
<h3 class="msg comment"><%= comment%></h3>
<p class="date"><%= date%></p>
</div>
</script>
<div id="content-box-output"></div>


How do I implement a underscore loop here?

Answer

You should take advantage of Backbone's features. And to do that, you need to understand how to use a REST API with Backbone.

Backbone's Model is made to manage a single object and handle the communication with the API (GET, POST, PATCH, PUT requests).

Backbone's Collection role is to handle an array of model, it handles fetching it and it also parse each object into a Backbone Model by default.

Instead of hard-coding a jQuery ajax call, use a Backbone collection.

var WorkCollection = Backbone.Collection.extend({
    url: "includes/server_api.php/work",
});

Then, modularize your views. Make an item view for each object of the array you received.

var WorkItem = Backbone.View.extend({
    // only compile the template once
    template: _.template($('#content-box').html()),
    render: function() {

        // this is how you pass data to the template
        this.$el.html(this.template(this.model.toJSON()));

        return this; // always return this in the render function
    }
});

Then your list view looks like this:

var WorkPage = Backbone.View.extend({
    initialize: function(options) {
        this.itemViews = [];
        this.collection = new WorkCollection();

        this.listenTo(this.collection, 'reset', this.render);

        // this will make a GET request to
        // includes/server_api.php/work
        // expecting a JSON encoded array of objects
        this.collection.fetch({ reset: true });
    },
    render: function() {
        this.$el.empty();
        this.removeItems();
        this.collection.each(this.renderItem, this);
        return this;
    },

    renderItem: function(model) {
        var view = new WorkItem({
            model: model
        });
        this.itemViews.push(view);
        this.$el.append(view.render().el);
    },
    // cleanup to avoid memory leaks
    removeItems: function() {
        _.invoke(this.itemViews, 'remove');
        this.itemViews = [];
    }

});

It's unusual to set the url in the render function, you should keep the responsibilities scoped to the right place.

The router could be something like:

var Router = Backbone.Router.extend({
    routes: {
        'work': 'workPage'
    },

    workPage: function() {
        var page = new WorkPage({
            el: $('#indexcontent'),
        });
    }
});

Then, if you want to see the work page:

var myRouter = new Router();

Backbone.history.start();

myRouter.navigate('#work', { trigger: true });

Templates and RequireJS

My index.html page contains this indexcontent div but the content-box which contains the template format which we are compiling is stored in different work.html. So, if i dont load this work.html in my main index.html i am unable to access content-box.

I would recommend to use the text require.js plugin and load each template for the view like this:

The work-item.js file:

define([
    'underscore', 'backbone',
    'text!templates/work-item.html',
], function(_, Backbone, WorkItemTemplate) {
    var WorkItem = Backbone.View.extend({
        template: _.template(WorkItemTemplate),
        /* ...snip... */
    });
    return WorkItem;
});

The work-page.js file:

define([
    'underscore', 'backbone',
    'text!templates/work-page.html',
], function(_, Backbone, WorkPageTemplate) {
    var WorkPage = Backbone.View.extend({
        template: _.template(WorkPageTemplate),
    });
    return WorkPage;
});