I have a Backbone view in which I want to fire a event resizeMovieFrame when the view gets initialized. It reacts to the $(window).on("load", #resizeMovieFrame) because 'hello' gets shown in the console.
But the resize code doesn't do anything on load. But when I resize my browser it does work. So I'm thinking that when the $(window).on("resize", #resizeMovieFrame) gets fired there aren't any .movie-frame divs to resize. If this is the case, what would be the propper load order?
class Movieseat.Views.MovieseatsIndex extends Backbone.View
template: JST['movieseats/index']
id: 'something'
initialize: ->
#listenTo #collection, 'change', #renderEntries, this
#listenTo #collection, 'add', #renderEntries, this
#listenTo #collection, 'destroy', #renderEntries, this
$(window).on("resize", #resizeMovieFrame)
$(window).on("load", #resizeMovieFrame)
render: ->
$(#el).html(#template(entries: #collection))
this
events: ->
"click li": "addEntry"
"click .remove": "destroyEntry"
addEntry: (e) ->
movie_title = $(e.target).parent().find('.movie-title').text()
poster_path = $(e.target).parent().find('img').attr('src')
release_date = $(e.target).parent().find('.release_date').text()
console.log poster_path
#collection.create title: movie_title, image: poster_path, release_date: release_date
destroyEntry: (e) ->
thisid = #$(e.currentTarget).closest('div').parent().data('id')
#collection.get(thisid).destroy()
renderEntries: (entry) ->
view = new Movieseat.Views.Showmovie(collection: #collection)
$('#movie-container').html(view.render().el)
resizeMovieFrame: ->
equalheight = (container) ->
currentTallest = 0
currentRowStart = 0
rowDivs = new Array()
$el = undefined
topPosition = 0
$(container).each ->
$el = $(this)
$($el).height "auto"
topPostion = $el.position().top
unless currentRowStart is topPostion
currentDiv = 0
while currentDiv < rowDivs.length
rowDivs[currentDiv].height currentTallest
currentDiv++
rowDivs.length = 0 # empty the array
currentRowStart = topPostion
currentTallest = $el.height()
rowDivs.push $el
else
rowDivs.push $el
currentTallest = (if (currentTallest < $el.height()) then ($el.height()) else (currentTallest))
currentDiv = 0
while currentDiv < rowDivs.length
rowDivs[currentDiv].height currentTallest
currentDiv++
return
return
$(window).load ->
equalheight ".movie-frame"
return
$(window).resize ->
equalheight ".movie-frame"
return
console.log ('hello')
The resizeMovieFrame event resizes all the .movie-frame divs to the same height.
you probably need to bind the context of the resizeWindowFrame function.
resizeMovieFrame: =>
Related
I have a CompositeView that I am working with in Marionette that when the collection fetches can potentially pull 1000s of records back from the API. The problem I am seeing is that when the UI loads, the browser wigs out and the script freezes the browser up.
I have tried following this blog post (http://lostechies.com/derickbailey/2011/10/11/backbone-js-getting-the-model-for-a-clicked-element/), but can't seem to figure out how to translate that to working in Marionette.
Here is my CompositeView:
var EmailsMenuView = Marionette.CompositeView.extend({
template: _.template(templateHTML),
childView: EmailItemView,
childViewOptions: function(model, index){
return {
parentIndex: index,
parentContainer: this.$el.closest('form')
};
},
childViewContainer: '.emails',
ui: {
emails: '.emails',
options: '.email-options',
subject: "#subject_line",
fromName: "#from_name",
fromEmail: "#from_email",
thumbnail: '.thumbnail img',
emailName: '.thumbnail figcaption'
},
events: {
'change #ui.subject': 'onSubjectChange',
'change #ui.fromName': 'onFromNameChange',
'change #ui.fromEmail': 'onFromEmailChange'
},
childEvents: {
'menu:selected:email': 'onMenuEmailSelect'
},
collectionEvents: {
'change:checked': 'onEmailSelected'
},
behaviors: {
EventerListener: {
behaviorClass: EventerListener,
eventerEvents: {
'menu:next-click': 'onNext',
'menu:previous-click': 'onPrev'
}
},
LoadingIndicator: {
behaviorClass: LoadingIndicator,
parentClass: '.emails'
}
},
renderItem: function(model) {
console.log(model);
},
/**
* Runs automatically during a render
* #method EmailsMenuView.onRender
*/
onRender: function () {
var colCount, showGridList, selected,
threshold = 300;
if(this.model.get('id')) {
selected = this.model;
}
// refresh its list of emails from the server
this.collection.fetch({
selected: selected,
success: function (collection) {
colCount = collection.length;
console.log(colCount);
},
getThumbnail: colCount <= threshold
});
this.collection.each(this.renderItem);
var hasEmail = !!this.model.get('id');
this.ui.emails.toggle(!hasEmail);
this.ui.options.toggle(hasEmail);
eventer.trigger(hasEmail ? 'menu:last' : 'menu:first');
}
});
My ItemView looks like this:
var EmailItemView = Marionette.ItemView.extend({
tagName: 'article',
template: _.template(templateHTML),
ui: {
radio: 'label input',
img: 'figure img',
figure: 'figure'
},
events: {
'click': 'onSelect',
'click #ui.radio': 'onSelect'
},
modelEvents: {
'change': 'render'
},
behaviors: {
Filter: {
behaviorClass: Filter,
field: "email_name"
}
},
/**
* initializes this instance with passed options from the constructor
* #method EmailItemView.initialize
*/
initialize: function(options){
this.parentIndex = options.parentIndex;
this.parentContainer = options.parentContainer;
this.listenTo(eventer, 'menu:scroll', this.onScroll, this);
},
/**
* Runs automatically when a render happens. Sets classes on root element.
* #method EmailItemView.onRender
*/
onRender: function () {
var checked = this.model.get("checked");
this.$el.toggleClass('selected', checked);
this.ui.radio.prop('checked', checked);
},
/**
* Runs after the first render, only when a dom refrsh is required
* #method EmailItemView.onDomRefresh
*/
onDomRefresh: function(){
this.onScroll();
},
/**
* Marks this item as checked or unchecked
* #method EmailItemView.onSelect
*/
onSelect: function () {
this.model.collection.checkSingle(this.model.get('id'));
this.trigger('menu:selected:email');
},
templateHelpers: {
formattedDate: function() {
var date = this.updated_ts.replace('#D:', '');
if (date !== "[N/A]") {
return new Moment(date).format('dddd, MMMM DD, YYYY [at] hh:mm a');
}
}
},
/**
* Delays the load of this view's thumbnail image until it is close to
* being scrolled into view.
* #method EmailItemView.onScroll
*/
onScroll: function () {
if (this.parentIndex < 10) {
// if it's one of the first items, just load its thumbnail
this.ui.img.attr('src', this.model.get('thumbnail') + '?w=110&h=110');
} else if (this.parentContainer.length && !this.ui.img.attr('src')) {
var rect = this.el.getBoundingClientRect(),
containerRect = this.parentContainer[0].getBoundingClientRect();
// determine if element is (or is about to be) visible, then
// load thumbnail image
if (rect.top - 300 <= containerRect.bottom) {
this.ui.img.attr('src', this.model.get('thumbnail') + '?w=110&h=110');
}
}
}
});
I am trying to adjust my CompositeView to work where I can build the HTML and then render the cached HTML versus appending 1000s of elements
Render all 1000 elements isn't good idea - DOM will become too big. Also on fetch there are parse for all 1000 models, and it works synchroniously. So there are two ways - load less data, or splist data to parse/render by chunks
I want to hide my "show more"-button as soon all my models has been loaded into the collection. How can I achieve that? Lets say, I have a collection which contain 20 models. I display 4 to start with and when I have clicked myself through to all 20 models, the "showMore"- button should disappear.
So far I have in my View:
events: {
'click .showMore': 'showMore'
},
showMore: function(){
this.collection.fetch({remove: false});
},
afterRender: function(){
var collection = this.collection;
if(collection.length > 3) {
$('<button class="showMore">Show more</button>').insertAfter('div.news');
}
}
And my Collection:
myCollection = Backbone.Collection.extend({
step: 0,
parse: function(response){
var slice = response.data.news.slice(this.step*4,(this.step+1)*4)
this.step++;
return slice;
}
});
Thanks in advance...
You could try counting the # of models you have on the page. For example, if your models are in <div class="myModel">, you could add
if(document.getElementByClassName('myModel').length == collection.length) {
$('.showMore').hide()
}
to your afterRender function.
I solved myself in a different way!
If I console.log the length, the collection has the amount of models given by the var slice-function I have in my Collection (see code).
So I made it like this:
afterRender: function(){
$("article.news:gt(3)").hide();
var count = $("article.news").length;
if(count === 1){
$(".showmore").hide();
}
var i = 4;
$(".showmore").on("click" , function() {
i = i + 4;
$("article.news:lt(" + i + ")").show();
if(i > count){
$(".showmore").hide();
}
});
}
and removed the .slice()-method completely.
article.news is the model selector in this case.
Its not the prettiest solution, but it works for me.
I want to bind click events to points created by a chart instantiated by a Backbone View, but events and delegateEvents aren't working (ie, no event is fired on clicking '.point' elements):
// View
var ScatterView = Backbone.View.extend({
defaults:{
// height, width, etc.
},
events:{
'click .point':'clickFunction'
},
clickFunction:function() {
console.log('clicked')
},
initialize:function(options) {
this.options = _.extend({}, this.defaults, options)
this.render()
},
render:function() {
var that = this
this.chart = new Scatter({
container:that.el.id,
// pass parameters: width, height, etc.
})
this.delegateEvents()
}
})
// Scatter function
var Scatter = function(settings) {
this.settings = settings
this.build()
}
Scatter.prototype.build = function() {
// Bind points
this.div = d3.select('#' + settings.container).append('div').attr('id', settings.id).attr('class', 'chart')
this.g = this.div.append('g')
this.points = this.g.selectAll('.point')
.data(this.data, function(d) {return d.id})
.enter().append('circle')
.attr('cx', function(d) {return that.xScale(d.x)})
.attr('cy', function(d) {return that.yScale(d.y)})
.attr('r', that.settings.radius)
.attr('class', 'point')
}
// instantiate view
var sv = new ScatterView({model:sm, el:'#main', chartid:'scatter-' + d, textid:'text-' + d})
You need to set an el property, for example:
var ScatterView = Backbone.View.extend({
el: '#chartContainer', ...
});
Try this:
events:{
"click [class~=point]" : "clickFunction"
}
The problem is that Backbone uses jQuery event binding under the covers, but you're trying to set click events on SVG elements. Without additional plugins, jQuery won't reliably select SVG elements, so there's no way it can bind events to them.
I have the following setup:
class App.Views.Maps extends Backbone.View
el: '#map'
events:
initialize: ->
#searchModel = new App.Models.Search()
#view = new App.Views.MapBox(map: this)
#render()
render: ->
#loadMap()
$("[rel=tooltip]").tooltip()
loadMap: =>
osmMapType = new google.maps.ImageMapType(
getTileUrl: (coord, zoom) ->
"http://tile.openstreetmap.org/#{zoom}/#{coord.x}/#{coord.y}.png"
tileSize: new google.maps.Size(256, 256)
isPng: true
alt: "OpenStreetMap layer"
name: "OSM"
maxZoom: 19
)
cloudMadeMapType = new google.maps.ImageMapType(
getTileUrl: (coord, zoom) ->
"http://b.tile.cloudmade.com/111/54912/256/#{zoom}/#{coord.x}/#{coord.y}.png"
tileSize: new google.maps.Size(256, 256)
isPng: true
alt: "CloudMade layer"
name: "CMade"
maxZoom: 13
)
lat = 51.503
lng = -0.113
latlng = new google.maps.LatLng(lat, lng)
options =
zoom: 10
center: latlng
mapTypeId: 'OSM'
#gMap = new google.maps.Map(document.getElementById("map"), options)
#gMap.mapTypes.set('OSM', osmMapType)
#gMap.mapTypes.set('CloudMade', cloudMadeMapType)
#gMap.setMapTypeId('CloudMade')
allowedBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(51.278, -0.536)
new google.maps.LatLng(51.701, 0.309)
)
lastValidCenter = new google.maps.LatLng(51.503,-0.113)
google.maps.event.addListener #gMap, "dragend", =>
if allowedBounds.contains(#gMap.getCenter())
lastValidCenter = #gMap.getCenter()
return
$('#myModal').modal(backdrop: true)
$('#myModal').on('hide', =>
origin = new google.maps.LatLng(51.438264485659836,-0.05715396179630261)
#gMap.setCenter(origin)
center = google.maps.LatLng(51.503,-0.113)
#gMap.panTo(origin)
)
#initLabel()
initLabel: =>
#rendered = view.render().el
#console.log rendered
#initLabel = new InfoBubble(
position: new google.maps.LatLng(51.44115356738888, 0.14849636779354114)
maxWidth: 240
maxHeight: 210
padding: 0
content: '<div class="tooltip_header"></div>'
tabPadding: 15
backgroundColor: 'black'
borderRadius: 0
arrowSize: 10
borderWidth: 0
borderColor: '#AB2424'
disableAutoPan: true
hideCloseButton: false
arrowPosition: 0.5
backgroundClassName: 'phoney'
tabClassName: 'tabClass'
activeTabClassName: 'activeTabClass'
arrowStyle: 2
)
#initLabel.open(#gMap)
This loads a map and then loads an InfoBubble ontop of the map. It loads it with the content <div class="tooltip_header"></div>
After this has loaded, if I append the loadMap and add
view = new App.Views.MapBox()
view.render().el
which attempts to load the view:
class App.Views.MapBox extends Backbone.View
el: '.tooltip_header'
events:
'click .testdrive' : 'loadTestDrive'
'click .test' : 'loadTestDrive'
template: JST["app/backbone/templates/mapbox"]
initialize: ->
#render()
render: ->
$(#el).html(#template())
this
loadTestDrive: ->
console.log #options.map
console.log "yessss"
#options.map.loadTestDrive()
Nothing happens... however if I go into console and do:
view = new App.Views.MapBox({map: this})
The content is rendered inside the Infobubble ontop of the map.
I think its because the load of InfoBubble is Asynchronous and I am calling the div to be rendered before it exists. But I have tried delay loading and it still happens.
What is the best way to get this view to render after the infobubble is loaded and the div is therefore available. This is why it works in console. but not on load.
Just use google maps events to listen to when the map is fully loaded like this
google.maps.event.addListener(map, 'tilesloaded', _.bind(function() {
this.render();
google.maps.event.clearListeners(map, 'tilesloaded');
},this));
With this you are 100% sure the map is rendered and the google.maps globals are avaliable and initialized.
Is there any way to change the text of "Update" button in ExtJS-4 Row Editor Grid ?
Good question, I had a look through the source code and whilst there is nothing inside the RowEditing plugin, in the class it extends 'RowEditor.js' there is the following:
Ext.define('Ext.grid.RowEditor', {
extend: 'Ext.form.Panel',
requires: [
'Ext.tip.ToolTip',
'Ext.util.HashMap',
'Ext.util.KeyNav'
],
saveBtnText : 'Update',
cancelBtnText: 'Cancel',
...
});
So I'd assume you'd just need to override the 'saveBtnText' in your instance of 'Ext.grid.plugin.RowEditing' as it calls the parent constructor with callParent(arguments) in the RowEditing class
Not that easy and not without hacking in undocumented areas. The problem is, that the Ext.grid.plugin.RowEditing directly instantiates the Ext.grid.RowEditor without allowing you to pass in configuration options. So in general you have to override the initEditor() method in the plugin and instantiate your own row editor:
// ...
plugins: [{
ptype: 'rowediting',
clicksToEdit: 2,
initEditor: function() {
var me = this,
grid = me.grid,
view = me.view,
headerCt = grid.headerCt;
return Ext.create('Ext.grid.RowEditor', {
autoCancel: me.autoCancel,
errorSummary: me.errorSummary,
fields: headerCt.getGridColumns(),
hidden: true,
// keep a reference..
editingPlugin: me,
renderTo: view.el,
saveBtnText: 'This is my save button text', // <<---
cancelBtnText: 'This is my cancel button text' // <<---
});
},
}],
// ...
For ExtJS 4
Ext.grid.RowEditor.prototype.cancelBtnText = "This is cancel";
Ext.grid.RowEditor.prototype.saveBtnText = "This is update";
This solution is to define the prototype of rowEditors. that means that this config is than general.
If you want to change it just for one editor, or if you want to get different configs , the prototype is definitely not the solution.
look at source code :
initEditorConfig: function(){
var me = this,
grid = me.grid,
view = me.view,
headerCt = grid.headerCt,
btns = ['saveBtnText', 'cancelBtnText', 'errorsText', 'dirtyText'],
b,
bLen = btns.length,
cfg = {
autoCancel: me.autoCancel,
errorSummary: me.errorSummary,
fields: headerCt.getGridColumns(),
hidden: true,
view: view,
// keep a reference..
editingPlugin: me
},
item;
for (b = 0; b < bLen; b++) {
item = btns[b];
if (Ext.isDefined(me[item])) {
cfg[item] = me[item];
}
}
return cfg;
}`
this method inits the rowEditor, and there's a loop on btns Array:
btns Array :
btns = ['saveBtnText', 'cancelBtnText', 'errorsText', 'dirtyText']
for (b = 0; b < bLen; b++) {
item = btns[b];
if (Ext.isDefined(me[item])) {
cfg[item] = me[item];
}
}
In this loop foreach string in btnArray it's searched if exists in cfg the same string property, if it's found it's added to config. You just have to manage that this loop finds what you want to modify:
Example: we want to change the text of save button:
the property saveBtnText which is the first item of btns Array must exists in cfg:
if (Ext.isDefined(me[item])) {
cfg[item] = me[item];
}
this search if property exists : if (Ext.isDefined(me[item]))
if saveBtnText already exists in rowEditor properties then:
cfg[item] = me[item];
and the additional config property will be set!!