var BaseView = Backbone.View.extends({
});
var ComponentView = BaseView.extends({
});
var ChildView1 = ComponentView.extends({
});
var ChileView2 = ComponentView.extends({
});
I want to a have cross component communication between ChildView1 and ChileView2.
I would like to have a _.extend({}, Backbone.Events) obj in the parent(ComponentView).
I saw in some of the examples something like below
var ComponentView = BaseView.extends(_.extend({}, Backbone.Events, {
});
PS: i am initializing all the components from another BackboneView using an attribute present on the components
In Backbone, I prefer using some sort of publish/subscribe event pattern to communicate between views. In it's most simplest form, your code will look something like the following:
/* Create an Event Aggregator for our Pub/Sub */
var eventAggregator = _.extend({}, Backbone.Events);
/* Pass that Event Aggregator to our Child Views */
var childView1 = new ChildView1({ "eventAggregator": eventAggregator });
/* From here we can just bind/trigger off of eventAggregator whenever we need */
eventAggregator.bind("tellChild", function(e) { alert(e.message); });
eventAggregator.trigger("tellChild", { "message": "hello" });
Notice how we are creating a new object that extends off of the built in Backbone.Events and passing it into the ChildView1. Inside of the ChildView or anywhere else that has a reference to eventAggregator you can bind/trigger new events. However, this is the tip of the iceberg as you will need to handle no longer needing to know about this event handler, unbinding the event handler and ensuring you're not leaking memory.
There isn't enough space here to go deep into this, so I would recommend reading more about event aggregation in Backbone. All of my logic that I have ever used is derived from the work that Derick Bailey wrote in blog posts and his book "Building Backbone Plugins" (both excellence sources of information). These came ultimately from his work in creating Marionette, which is a nice compliment to Backbone. If you don't want to have to worry about these issues or just want a simpler API, I recommend using Marionette or something equivalent to improve your Backbone Views.
Related
I'd like to set a variable or object in main.js which I can then reference from any backbone view.
I considered using localStorage, but the data is dynamic and a little sensitive so I wouldn't like to have it stored in localStorage as it could be manipulated by the user very easily.
Since you said "main.js" I think you're confused between RequireJS and Backbone.js. RequireJS is not part of Backbone. It is an AMD module loader which happens to be used a lot with backbone projects.
Looks like you need a RequireJS module like:
define(function (require) {
var someData;
var singleton = function () {
return {
getMyData = function(){},
setMyData = function(data){},
};
};
return singleton();
});
P.S: Above code can be made object literal, an instance of proper constructor function, es6 class of whatever. I just posted something as an example.
#TJ already gave what's needed to achieve what I call a Service within my app, borrowed from AngularJS services. I have a couple services, like i18n which is just a i18next instance.
Like you, I wanted to manage certain data relative to the app that could be shared everywhere, without putting it in the window object.
I came up with a AppState service which is just a Backbone model instance.
define(['underscore', 'backbone', 'color'], function(_, Backbone, Color) {
var State = Backbone.Model.extend({
setBrandColor: function(hexColor, options) {
return this.set({ color: new Color(hexColor) }, options);
},
getBrandColor: function() {
return this.get('color');
},
});
return new State(); // return a new instance instead of the constructor.
});
What's cool with a Backbone model is that anything within the app can listen to its events. This is inspired from React.
this.listenTo(AppState, 'change:color', this.onBrandColorChange);
Note that I prefer to use PascalCase for services even though they're instances, they're closely related to a static type in other languages.
This way, when the app state changes, other parts of the app may or may not react accordingly. Without the events, the app would need to be more coupled which is undesirable.
I'm trying to get a practical grasp of MVC model implementation (not the conceptual understanding) in JavaScript.
As for the start, I thought it would be worth making an effort and try building a MVC app in plain JS. I've read dozens of articles and book chapters referring to MVC and its variations. Of course I googled lots of examples to see how it's done for real. The most understandable and with the proper meaning is in my opinion this one:
https://github.com/tastejs/todomvc/tree/master/examples/vanillajs
In the end, I was able to refactor my own app in the todomvc-vanillajs way.
However, there is one thing that still bothers me. All these apps and examples are very basic, so there is only one Model, View and Controller specified for the whole app.
What if I wanted to add more (equally complex) features to such app?
Should I add them one by one to my controller.js view.js and model.js files or whether should I stop developing spaghetti code and add new files instead, thus creating new models, controllers and views for each of the new feature individually?
It seems to me, that every feature should have its own view, controller and model, or at least, could have, depending on the subjective evaluation. But I'm not quite sure how such implementation should look at this situation in terms of code structure, namespacing etc.?
What if I want to imitate a scale by creating multiple views, models and controllers on every single functionality like e.g. handling an "add task to the list" or "delete the task" actions.
For the purpose of my dilemma, I've created my own MVC draft, which has two models, controllers and views. Whether such an approach would make sense? What happens when further developing my application, I quickly get to the point where I have dozens and more specific (coresponding) models, views and controllers.
Heres is the aforementioned fiddle.
;(function () {
'use strict';
/**
* #file ./App.js
*/
var App = {
Model : {},
Controller : {},
View : {}
};
console.log('start');
window.App = App;
})();
/* -------------Views-folder----------------------*/
/* -------------separate-file-----------------------*/
(function () {
'use strict';
/**
* #file Views/buildAdd.js
*/
var buildAdd = {
// render
// event
// pass the reference to event handler in Controller
};
App.View.buildAdd = buildAdd;
})(App);
/* -------------separate-file-----------------------*/
(function () {
'use strict';
/**
* #file Views/buildDelete.js
*/
var buildDelete = {
// render
// event
// pass the reference to event handler in Controller
};
App.View.buildDelete = buildDelete;
})(App);
/* -------------Controllers-folder----------------------*/
/* -------------separate-file-----------------------*/
(function () {
'use strict';
var addController = {
// handle the event and decide what the Model has to do
// handle the response from Model and tells the View how to update
};
App.Controller.addController = addController;
})(App);
/* -------------separate-file-----------------------*/
(function () {
'use strict';
var deleteController = {
// handle the event and decide what the Model has to do
// handle the response from Model and tells the View how to update
};
App.Controller.deleteController = deleteController;
})(App);
/* -------------Models-folder----------------------*/
/* -------------separate-file-----------------------*/
(function () {
'use strict';
var addModel = {
// send request
// get response
};
App.Model.addModel = addModel;
})(App);
/* -------------separate-file-----------------------*/
(function () {
'use strict';
var deleteModel = {
// send request
// get response
};
App.Model.deleteModel = deleteModel;
})(App);
/* -------------separate-file-----------------------*/
Thus, I found this question very similar to mine, but the provided answers are not entirely satisfactory, at least to me.
Check my implementation of so called Single Page Application framework. The whole thing is of 60 lines of code. It uses jQuery but can be implemented in VanilaJS.
Basic idea is simple - your app is just a collection of pages a.k.a. views
<section id="route1" src="content1.htm" />
<section id="route2" src="content2.htm" />
...
Sections id's define set of possible "routes"
SpAPP catches browser's navigate event and load requested view on the route.
And partial content1..N.htm files contain view markup, setup and controller functions.
Data model here is JS data received from server and stored in memory or in local storage.
As of MVC frameworks in general... You cannot bring joy to everyone and free of charge. That small SpAPP thing that can easily be understood and adjusted to particular project's needs is a way to go I think.
Looking at my experience in Ruby on Rails framework, I don't always need all three elements of MVC pattern. Sometimes you need a model for a database but it's only accessed internally, not by the client. Or sometimes you only need a generic helper class.
As a convention, the files are split, each one has its own controller, model and view, following a naming convention, maybe something like:
articles-view.html
articles-controller.js
articles-model.js
Views are split for each action in the controller:
articles-index.html
articles-show.html
articles-update.html
...
articles-controller.js
articles-model.js
Inside the controller, you will have the "actions", the functions for everything semantically related to an Article in a blog.
function ArticlesController() {
function index() { ... }
function create() { ... }
function edit() { ... }
...
function delete() { ... }
}
In models, you basically have the class / prototype itself, something that is built with the given data.
function Article() {
this.name = "";
this.author = "";
this.text = "";
this.dateCreated = "";
}
And finally, your views should have element with the same name used in the model.
If you have a basic CRUD system, for example, you can have just one controller and one model, but different views (one for listing all items, one for creating and editing, one for just one item, etc).
Taking examples from Rails and NodeJS, a way to write less code for the views is by using "partials". Common HTML structures can be saved on a file and imported into other HTML files as needed, such a form, the headers, the footer of a page and so on.
Example:
Instead of having a form on articles-create.html and another on articles-edit.html, you will have something like:
_articles-form.html <- this is your partial!
articles-create.html
articles-edit.html
"_articles-form.html" will be imported / appended into the create and edit pages.
Other common features can be consider as "Helpers". They are not a letter in "MVC", but often used. Like the Datepicker library, a simple validation function, a parser, etc. Something that can be used by everyone, not a specific feature of a class.
The project structure could be something like:
app/
app/controllers/
app/controllers/articles-controller.js
app/models/
app/models/articles-model.js
app/views/
app/views/articles/
app/views/articles/index.html
app/views/articles/create.html
app/views/articles/edit.html
app/views/articles/delete.html
app/views/articles/_form.html
Also, having a Manager functionality as you described above, will help you load all the data needed. Some function that maybe will read a json file, looking for the feature's name and parsing through the file's names, loading everything.
The manager would check if there is a model file, a controller file and a folder with N view files in it, containg the word "articles". The same would happen to "authors", "comments", "users" and so on.
I understand that you are proposing this question for study reasons and you took JS as a personal preference, so I´m not saying "don't try it" or something like that. But something to consider: the MVC pattern tackles applications that involves both client and server side. Unless your are developing on a full stack with NodeJS and MongoDB (or other similar technologies), HTML and Javascript are more on the View side of the application (or as helpers).
And if you are developing something like a library, you'll end up putting everything on a single file and minifying/uglifying it. Take JQuery as an example. Javascript developers often go with the Module pattern. They create an object, expose methods and variables that the other developer needs to know and that's it.
So, probably (but not for sure, you never know!), you won't see or work on many vanilla Javascript applications implementing MVC pattern.
Is there a handy way to throw/catch (custom) events from/to multiple levels of child/parent views in Backbone?
Let me explain my situation. I'm catching "keydown" events and check if some interactions should been done on the parent level. If not, then I'm calling the childView function for that. My BaseView looks something like this:
var BaseView = Backbone.View.extend({
keydown: function(e){
return this;
},
});
It works fine until I'm not trying to throw other customEvents to interact with childViews of childViews. Which by the way, don't know about the existence of themselves.
I can't just simple do something like the stuff below, cause my parent don't even know all childViews of the subView. I'm trying to do something like this:
eg. Blur the subChildViews input
Parent.subview.subsubview.trigger('blurInput');
I'm really sure I'm on the wrong way with my event-pushing-"keydown"-method,
could someone point me the right direction?
EDIT:
The raw BackboneJS isn't really build for something like that, but there is a Module out there. MarionetteJS was my solution, it provides everything I was looking for. Subviews, modular logic and an optimized cross view event system.
There is an awesome getting started tutorial on smashingmagazine.com
Well there's no handy solution in backbone form scratch, cause nesting views is not handeled there is any way.
What i can advise you is to set the parental relation(so each view knows it's parent) rather then parent knows all of it's subviews, and as long as view is an event emitter by default toy can do something like this [pseudo-code]:
parent view:
var BaseView = Backbone.View.extend({
keydown: function(e){
this.trigger("custom.keydown");
return this;
},
});
child view:
var ChildView = Backbone.View.extend({
initialize:function(){
this.parent.on("custom.keydown",this.keydown,this);
}
});
I work on a Backbone app, which utilizes a lot of view. I fell into the (usual I think) trap of instantiating a Router (sort of, the main controller) which is responsible for clearing out the views, instantiating new ones, and filling them with data. I say trap, because although JavaScript has a built-in garbage collector, one will quickly start noticing how the usability of the app gets hampered by the many unused views which still reside somewhere in memory.
I wish there were a way to recycle those views. I thought that this would be as easy as calling initialize() on the view with a new model, and then rerender. This is unfortunately not as easy.
Also, one would have to kinda "destroy" the view handles, for example, event handlers and stuff...
What would be a good practice to do this?
Once a view has been removed from the DOM it will be garbage collected. Unless of course you cache it. If you do cache a view and remove it from the DOM, all event handlers are garbage collected as well. Unless you use something like jQuery's detach method, which will preserve the event handlers.
http://api.jquery.com/detach/
If you want to recycle a view, simply cache it in a variable.
Inside your router's init method, do something like this:
this.views = {};
Then whenever a route is called check if the name of the view is available in the cache and if it is, use that, otherwise create a new one.
someRoute: function () {
var view;
if ( _.has(this.views, 'someRouteView') ) {
view = this.views.someRouteView;
} else {
view = new SomeRouteView;
this.views.someRouteView = view;
}
// You have a view now
}
Again, if you don't create a new view, you will have to use something like jQuery's detach method to preserve event handling.
I am using Base2 as a means to allow us to easily do inheritance in our system, aswell as using KnockoutJS for some UI interactions.
We have defined a base class for our ViewModels
BaseViewModel = Base.extend({
...
});
Which we then extend for our view models:
ExampleViewModel = BaseViewModel.extend({
text: ko.observable("")
});
However there seems to be a problem. When you create 2+ instances of the view model (say if you are pushing them in to an observableArray and using templates to build up a UI) it seems like any changes made to a bound field, updates all view models rather than just the one it's bound to.
Does anybody know why this might be?
Because the extension is not actually instantiating a new observable, its just copying the reference.
I think you can do something like this:
ExampleViewModel = BaseViewModel.extend({
constructor: function() {
this.text = ko.observable("");
}
});
Not as nice though as normal Base2 syntax, but just a limitation in how Knockout is implemented due to issues with properties.