Backbone / Marionette JS : navigation region, chan - marionette

I'm learning backbone / marionette js and i'm using a boilerplate to do so : https://github.com/BoilerplateMVC/Marionette-Require-Boilerplate-Lite
I have created 2 view (welcome / files) and 2 regions : mains and header.
In my headerRegion there is my navbar and I would like to handle the "active" class of my menu (template: header.html) on change or reload... but I can't figure out what is the best way to do it
I have defined a Region in my App.js :
App.addRegions({
headerRegion:"header",
mainRegion:"#main"
});
In my controller i create a new HeaderView on init:
initialize:function (options) {
App.headerRegion.show(new HeaderView(options));
}
And this is my HeaderView :
define([ 'marionette', 'handlebars', "App", 'text!templates/header.html'],
function (Marionette, Handlebars, App, template) {
//ItemView provides some default rendering logic
return Marionette.ItemView.extend({
template:Handlebars.compile(template),
initialize: function (options) {
_.bindAll();
},
onRender : function(options){
$('ul.nav li', this.$el).removeClass('active');
}
});
});
});
Thanks for your help :) !

What I do in my book on Marionette is to use Backbone.picky to manage which header model is the active one, and add the proper CSS class in that case. You can see the relevant header model selection here: https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/apps/header/list/list_controller.js
And when the user enters the app via a direct URL (e.g. a book mark), I set the proper active header (e.g. https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/apps/contacts/contacts_app.js)

Related

SAPUI5: Extend Control, renderer has html tags with event

I extend a Control to create a new custom control in UI5 and this control renders a tree as UL items nicely. Now I need to implement a collapse/expand within that tree. Hence my renderer writes a tag like
<a class="json-toggle" onclick="_ontoggle"></a>
and within that _ontoggle function I will handle the collapse/expand logic.
No matter where I place the _ontoggle function in the control, I get the error "Uncaught ReferenceError: _ontoggle is not defined"
I am missing something obvious but I can't find what it is.
At the moment I have placed a function inside the
return Control.extend("mycontrol",
{_onToggle: function(event) {},
...
Please note that this event is not one the control should expose as new event. It is purely for the internals of how the control reacts to a click event.
I read things about bind and the such but nothing that made sense for this use case.
Took me a few days to crack that, hence would like to provide you with a few pointers.
There are obviously many ways to do that, but I wanted to make that as standard as possible.
The best suggestion I found was to use the ui5 Dialog control as sample. It consists of internal buttons and hence is similar to my requirement: Render something that does something on click.
https://github.com/SAP/openui5/blob/master/src/sap.ui.commons/src/sap/ui/commons/Dialog.js
In short, the solution is
1) The
<a class="json-toggle" href></a>
should not have an onclick. Neither in the tag nor by adding such via jQuery.
2) The control's javascript code should look like:
sap.ui.define(
[ 'sap/ui/core/Control' ],
function(Control) {
var control = Control.extend(
"com.controlname",
{
metadata : {
...
},
renderer : function(oRm, oControl) {
...
},
init : function() {
var libraryPath = jQuery.sap.getModulePath("mylib");
jQuery.sap.includeStyleSheet(libraryPath + "/MyControl.css");
},
onAfterRendering : function(arguments) {
if (sap.ui.core.Control.prototype.onAfterRendering) {
sap.ui.core.Control.prototype.onAfterRendering.apply(this, arguments);
}
},
});
control.prototype.onclick = function (oEvent) {
var target = oEvent.target;
return false;
};
return control;
});
Nothing in the init(), nothing in the onAfterRendering(), renderer() outputs the html. So far there is nothing special.
The only thing related with the onClick is the control.prototype.onclick. The variable "target" is the html tag that was clicked.

does an onclick function go in model, view or controller?

