Underscore Template Looping, Without A Loop?

2019-08-12 06:52发布

This sort of follows on from another question I asked but the guy I think was trying to explain something else, or more likely I did not explain my self right.

So all my backbone code is working, and displaying out the output right data. Now for this explain with my test data, I have 17 rows of inputs, but even without making a _.each loop, its looping around 17 times, which I just don't understand why?

So my Backbone code,

var TimeSheetModel = Backbone.Model.extend({
     defaults: {
        Timesheetrow: "",

     }
}); //End of Timesheet Model

var TimeSheetCollection = Backbone.Collection.extend({
    model: TimeSheetModel,
    url: '/dashboard/jsondata/' + TimesheetID()
}); //End of Timesheet Collection

var TimeSheetView = Backbone.View.extend({
    el:'#testarea', //HTML loading area for the data

    template: _.template( $('#TimesheetData').html() ), //Template to load the JSON data into

    initialize: function(){
      var NewTimeSheetCollection = new TimeSheetCollection(); //New Instance Of Collecttion
      this.listenTo(NewTimeSheetCollection, "add", this.AddMyModel);
      NewTimeSheetCollection.fetch();
    },

    AddMyModel: function(TimeSheetModel) {  //apply model data to view template and append to view element   
       this.$el.append(this.template(TimeSheetModel.toJSON()));
       //$(this.el).html(this.template(TimeSheetRowModel.toJSON()));
    }
}); //End of Timesheet View

//New Instance & render call for the set Timesheet View.
NewTimeSheetVew = new TimeSheetView();
//NewTimeSheetVew.render(); <- do I need this? seems to work, without it?

});

And my Underscore template code

<script type="text/template" id="TimesheetData">

<% console.log(Timesheetrow) %>

<% _(1).times(function(n){ n=2;  }); //just a test bit of code %>


 <% if(Timesheetrow.jobtitle) { %>

   <form action="#" method="post" id="TimesheetDataList">

        <div class="TimesheetRowData">
            <input type="hidden" name="data[Timesheetrow][0][id]" value="<%= Timesheetrow.id  %>">
            <input type="type" name="data[Timesheetrow][0][jobtitle]" value="<%= Timesheetrow.jobtitle  %>">
        </div>

    </form>

 <%  }; %>

</script>

so my goal is to use backbone, to have one master form, and to loop around 17 times (or X, depending on user given rows). So, for example, I would have an output like:

<form id="ID-HERE">
      <div><input value="XXXXX"></div>
      <div><input value="XXXXX"></div>
      <div><input value="XXXXX"></div>
      <div><input value="XXXXX"></div>
      <div><input value="XXXXX"></div>
</form>

But what I am getting is that is loops 17 times, so each input as its own form tag, so I currently have 17 forms on my page.

Any help most welcome.

Please, let me know if I have not explained myself right, I am dyslexic, so my spelling, grammar, may be a little off, sorry.

Thanks,

1条回答
姐就是有狂的资本
2楼-- · 2019-08-12 07:31

You are listening to the add event in the collection, which will be fired after a fetch operation for each model retrieved from the server:

this.listenTo(NewTimeSheetCollection, "add", this.AddMyModel);
NewTimeSheetCollection.fetch();

So your server is returning 17 rows, the add event is fired 17 times, and every time your AddMyModel function is rendering the template. (If you have a look at the documentation, the fetch uses internally the set method which will fire the add events).

Your problem is that the template you are using to render each model contains the form tag, which shouldn't be repeated for every model.

You have a few ways of fixing this, basically splitting what has to be rendered once and what has to be rendered for each model:

  • Update your template to render an array of models, manually looping inside the template. Do not listen to the add events in the collection and instead pass a success callback to the fetch method, where you render your template passing to it the array of models.

  • Split your template in 2: a master one with the form loop and a child one with the inputs for a single TimesheetRow. Render the master template on view initialize, and keep listening to collection add events. On AddMyModel, render the child template and append it inside the form.

  • Split your view into a master view and a child view. The master view will render the form and fetch the collection, listening to add events. For every added model, it will create a new instance of the child view passing to it the added model, rendering it and appending its el inside the form.


EDIT

I have created a jsfiddle with the third option. There are 2 views TimeSheetRowView and TimeSheetCollectionView:

var TimeSheetRowView = Backbone.View.extend({
    className:'TimesheetRowData', 
    template: _.template($('#TimesheetData').html()), 
    render: function() {
        this.$el.append(this.template(this.model.toJSON()));
        return this.$el;
    }
});

var TimeSheetCollectionView = Backbone.View.extend({
    el:'#MasterContainer', 
    template: _.template($('#TimesheetForm').html()),
    initialize: function(){
        //render master template
        this.$el.append(this.template());
        //keep element so we insert child views before it, inside the form
        this.submitButton = this.$(".actionSubmit"); 
        //initialize collection and listen to events
        this.collection = new TimeSheetCollection();
        this.listenTo(this.collection, "add", this.AddTimesheetRow);

        //simulate a fetch just setting the models
        var hardcodedModels = [{Timesheetrow: {id: 1, index: 0, jobtitle: 'Foo'}},
                              {Timesheetrow: {id: 2, index: 1, jobtitle: 'Bar'}}];
        this.collection.set(hardcodedModels);
    },
    AddTimesheetRow: function(model) {
        //render a single row view and insert it inside the form, right before the submit button
        var view = new TimeSheetRowView({model: model});
        //view.render() returns its $el so we can chain the insertBefore method
        view.render().insertBefore(this.submitButton); 
    }
});

And the templates have been divided as:

<script type="text/template" id="TimesheetForm">
   <form action="#" method="post" id="TimesheetDataList">   
       <input type="submit" class="actionSubmit" value="Send"/>
   </form>
</script>

<script type="text/template" id="TimesheetData">     
    <input type="hidden" name="data[Timesheetrow][<%= Timesheetrow.index %>][id]" value="<%= Timesheetrow.id  %>">
    <input type="type" name="data[Timesheetrow][<%= Timesheetrow.index %>][jobtitle]" value="<%= Timesheetrow.jobtitle  %>">        
</script>
查看更多
登录 后发表回答