Search code examples
jquery-mobilebackbone.js

Backbone templating in jQuery Mobile rendering outside pages


I'm having a problem when templating into jQuery Mobile using Backbone.js.

The template is being rendered outside of the jQuery Mobile pages. When I change using a jquery mobile class="ui-listview", the rendered is overlapping the pages and I can't fix it.

Any ideas to make this work?

index.html

<!DOCTYPE html>
<html> <!--<![endif] class="no-js"-->
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <title></title>
        <meta name="description" content="">
        <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1, height=device-height>

        <link rel="stylesheet" type="text/css" href="css/jquery.mobile-1.4.3.min.css"/>
        <link rel="stylesheet" href="css/new.css">


    <title>App Init</title>
    </head>
<body>



<section id="mainapp" data-role="page" class="notSelectable">   
    <header data-role="header" data-theme="b">
        <h2>Ads</h2>
    </header>

   <!-- THIS IS NOT WORKING. THE BIGGEST APPROACH IS BY USING class="ui-listview" IN THE "UL" TAG -->
    <div data-role="content">
        <ul data-role="listview" id="elem" >
        </ul>
    </div>

    <script id="personTemplate" type="text/template">
         <strong><%= status %></strong> (<%= id %>) - <%= description %>
    </script>
    <!-- END COMMENT -->


    <footer data-role="footer" data-position="fixed" data-theme="b" data-tap-toggle="false">
        <nav data-role="navbar">
            <ul>
                <li><a href="#localstorage" class="ui-btn ui-icon-recycle ui-btn-icon-notext">Refresh</a></li>
            </ul>
        </nav>
    </footer>
</section>




<section id="localstorage" data-role="page">
    <div id="loaddiv"><input type="file" class="hide" id="loadbutton" />Load</div>

     <footer data-role="footer" data-position="fixed" data-theme="b" data-tap-toggle="false">
        <nav data-role="navbar">
            <ul>
                <li><a href="#localstorage" class="ui-btn ui-icon-recycle ui-btn-icon-notext">Refresh</a></li>
            </ul>
        </nav>
    </footer>
    <div id="alert">Activate</div>
</section>




    <script type="text/javascript" src="js/jquery.js"></script>
    <script type="text/javascript" src="js/underscore.js"></script>
    <script type="text/javascript" src="js/backbone.js"></script>
    <script type="text/javascript" src="js/backbone-localstorage.js"></script>
    <script type="text/javascript" src="js/jquery.mobile-1.4.3.min.js"></script>
    <script type="text/javascript" src="js/appbackbone.js"></script>

</body>
</html>

appbackbone.js

var TodoItem = Backbone.Model.extend({
    defaults: {
        description: "Pick up milk", 
        status: "incomplete"
    },
    toggleStatus: function(){
        if(this.get('status') === 'incomplete'){
          this.set({'status': 'complete'});
        } else {
          this.set({'status': 'incomplete'});
        }
        this.save();        } 
});

var TodoItems = Backbone.Collection.extend({
    model: TodoItem,
    localStorage: new Backbone.LocalStorage("somekey"),

    initialize: function () {
        this.on('remove', this.hideModel, this);
    },

    hideModel: function (model) {
        model.trigger('hide');
    }

});

var TodosView = Backbone.View.extend({
    initialize: function () {
        // add new item and the view would be updated
        this.collection.on('add', this.addOne, this);
    },

    addOne: function (todoItem) {
        var todoView = new TodoView({ model: todoItem });
        this.$el.append(todoView.render().el);
    },
    addAll: function () {
        this.collection.forEach(this.addOne, this); 
    },

    render: function() {
        this.collection.forEach(this.addOne, this);
        this.addAll;
        return this;
    }
});

var TodoView = Backbone.View.extend({
    tagName: 'li',
    id: 'todo-view',
    className: 'todo',

    template: _.template( $('#personTemplate').html() ),

    events: {

        "touchstart": "toggleStatus",
        "touchend": "toggleStatus"
    },

    toggleStatus: function () {
        this.model.toggleStatus();
    },

    remove: function(){
        this.$el.remove();
    },

    initialize: function(){
        this.render();
        this.model.on('change', this.render, this);
        this.model.on('destroy', this.remove, this);
        this.model.on('hide', this.remove, this);
    },

    render: function () {
        var attributes = this.model.toJSON();
        this.$el.html(this.template(attributes));

        return this;
    }
});


var todoItems = new TodoItems([
    {
        description: 'Jeffrey Way',
        status: "incomplete",
        id: 1
    },
    {
        description: 'John Doe',
        status: "incomplete",
        id: 2
    },
    {
        description: 'Sally Doe',
        status: "incomplete",
        id: 3
    }
]);

var todosView = new TodosView({ 
    el: $('#elem'),
    collection: todoItems
});

$(document.body).append(todosView.render().el);

Solution

  • You are appending the results of your render to the body which is placing it outside of your sections.

    $(document.body).append(todosView.render().el);
    

    should be

    todosView.render().el;
    

    your collection view has an $el specified so it already knows where to place the content when you set it's html so no need to append it.

    Also in some parts of your code you call render from your initialise but you also then call render. Best practice is to call it explicitly and not place the call to render inside you initialise method. and in you render of the collection you loop over your collection rendering each item but them try and call addAll to do the same. you can remove the collection loop and just call all

    render: function() {
    
            this.addAll();
            return this;
        }
    

    here is a code pen to show it working