I am using backbone.js and trying to stay strict to the model-view-controller structure as I learn it. I have an onclick function for a link in one of my views that I am not sure where to put. Is the best place to keep this in the render function of the view?
Thanks
More specifically, the onclick performs a facebook login and then adds the user to my database if they are not currently in it. Don't know if this changes anything.
Here is what I think I will go with:
var NewUserView = Backbone.View.extend({
el: $('#window'),
render: function(){
// Render
this.listeners();
},
listeners: function(){
// onclick and other listeners
}
});
From the Backbone documentation:
In Backbone, the View class can also be thought of as a kind of
controller, dispatching events that originate from the UI, with the
HTML template serving as the true view. We call it a View because it
represents a logical chunk of UI, responsible for the contents of a
single DOM element.
Here's the general way to handle events in Backbone:
var NewUserView = Backbone.View.extend({
el: $('#window'),
render: function() {
// Render
},
events: {
"click #facebookButton": "loginViaFacebook"
},
loginViaFacebook: {
// Perform facebook login and add user to database
}
});
Where do you want the link to appear? On View page right? So , you should keep it in the same view on which you want the link to appear.
But , if you are building an architecture rather than just a web application, then you should put the onclick function in some different file where you will keep all these function and then import them in the view as required or keeping them in separate files and bundling them for import on view page.
Please make a file and write all the functions in that file and include that file in the your view file and use the onClick in the anchor tag. Please let me know if this make sense.

Send parameters from a view to other in Sencha Touch 2 using a controller

I'm just migrating from Sencha Touch 1.x to Sencha Touch 2 and I can't find a way to pass parameters between views.
Lets say I have to views:
Place (a list with all the places)
PeopleAtPlace (A list of the people for each place)
Now what I need to do is to pass the id of the place that is pressed to the peopleatplace view so it can get the people for that specific view.
I've been reading Sencha's documentation but it's pretty confusing to me.
Could somebody please help me? A code snippet would be great help to me.
Controllers can be the glue between different views. I don't know what kind of views you exactly have, but the following code can serve as a base:
Ext.define('MyApp.controller.TestController', {
extend : 'Ext.app.Controller',
config : {
views : [ // you need to list all views your controller will use
'Place',
'PeopleAtPlace'
],
refs : {
place : 'place', // ComponentQuery used to find the view e.g. xtype, id, etc of the view
peopleAtPlace : 'peopleAtPlace'
},
control : {
place : {
select : 'onPlaceSelected' // use the appropriate event
}
}
},
onPlaceSelected : function (view, record) {
var peopleAtPlaceView = this.getPeopleAtPlace(); // generated by Sencha from the ref property
// now you have the reference to the target view, you can put your logic here
peopleAtPlaceView.doSomething(record);
}
});

ExtJS 4.1 Call One Controller From Another

