Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
tl;dr
To what extent is the Event Aggregator pattern duplicating what Backbone.Router already does? Wouldn't it be better if Views didn't communicate with each other, even with events?
The trouble
I'm having a conceptual problem with the use of an Event Aggregrator in a Backbone application. My very basic implementation is derived from Derick Bailey's post about it.. I'm using it in Requirejs and I just define a module that extends Backbone.Events and require it in the modules where I need it.
A bit of background: The problem I was trying to solve in the first place was how best to handle transitions between views in my app. Especially automated transitions. It was clear from my reading that two obvious methods were discouraged:
Manually firing a navigation callback on the router with something likerouter.navigate("login", { trigger: true })
Manipulating the browser window.location.replace("/#login") and thus triggering the relevant routing callback.
As far as I can tell, the main objection to both these methods is a loss of the proper separation of concerns. As I can certainly see the value in avoiding Views having to know too much about each other, I looked about for a better way and found the Event Aggregator.
The problem I'm facing with the Event Aggregator, though, is made quite obvious from Derick Bailey's example on his blog post. The views that respond to the events are already instantiated. Any view that is not already instantiated will naturally not respond to any events. So if, for example, I want an event to instantiate a new View as a result of some logic, I either:
Instantiate a new instance inside the current View. In this case I lose most of the advantage of the approach, because now my Views need to know about each other again.
OR
Create some kind of central event-handler that instantiates new Views based on specific events. Which sounds a lot like the original Backbone Router to me. It seems like I could just separate the logic for each Router callback into various Controllers and I'd have the same clarity.
My second problem is perhaps more general. I feel like the Event Aggregator encourages active inter-communication between Views: admittedly in a decoupled way. Ideally though I'd like to keep that to a minimum. Really I want my user to perform fairly self-contained actions within a defined area and then transition to a new area. Am I being naive, in hoping to sustain that?
I feel like I must be missing something obvious, but right now it feels like the Event Aggregator solves the problem of inter-View communication (which I'm not sure I want), but ends up replicating the Router with regard to View management. The problem I have with that is that I'll have two Event systems acting in parallel.
What I'm really looking for is a sensible, reasonably lightweight pattern for handling the instantiation of Views using the Event Aggregator. I know I could look into Marionette, but I'd prefer to understand this on my own terms first, before diving into another Framework.
Oh backbone, confusing as ever. Everyone still has differing opinions on how a good application should work.
So here's my opinion.
For your first problem, have you considered just using an anchor tag in one of your templates?
Login
For your second problem, I'd take a page from other frameworks. Before our company switched away from backbone/marionette we tried to adopt the flux (redux) architecture on top of backbone. It has worked out pretty well for some applications that stayed on backbone.
Adopting that architecture just meant defining some best practices. I think the following rules could work for you:
The event aggregator will be the only thing that can fire route changes directly.
At minimum 2 levels of views (or objects) are needed for any page.
The top most views is called a "smart" or "highly coupled" view (or object). It will allow you to hook into the event aggregator and then pass data or callbacks downward. They can represent layouts or can be mostly devoid of style and just transform event aggregator callbacks to something the generic views can work with.
The 2nd level and below should be designed in a decoupled way. We can call them "generic" views, and they can accept handlers as arguments. This is how they communicates with the outside world.
This means, views have APIs and smart views need to adhere to those APIs.
Quick Example:
function init() {
// this level is highly coupled, and knows about the events
// that will occur
var aggregator = new Backbone.Events();
aggregator.on('gotoLogin', function() {
router.navigate("login", { trigger: true });
})
function gotoLogin() {
aggregator.trigger('gotoLogin');
}
// the Todo class is generic, and defines an API that expects
// a gotoLogin callback
new Todo({
el: '.js-todo',
gotoLogin: gotoLogin,
});
// the Header class is generic, and it also expects
// some way to navigate to the login screen.
new Header({
el: '.js-header',
gotoLogin: gotoLogin,
});
}
If you are building your application as some sort of single page app. This will cause you to nest views, which isn't the end of the world, but it does become tricky with backbone, but is still doable. In fact, you may be able to automate creation of child views (see marionette). I bring this up because it seems vaguely related to the original question around de-coupling. I think the nested view problem in backbone is particularly problematic.
I believe people over-emphasize reusable code and decoupling. Honestly, there needs to be some amount of coupling. Finding an acceptable level is difficult. Defining it when everyone using backbone has differing thoughts on it, is very difficult. My recommendation is that you and your team should figure out a system that works well for you in backbone land and stick to it otherwise chaos will surely ensue.
Related
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 2 years ago.
Improve this question
I have recently started learning React and I quickly encountered the problem of a bloated parent component full of functions and state. At first I looked at Flux and then I looked at Redux but both seemed like really overbearing solutions.
I am wondering why something like this is not done:
//EventPropagator.js
let EventPropagator = {
registerListener(listenerObject){
if (this.listeners == null)
this.listeners = []
this.listeners.push(listenerObject)
},
fireEvent(eventObject){
let listenerList = this.listeners.filter(function(listener){
return listener.eventType === eventObject.eventType
})
listenerList.forEach((listener) => {
listener.callback(eventObject.payload)
})
}
}
export default EventPropagator
To use: components simply registerListener and fireEvent:
//main.js
import EventPropagator from './events/EventPropagator'
EventPropagator.registerListener({
"eventType": "carry_coconut",
"callback": (payload) => {
// actually carry coconut
this.setState({"swallow_type": payload.swallow})
console.log(payload)
}
})
EventPropagator.fireEvent({
"eventType": "carry_coconut",
"payload": { "swallow": "african" }
})
This would allow a lot of decoupling and state could easily be passed around with this sort of event. What are the downsides to this approach?
You should try mobX
mobX is a state management library which took the advantage of decorators and succeeded in removing unwanted code. For example, there is no concept of reducers in mobx.
Hope this helps!
There is Reactn based in hooks.
It is orders of magnitude easier to use, compared to Redux.
Check https://www.npmjs.com/package/reactn and read Stover's blog.
Redux is a continuation of React's declarative programming Style. A simple way of mapping your application logic to components is to use something like Backbone.React.Component but using redux, will let you use all the tooling & middle-ware. Also have indefinite transitions in your apps makes debugging a lot easier. Now, I agree that you need a lot of wiring & boilerplate to get anyway.
If you want to use redux, you could look at something like redux-auto
Redux-auto: Redux made easy (with a plug and play approach)
Removing the boilerplate code in setting up a store & actions. why? This utility to allow you to get up and running with Rudux in a fraction of the time!
You can see now a redux-auto: helloworld project
If you want to take advantage of all the Redux benefits, use Redux.
If you only want to keep a var on sync between React components, use Duix.
I'm the Duix's creator. I've been using it for 1 year, and finally, I released it a few hours ago. Check the doc: https://www.npmjs.com/package/duix
You should try Hookstate. It gives the same simplest API for local, lifted, and global states. First class typescript. DevTools support. Extendable with plugins. Comprehensive documentation. It has got state usage tracking feature and smart re-rendering of only deeply nested affected components. This makes it the fastest state management solution for large frequently changing states. Disclaimer: I am maintainer of the project.
What is needed is an event driven system so a child component can speak to its sibling but this does not mean that data should be passed as the events payload. That would lead to a system of no governance and a nightmare for a project where multiple developers are working on it.
So long as you follow the flux-like architecture pattern (google it) then you actually can do this successfully with just javascript and Javascript objects to hold the data along with the event system.
Data should be treated as being one of three different states. It's either
original data pulled from the server
transformed data (transformed from the original)
Store data. That which acts as the model which the UI is bound to
If you write a javascript library to handle the movement of data between the three states and handle the transforms you can use the event system to to fire a "store changed" event that components can listen to and self-rerender.
https://github.com/deepakpatil84/pure-data
Note: i am the author of this
Using your EventPropagator solves the problem of communication, but does not handle data integrity. For example, if there are more than 2 components listening to same event, and using that event's payload to update their own states. The more components listen to same events, the more complex it is to make sure that all those components have same data in their own states.
Secondly, Flux/Redux provides uni-directional data flow. A simple example could be about 2 components A & B, consuming same data X from external source (API or storage). User can interact with either of them to get latest X data. Now if user asks B to update X, we have 2 solutions with your EventPropagator:
B will update X itself, and fire an event to inform A about the update to let A re-rendered. Same goes for A if user asks A to update X. This is bi-directional data flow.
B will fire an event to A asking to update X, and await A to fire an event back to get updated X. If user asks A to update, A will do it itself and inform B. This is uni-directional data flow.
Once your number of components goes large and communication is not limited between only A & B, you might need to stick with only one of those 2 above solution to avoid your app's logic going wild. Flux/Redux chooses the second, and we are happy with that.
If you really don't think you need another library for data control, you should look at the newest feature of React: Context. It provides most essential features that a data control library can have, and it's React only. Noted that you have to update your project's React version to 16.3 to use this feature.
Another modest multi-store alternative is react-scopes
Here are some features:
Simple decorators syntax,
Allow defining independent store logic directly in Components
Easily connect stores through the Components tree
Allow templating complex / async data process
SSR with async data
etc
I wrote Reducs. It's only 25 lines of code, it doesn't require boilerplate, and... did I mention that it's only 25 lines of code?
I am using it for a big project, and it's holding up very well. There is a live example that uses lit-html and Reducs.
I faced similar issues, with lot of switch, case, etc...
I create for myself something as alternative, you could take a look:
https://www.npmjs.com/package/micro-reducers
Take a look on JetState for state management with RxJS. I wrote this for my Angular projects and later adapted it for using with React's hooks.
PullState I really want this underdog to shine. Tried other similar ones which promised quick and no-boilerplate-ness, none came close to the ease of use of PullState. I just read the first page of the documentation and implemented it in one of my pages and it just worked. It is easy to understand if you already using functional components in react.
http://examples.ractivejs.org/comments
There is a line in the above example:
// fire an event, so we can (for example)
// save the comment to our server
this.fire( 'newComment', comment );
I'm curious if this is a common practice in Ractive? Firing the event rather than shooting of an AJAX request in the component? Or instantiating some model object and calling a #save method on that object to fire off the request?
Is this separation of concerns? Testing? Just simplified example code?
var user = new Comment({ text: "text is here", author: "author name" });
user.save()
The only thing I can think of is that by firing the event off and letting something else handle it would possibly make testing simpler? It helps with separation of concerns, but it also seems to me like it would make it more difficult to track down who is actually handling the actual creation of the data?
In your opinions, who would handle the firing of the event? In the example it looks like you just tack it on to the "root" ractive instance and let it handle it up there? That seems like it would get really full in a real world application?
Also, as a side question to his one, how often do you find yourself using "models" with ractive on a real world application? Coming from the server-side world, I'm pretty used to thinking of things in terms of classes and domain models. However, the only "model" library I've seen to be popular on front-end side of things is Backbone. However, Backbone seems to be a little overkill for what I'm thinking about?
I'm curious if this is a common practice in Ractive? Firing the event rather than shooting of an AJAX request in the component? Or instantiating some model object and calling a #save method on that object to fire off the request?
Let's say your app needs an <input> element to call an endpoint via AJAX when someone types in something. It's not the <input> that calls the AJAX. It's the surrounding code that hooks on to some known event fired by the input that does the AJAX when the event is fired. Ractive components are given the facilities needed to operate in that way, but you're not necessarily required to do so.
how often do you find yourself using "models" with ractive on a real world application?
Ractive doesn't impose a convention. That's why the authors prefer to call it a library than a framework. You can use any programming pattern you think is necessary. I have used Ractive in the same way React components operate (one-way binding), and I know people who use Ractive merely as a templating engine. What you're provided is a set of API to be able to do stuff. It's up to you how you use it.
If you want to know if Ractive's the only one doing this, that's a no. Several other frameworks do components in one form or another: Ember, Angular (directives), React (Flux + stateless components), Riot, Polymer (web components).
I use Backbone.js. I have popup1, which creates popup2. popup2 is component-like and when it closes it triggers the event 'school_address:saved'. I need to send a request to the server on 'school_address:saved' event. I placed the handler which do so in the view of the popup1 (its instance still exists), but I'm not sure that this place is appropriate, because views are responsible for template UI logic, aren't they?.
What do you think is the best place for such code? And what would it be if I used Marionette.js?
We have been using BackboneJS for a few years now and have wondered about similar cases in the past...
Since BackboneJS (unlike other JS frameworks)does not enforce any common way of doing this, I would generally say this really depends on your implementation and application flow.
However, since these are popups/"application modals", I think it would help if you consider the following based on your current need:
If it is ONLY popup1 that can instantiate and display popup2, then have popup1 listenTo the events triggered by popup2.
If you foresee your application flow changes, and possibly having popup2 display elsewhere, or independently, I would suggest you listen to popup2 events from a view at a higher level (possibly one you have a route for). this way, both of your popups will be "independent", and you could recycle their code more easily and support greater flexibility as your application grows.
I use backbone in most of my projects, and I think I know exactly what the M V C means: M for abstract of the data, V for representation and C for handlers.
However during my current project I found that there are a lot of views interactive with each other, while there are little modes(data with the server).
For example, I have there complex view named V1 V2 V3, when user do something in the V1, V2 should respond accordingly, so does the V3 and etc, and at the last step, a requst may be made to request data from the server. And most of the request are used to fetch data rather than modify data.
It does not like the common style: one(or more) view for one model such as the CRUD operation.
Now I have two ideas:
1 Virtual model
Create a backbone model to represent the state of the whole application, bind this model to all the views. Sounds like make the application as a state machine.
While it is not easy to describe the application with different states.
2 Use the event mediator
Use the event mediator to register/un-register different events, then the views can trigger or respond by different events.
While how to define the events to avoid insufficien or excessive, in a word to make the events orthogonal is not easy. Or I have not found any instructions yet.
Is there any other alternative solutions?
I think that it is a quite relevant question actually.
Create a backbone model to represent the state of the whole
application, bind this model to all the views. Sounds like make the
application as a state machine.
This doesn't seem like a very good idea, if the model isn't a consistent representation that corresponds to a specific backend resource.
Ideally, a view is a representation of a single model or collection. When a view would be bound to a model with unrelated properties, this doesn't seem too practical to manage in all circumstances, also due to an unforseeable future.
Use the event mediator to register/un-register different events, then
the views can trigger or respond by different events.
I don't think that it is overall a good idea to make views respond to custom events but this is personal for me. When apps become larger, assigning too much responsibility to complex views can become a mess; therefore I take it as a general rule to limit the task of the view to:
Rendering templates;
Activating plugins (if they're specific to the view);
DOM event binding;
(Model binding);
Methods internal to the view (related to the DOM);
Triggering custom events to notify other listeners when further action is required after interaction with the view;
In any case, in my experience I've found that it is practical to use a custom presenter/controller to instantiate / update views on custom events, and not let the view itself worry about these things at all. It keeps them clean and you always know what you will find there.
The views 1, 2 and 3 as you mention can be re-rendered from the presenters.
The presenter does something like:
"I get some data from a service and give it to some of my views that require
them. If something changes, I'll let them know"
I usually have 1 presenter per related set of views.
I like this approach because:
It keeps logic for related views centralised;
When an event is triggered, the presenter needs to listen only once for all the views that it controls;
Presenters are given the authority to talk to each other, which is to my feeling easier to control than when all views would start talking to each other through each other;
In simple cases, all of this doesn't matter too much probably. But when building a larger application, I found that it can become a mess.
My two cents
I have there complex view named V1 V2 V3, when user do something in the V1, V2 should respond accordingly, so does the V3 and etc
It doesn't seem like you have 3 views, but in fact 1 view with 3 interrelated sections. I would use one super view that renders 3 child views, and listen to view events. For example:
Backbone.View.extend({
initialize: function () {
this.v1 = ...;
this.v2 = ...;
this.v3 = ...;
this.v1.on('user do something', this.v2.respondAccordingly);
this.v1.on('user do something', this.v3.soDoesEtc);
}
})
And in view 1:
$('button').on('click', function () {
self.trigger('user do something');
})
This is a problem that many Backbone developers face.
What I've done in the past is to have a baseModel/baseCollection, and treat them as an abstract class/interface from which other models/collections extend, respectfully. These base objects would contain a listener/trigger methods which I could then use across my application to have changes in one model/collection be able to initiate updates on collections/models (thus triggering view changes) respectively as I chose. Using this method allowed me to compose my application by having the appropriate objects listen/broadcast events appropriately as I wished.
One of my friends created a server-side JS state machine which would initiate super-models (app-level model which in turn could trigger sub-view model/collection updates).
Of course, Marionette provides a framework that makes this a little less manual and allows you to get back to writing app-code.
One of the joys and burdens of Backbone.js is that you have all the flexibility you want. :)
Should I use a router to manage changing of views? Currently, I'm using a 'parent' view to handle 'child' view management.
I have multiple 'child' views within a single parent. Child views/pages are changed by clicking links, which also modifies the URL. The changing of the displayed view is handled by the parent view -- NOT a router. The router in question has nothing of value coded, I created the 'dummy' router so I could use Backbone.history
I suppose you searching for some sort of validation that this is a proper way to construct your application. From the limited information you've given, it's hard to say, and even if you did provide some code, your question might not be definitively answerable, either.
So, let me just put it this way. I don't think there is any flaw in creating Backbone.js views that are composed of other views. I do think there's something wrong if you're putting everything related to view changing in the router--unless you're giving some sort of high level description there--like passing in views the parent view should show/attach events to/something else.
Another point. How are the child views related? Might some of them have a common (high-level) interface? Or does your parent view wrap disparate things, like a search form and some grids and labels? Or both? Is there some chance that this parent view might be useful on another page, or is it just going to manage this particular pages views, etc. All this matters.
I suppose there might even a reason to question as to whether or not your parent view should strictly extend from Backbone.View or should have some other sort of "base" class. But if it works and has some reasonable connection to being a "view" I wouldn't bother asking that question. As Jeff Attwood has said, "meta is murder". At some point these kinds of discussions become impractical, and when there are still important issues to be fixed or implemented, it's best to leave reasonable solutions be.
My thinking is if you are using the router to control and delegate things to the parent view, and let the parent view do it's things with it's child views, you probably fine.
You might want to consider passing in the child views through parent view's constructor in the router, though. It's often better, but not always better, to have a separate thing determine the composition of an object than to let the object create it's composition itself. But always go for transparency as well. Sometimes the freedom you get with dependency injection isn't worth the obtuseness of abstraction your code; especially if you're not quite sure what you're doing. Figure out what you're doing, then consider the abstractions you may not have thought of.
If you want to know if you are doing things consistent with the community's ways of doing certain things, you probably ought to go to the backbone.js Google discussion group and review or ask questions the questions there as well.