I'm currently writing an application using Backbone and for some reason, it doesn't update a view, but only in certain circumstances.
If I refresh the page at index.html#/blog/2 it loads the page just fine, everything works great. However, if I refresh the page at index.html#/blog/1 and then change the URL to index.html#/blog/2 and press enter(NOT refresh), the change never gets fired.
This is my router:
makeChange: function() {
// Set activePage to the current page_id => /blog/2
var attributes = {activePage: this.page_id};
var $this = this;
// Loop through all sections in the app
app.sections.some( function( section ) {
// Check if section has the page
if( !section.validate( attributes ) )
{
// If it has, set the activePage to /blog/2
section.set( attributes, {silent: true} );
// Set active section to whatever section-id was matched
app.set( {activeSect: section.id}, {silent: true} );
console.log('Calling change!');
// Calling change on both the app and the section
app.change();
section.change();
console.log('Change complete!');
return true;
}
});
}
This is the app view(which is referenced as "app" up above^):
var AppView = Backbone.View.extend({
initialize: function( option ) {
app.bind( 'change', _.bind( this.changeSect, this ) );
},
changeSect: function() {
var newSect = app.sections.get( app.get('activeSect' ) );
var newSectView = newSect.view;
if( !app.hasChanged( 'activeSect' ) )
newSectView.activate( null, newSect );
else
{
var oldSect = app.sections.get( app.previous( 'activeSect' ) );
var oldSectView = oldSect.view;
newSectView.activate( oldSect, newSect );
oldSectView.deactivate( oldSect, newSect );
}
}
});
Tell me if you need to see some other classes/models/views.
I solved it! This only happens when navigating between different pages(by changing activePage in the section) in the same section, so activeSect in app was never changed, thus never called changeSect(). Now even when activeSect is the same in the app, and activePage has changed in the section, it will call the changeSect() in the app anyway.
In Section-model, I added this:
initialize: function() {
this.pages = new Pages();
this.bind( 'change', _.bind( this.bindChange, this ) );
},
prepareForceChange: function() {
this.forceChange = true;
},
bindChange: function() {
console.log('BINDCHANGE!');
if( this.forceChange )
{
AppView.prototype.forceChange();
this.forceChange = false;
}
},
In router.makeChange() above this:
section.set( attributes, {silent: true} );
app.set( {activeSect: section.id}, {silent: true} );
I added:
var oldSectId = app.get('activeSect');
if( oldSectId == section.id ) section.prepareForceChange();
Related
I'm using a library called Golden Layout, it has a function called destroy which will close all the application window, on window close or refesh
I need to add additional method to the destroy function. I need to removeall the localstorage aswell.
How do i do it ? Please help
Below is the plugin code.
lm.LayoutManager = function( config, container ) {
....
destroy: function() {
if( this.isInitialised === false ) {
return;
}
this._onUnload();
$( window ).off( 'resize', this._resizeFunction );
$( window ).off( 'unload beforeunload', this._unloadFunction );
this.root.callDownwards( '_$destroy', [], true );
this.root.contentItems = [];
this.tabDropPlaceholder.remove();
this.dropTargetIndicator.destroy();
this.transitionIndicator.destroy();
this.eventHub.destroy();
this._dragSources.forEach( function( dragSource ) {
dragSource._dragListener.destroy();
dragSource._element = null;
dragSource._itemConfig = null;
dragSource._dragListener = null;
} );
this._dragSources = [];
},
I can access the destroy method in the component like this
this.layout = new GoldenLayout(this.config, this.layoutElement.nativeElement);
this.layout.destroy();`
My code
#HostListener('window:beforeunload', ['$event'])
beforeunloadHandler(event) {
var originalDestroy = this.layout.destroy;
this.layout.destroy = function() {
// Call the original
originalDestroy.apply(this, arguments);
localStorage.clear();
};
}
Looking at the documentation, GoldenLayout offers an itemDestroyed event you could hook to do your custom cleanup. The description is:
Fired whenever an item gets destroyed.
If for some reason you can't, the general answer is that you can easily wrap the function:
var originalDestroy = this.layout.destroy;
this.layout.destroy = function() {
// Call the original
originalDestroy.apply(this, arguments);
// Do your additional work here
};
You may be able to do this for all instances if necessary by modifying GoldenLayout.prototype:
var originalDestroy = GoldenLayout.prototype.destroy;
GoldenLayout.prototype.destroy = function() {
// Call the original
originalDestroy.apply(this, arguments);
// Do your additional work here
};
Example:
// Stand-in for golden laout
function GoldenLayout() {
}
GoldenLayout.prototype.destroy = function() {
console.log("Standard functionality");
};
// Your override:
var originalDestroy = GoldenLayout.prototype.destroy;
GoldenLayout.prototype.destroy = function() {
// Call the original
originalDestroy.apply(this, arguments);
// Do your additional work here
console.log("Custom functionality");
};
// Use
var layout = new GoldenLayout();
layout.destroy();
Hooking into golden layout is the intended purpose for the events.
As briefly touched on by #T.J. Crowder, there is the itemDestroyed event which is called when an item in the layout is destroyed.
You can just listen for this event like such:
this.layout.on('itemDestroyed', function() {
localStorage.clear();
})
However, this event is called every time anything is destroyed, and propagates down the tree, even just by closing a tab. This means that if you call destroy on the layout root, you will get an event for every RowOrColumn, Stack and Component
I would recommend to check the item passed into the event and ignore if not the main window (root item)
this.layout.on('itemDestroyed', function(item) {
if (item.type === "root") {
localStorage.clear();
}
})
I have a page loading script that swpas between two dives (giving the illusion of moving from a loading screen to a homepage). The functions uses the classie library to do some off the cuff swap outs of classes via a trigger (in this case a button/link)
I want to include a small loader that I have but only after the button is click (triggerLoading) so this loader would need to appear below loadingSVG.show();
The problem is I'm not quite sure how to include this laoder. Here is the code.
My current page swap script using Classie
(function() {
var pageLoaderWrap = document.getElementById( 'pagewrap' ),
pagesLoader = [].slice.call( pageLoaderWrap.querySelectorAll( 'div.client-homepage' ) ),
currentPageLoader = 0,
body = document.body,
triggerLoading = [].slice.call( pageLoaderWrap.querySelectorAll( 'a.pageload-link' ) ),
loaderSVG = new SVGLoader( document.getElementById( 'loader' ), {
speedIn : 300,
easingIn : mina.easeinout
} );
function pageSwap() {
triggerLoading.forEach( function( trigger ) {
trigger.addEventListener( 'click', function( ev ) {
ev.preventDefault();
loaderSVG.show();
setTimeout( function() {
loaderSVG.hide();
classie.removeClass( pagesLoader[ currentPageLoader ], 'show-page' );
classie.addClass (pagesLoader[currentPageLoader], 'page-hide');
classie.removeClass(body, 'scrolling');
currentPageLoader = currentPageLoader ? 0 : 1;
classie.addClass( pagesLoader[ currentPageLoader ], 'show-page' );
}, 5000 );
});
});
}
pageSwap();
})();
Here is the small loader I want to include
$(function () {
$('.waveLoader').loader();
var progr = 1;
setInterval(function () {
$('.waveLoader').loader('setProgress', ++progr);
}, 100);
});
The reason I want to include this only on the trigger statement is because if you don't the animation of the loader starts when the page loads instead of when the link is clicked on.
Cheers
I have this ReactJS code to show a custom image button that toggles between 2 different images for ON and OFF state. Is there a simpler way to do this? I was hoping CSS might be less lines of code, but wasn't able to find a simple example.
The code below passes state up from <MyIconButton> to <MyPartyCatButton> then to <MyHomeView>. My app will have 4 of these custom buttons on the home screen, which is why I factored out <MyIconButton>.
btw - this is for a mobile App and I read (and noticed this myself) it's really slow using checkboxes on mobile browsers; that's why I chose to try this without using checkboxes.
ReactJS code
var MyIconButton = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
console.log("INSIDE: MyIconButton handleSubmit");
// Change button's state ON/OFF,
// then sends state up the food chain via
// this.props.updateFilter( b_buttonOn ).
var b_buttonOn = false;
if (this.props.pressed === true) {
b_buttonOn = false;
}
else {
b_buttonOn = true;
}
// updateFilter is a 'pointer' to a method in the calling React component.
this.props.updateFilter( b_buttonOn );
},
render: function() {
// Show On or Off image.
// ** I could use ? : inside the JSX/HTML but prefer long form to make it explicitly obvious.
var buttonImg = "";
if (this.props.pressed === true) {
buttonImg = this.props.onpic;
}
else {
buttonImg = this.props.offpic;
}
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type="image" src={buttonImg}></input>
</form>
</div>
);
}
});
// <MyPartyCatButton> Doesn't have it's own state,
// passes state of <MyIconButton>
// straight through to <MyHomeView>.
var MyPartyCatButton = React.createClass({
render: function() {
return (
<MyIconButton pressed={this.props.pressed} updateFilter={this.props.updateFilter} onpic="static/images/icon1.jpeg" offpic="static/images/off-icon.jpg"/>
);
}
});
//
// Main App view
var MyHomeView = React.createClass({
getInitialState: function() {
// This is where I'll eventually get data from the server.
return {
b_MyPartyCat: true
};
},
updatePartyCategory: function(value) {
// Eventually will write value to the server.
this.setState( {b_MyPartyCat: value} );
console.log("INSIDE: MyHomeView() updatePartyCategory() " + this.state.b_MyPartyCat );
},
render: function() {
return (
<div>
<MyPartyCatButton pressed={this.state.b_MyPartyCat} updateFilter={this.updatePartyCategory}/>
</div>
// Eventually will have 3 other categories i.e. Books, Skateboards, Trees !
);
}
});
if you update the coponent 'pressed' prop dynamically (like you did), simply
var MyIconButton= React.createClass({
render: function(){
var pic= this.props.pressed? this.props.onpic : this.props.offpic
return <img
src={pic}
onClick={this.props.tuggleSelection} //updateFilter is wierd name
/>
}
})
(EDIT: this way, on MyPartyCatButton component, you can pass function to handle 'tuggleSelection' event. event function argument is an event object, but you have the button state allready in the wrapper state (the old one, so you should invert it). your code will be something like that:
render: function(){
return <MyIconButton pressed={this.state.PartyCatPressed} tuggleSelection={this.updatePartyCategory} />
}
updatePartyCategory: function(e){
this.setState(
{PartyCatPressed: !this.state.PartyCatPressed} //this invert PartyCatPressed value
);
console.log("INSIDE: MyHomeView() updatePartyCategory() " + this.state.b_MyPartyCat )
}
)
but if you don't, use prop for defult value:
var MyIconButton= React.createClass({
getInitialState: function(){
return {pressed: this.props.defultPressed}
},
handleClick: function(){
this.setState({pressed: !this.state.pressed})
},
render: function(){
var pic= this.state.pressed? this.props.onpic : this.props.offpic
return <img
src={pic}
onClick={this.handleClick}
/>
}
})
I have a button there, when I click this button, i want render a div and append it to body.
and when I click this button again, a new div be rendered.
I want: How many times I click the button, how many div be render.
The follow code can only render one div: ( jsFiddle: http://jsfiddle.net/pw4yq/ )
var $tool = document.getElementById('tool');
var $main = document.getElementById('main');
var partBox = React.createClass({displayName: 'partBox',
render: function(){
return (
React.DOM.div({className:"box"}, "HELLO! ", this.props.ts)
)
}
});
var createBoxBtn = React.createClass({displayName: 'createBoxBtn',
createBox: function(){
var timeStamp = new Date().getTime();
React.renderComponent(partBox( {ts:timeStamp} ), $main);
},
render: function(){
return (
React.DOM.button( {onClick:this.createBox}, "createBox")
)
}
});
React.renderComponent(createBoxBtn(null ), $tool);
Your app should be data driven, meaning the state of your app is kept outside the DOM. In your example, you are essentially keeping a list of Date objects. Put that into a state that you can modify, and render a box for each Date object you have created:
Working JSFiddle: http://jsfiddle.net/pw4yq/6/
var $main = document.getElementById('main');
var partBox = React.createClass({displayName: 'partBox',
render: function(){
return (
React.DOM.div({className:"box"}, "HELLO! ", this.props.ts)
)
}
});
var createBoxBtn = React.createClass({displayName: 'createBoxBtn',
createBox: function(){
var timeStamp = new Date().getTime();
this.props.onClick({ts: timeStamp});
},
render: function(){
return (
React.DOM.button({onClick: this.createBox}, "createBox")
)
}
});
var app = React.createClass({
displayName: "app",
getInitialState: function() {
return {
partBoxes: []
};
},
createBox: function(partBox) {
this.state.partBoxes.push(partBox);
this.forceUpdate();
},
render: function() {
return (
React.DOM.div(null,
createBoxBtn({onClick: this.createBox}),
this.state.partBoxes.map(function(pb) {
return partBox({key: pb.ts, ts: pb.ts});
})
)
);
}
});
React.renderComponent(app(null), $main);
I've run into a strange problem with my jQuery code. I have a setup as follows:
(function($) {
var o = $({});
$.subscribe = function() { o.on.apply(o, arguments); };
$.publish = function() { o.trigger.apply(o, arguments); };
}(jQuery));
(function($) {
var CarrierView = {
subscriptions: function() {
$.subscribe( 'reporting.carrier.model.changed', this.renderModel );
},
renderModel: function( e, data ) {
var self = CarrierView;
}
};
var CarrierModel = {
subscriptions: function() {
$.subscribe( 'reporting.carrier.model.update', this.updateModel );
},
updateModel: function( value ) {
$.getJSON('carriers', function( data ) {
$.publish( 'reporting.carrier.model.changed', data );
});
}
};
window.CarrierView = CarrierView.init();
window.CarrierModel = CarrierModel.init();
})(jQuery);
Running a very basic pub/sub. My issue is the following:
A click event triggers the CarrierModel.updateModel method, which calls $.getJSON. The data returned is an Array[99], which is then published. When CarrierView.renderModel is called, the data there is the first element of the Array[99], an Array[5]. What am I doing incorrectly? How do I pass the whole set of data to the View?