0
votes

So I've been struggling for quire sometime to do this. I am building a dynamic form generator tool. One of the functions is that the user should be able to use a jQuery slider to select the font size. I use Underscore templates to create divs for the slider. So everytime a user selects a Label input, this underscore template is loaded and I call the slider() fn to initialize it. Here's is the part of the code that does this

UnderscoreTemplate

<!-- template for plain text inpt to be shown in form generate -->
    <script type="text/template" id="text-generate-template">       
        <div class="row">       
            <div class="col-sm-10">
                <input type="text"  placeholder="Enter text here" class="label-name form-control" value="<%=model.get('label')%>">
            </div>              
            <button type="button" class="close" id="remove-element" >&times;</button>
        </div>          
        <div class="row col-sm-6"  style="margin-top:10px">
            <div id="slider-<%=model.cid%>"></div>
            <small>Font Size:  <%=model.get('fontSize')%>px </small>            
        </div>  
    </script>

So as per the code ablove, each dynamically loaded element has a unique slider with a unique ID.

The Backbone Model extends from a Base element called "Element"

    var TextElement = Element.extend({
            defaults:function(){
                return _.extend({}, Element.prototype.defaults,{
                    name: 'PlainText',
                    generateTemplateType: 'text-generate-template',
                    previewTemplateType:'text-template',
                    textAlign: 'center'     
                });
            }                               
        });

The view which is responsible for generating the html is

var ElementView = Backbone.View.extend({

            tagName: 'li',
            events : {              

                },
            initialize : function(){
                this.listenTo(this.model,'change', this.render);        
                this.render();
            },
            render : function(){                            
                var htmlContent =  $('#'+this.model.get('generateTemplateType')).html();                        
                var content = _.template( htmlContent, {elementType : elementTypes, model : this.model} );                                      
                this.$el.html( content );           
                me = this;                  
                if(this.model.get('name') == 'PlainText'){                      
                    this.$el.find('#slider-'+this.model.cid).slider({
                         min: 10,
                         max:40,
                         step: 1,
                         value:me.model.get('fontSize') > 9 ? me.model.get('fontSize') : 24,
                         change: function(event, ui) {
                            me.slide(event, ui.value);
                        }
                    }); 
                }

                return this;
            },              

            slide: function(event, index){                  
                console.log("the models id in slide function -> "+this.model.cid);
                this.model.set('fontSize', index);                  
            }
        });

So what I do here is that every time I detect a change, I update this model's fontSize to the new value passed in through the Slider.

THe collection to which this model belongs to recognises this changes and rerenders itself.

When there is only one such element on the page, then everything works fine. The moment the user adds more than one element and tried to change the font size for each item, then only the font size of the last element is changed. Regardless of whether I slide the first , second or the third element it always sets the change to the last model in the collection and the last model get rerendered with the incorrect font size value.

I tried console logging the id of the model in the slide function and it always seems to return the last models ID regardless of which slider I use.

What am I doing wrong here??

1

1 Answers

0
votes

You have an accidental global variable right here:

me = this;

A side effect of this is that every instance of ElementView will end up sharing exactly the same me and that me will be the last ElementView you instantiate. Sound familiar?

The solution is to use a local variable:

var me = this;

or, if you don't need the slider's this inside the callback, use a bound function instead:

value: this.model.get('fontSize') > 9 ? this.model.get('fontSize') : 24,
change: _(function(event, ui) {
    this.slide(event, ui.value);
}).bind(this)

You could also use $.proxy or Function.prototype.bind if you prefer those over _.bind.