Note: I'm a total ignoramus regarding javascript.
I've broken my ExtJS 4.1 MVC app out into several controllers like:
/app/controller/Auth
| |Quiz
| |Result
| |Blah...
|model/...
I want to respond to an "event", not a DOM Event, rather a Ext.form.action.Submit.success event by calling functions in both my Auth and Quiz controllers. The summarized code for the first part is here:
// File: app/controller/Auth.js
attemptLogin : function() {
var form = Ext.ComponentQuery.query('#loginpanel')[0].form;
if (form.isValid()) {
form.submit({
success : function(form, action) {
// THIS IS THE FUNCTION FROM THE CURRENT CONTROLLER
Assessor.controller.Auth.prototype.finishLogin();
// THIS IS THE FUNCTION FROM THE OTHER CONTROLLER
Assessor.controller.Quiz.prototype.setupAssessment();
},
This works but feels wrong. Is there a proper way to do this? It seems like I should fire a unique event that is listened to by both controllers, but I can't understand how to do that with Ext.Event. Any guidance?
Thanks! I'm really grateful for all the great ideas and advice.
It makes sense to me to fire a custom event from the form and simply listen to it in both your controllers, like what you said here:
It seems like I should fire a unique event that is listened to by both
controllers
// File: app/controller/Auth.js
attemptLogin : function() {
var form = Ext.ComponentQuery.down('#loginpanel').form;
if (form.isValid()) {
form.submit({
success : function(form, action) {
// fire the event from the form panel
form.owner.fireEvent('loginsuccess', form.owner);
},
Then in each of your controllers you can listen to it with Controller#control, like this:
Ext.define('YourApp.controller.Auth', {
extend: 'Ext.app.Controller',
init: function() {
var me = this;
me.control({
'#loginpanel': {
loginsuccess: me.someHandler
}
});
},
someHandler: function(form) {
//whatever needs to be done
console.log(form);
}
}
And then add the same thing to your Quiz controller:
Ext.define('YourApp.controller.Quiz', {
extend: 'Ext.app.Controller',
init: function() {
var me = this;
me.control({
'#loginpanel': {
loginsuccess: me.someOtherHandler
}
});
},
someOtherHandler: function(form) {
//whatever needs to be done
console.log(form);
}
}
I've used this approach successfully in 4.1.0 and 4.1.1
It really should be
Assessor.controller.Auth.prototype.finishLogin.apply(this, arguments)
or something along these lines (in order to have a correct this reference that points to the 'owner' of the method, the controller object)
However, why do you use this unorthodox way to call the current controller's method. Just set the scope for the success callback, then call this.finishLogin().
form.submit({
success : function(form, action) {
// THIS IS THE FUNCTION FROM THE CURRENT CONTROLLER
this.finishLogin();
...
},
scope: this
});
Also, you can retrieve another controller instance using Controller#getController.
this.getController('Assessor.controller.quiz').setupAssignment();
Then, if your controller methods are not depending on each other, you could make them both listen to the same event.
Another solution is to fire a custom event once the login is finished. You could do that on the application object
this.application.fireEvent('logincomplete');
and in your controller's init method:
this.application.mon('logincomplete', this.setupAssignment, this);
Please note that you cannot listen to those events via Controller#control - see Alexander Tokarev's blog post for a patch to Ext to achieve this.
There is no standard way to fire events between controllers, but it's possible with some custom hacks. See my recent blog post.
I have also been looking for this and all you need is Asanda.app.getController('quiz').setupAssignment();, where Asanda is the name of your app
You should use a MessageBus if you have to send events between controllers:
Ext.define('MyApp.utils.MessageBus', {
extend : 'Ext.util.Observable'
});
store the message bus in a global var
MsgBus = Ext.create('MyApp.utils.MessageBus');
Where you have to send events:
MsgBus.fireEvent('eventName',eventArg_1,eventArg_2);
Where you have to receive events:
MsgBus.on('eventName', functionHandler,scope); //scope is not mandatory
...
functionHandler:function(eventArg_1,eventArg_2){
...
//do whatever you want
...
}

Backbone: How to dispose a view/model when navigating to a route?

I am creating a webapp where user creates an inventory of items and he uses folders to categorize them. Now suppose I have two folders like this:
All items (route: /)
Closet items (route: #get/closet/id)
When I navigate between these routes, I want to dispose of the models rendered in the previous route. How and where should I do that? Is there an event which is triggered when I navigate to a new route where may be I can perform this operation?
There are lots of ways to approach this, and it really depends on how you're doing the navigation. If you're changing routes with actual links, or by using router.navigate(), your router will dispatch a route:<route name> event that you can listen to, passing the same arguments to the handler as it passes to the route function.
In what turned out to be a demonstration of just how long it takes to set up test case code with Backbone, I made you a jsFiddle to illustrate this approach: http://jsfiddle.net/nrabinowitz/ZrgJF/7/
A lot of this is just setup code; the important parts for this question are the router:
var MyRouter = Backbone.Router.extend({
routes: {
'view/:id' : 'openView'
},
openView: function(id) {
app.openView(id)
}
});
router = new MyRouter();
And the view, which binds removal to the route:
var MyView = Backbone.View.extend({
initialize: function(opts) {
this.id = opts.id;
router.bind('route:openView', this.dispose, this);
},
// id is the same as the route argument
dispose: function(id) {
if (id != this.id) {
this.remove();
}
}
// etc
});

Resources