backbone can a collection contain model objects of different classes? - backbone.js

I am just beginning to use backbone.js for a new crash project. My app has a dynamic (data-driven) user menu. Each menu option is a set of graphs/small tables, of mixed types. For example, a Sales Overview menu option can have a page with 2 pie chart objects, 2 line charts, a bar chart, and so on. I don't know up front what the menu options are going to be, nor what each menu option will entail.
I am considering defining a bunch of generic model "classes" by extending Backbone.Model - PieModel, BarModel, DispersionModel, etc. And corresponding View classes that can render an object of a type - PieView, LineView, and so on. Then I can assemble a page by putting these together as defined by the dynamic configuration. Each model instance's data url can be easily generated on the fly, via the dynamic configuration..
My first concern was if Backbone supports a Collection of mixed Model types. This is instigated by presence of a "model" property for a Collection - does it assume homogeneity? But it also says a collection can hold an ordered set of models.... model attribute can be polymorphic... a method to get "models" held in the collection. Should I be reading this as "model objects"?
A "page" to me really is a collection of such objects. I would like to create a Collection on the fly and populate it with instances of different model types. And then render this through a View. Or, create a View with an array of various model objects and render the View, bypassing the Collection all together.
I will appreciate your inputs on the design I have outlined, and good reference on backbone, and clarity on how to deploy a Collection in mixed model cases? Perhaps there is a different, smarter way to handle such scenarios...
Thanks.

Collections only really use their model attribute when passing plain objects into its adder functions (e.g. add, push). If you take a look at the source, each adder function passes the input through _prepareModel, which checks if the input is an instance of a Backbone.Model. If it's not, it tries to instantiate a new model using the collection's model, otherwise it just returns the input untouched.
So as long as you're always adding real Model objects to your collections you should be fine using different types.
However, if you're planning to use aggregate functions that act on model attributes (e.g. pluck) you may run into errors when the function tries to get at an attribute that doesn't exist in one type of model (though most of the time I think it would just silently fail, which might be what you want).

I am not sure if I have 100% properly understood your scenario, however, I am not convinced you are thinking about this in the right way...
In my opinion, your models should contain the data, and views should represent them. As such, in a sales context you might have a SalesData model which could be displayed in PieView, BarView or TableView. Try to completely separate display logic from data - the type of chart falls under display logic in my opinion.
With the above approach, each page would then contain a set of different views, which you could potentially contain in a master view if you felt the need. Each view would have its own model (or collection depending on how you structure the data), which you can then update/manipulate using the normal Backbone methods.
As far as I know it is not possible for a collection to have different types of models contained within it, but even if it was, I would probably not recommend it as it would complicate the code a lot.
In terms of learning resources, here are a couple:
Learn Backbone JS compeltely -- javascriptissexy.com - this one is very thorough but will take some time to get through.
Backbone patterns - much quicker to get you in the right frame of mind.

Related

Using of graphql service within desktop application that "follows" MVVM and DDD

We have a WPF desktop application that uses MVVM pattern and DDD (well, let's say that at least my model classes that store data named by entities taken from the real world). APP uses several microservices through REST API. And it worked perfectly. Until we thought that it's time to use some facade for back-end part to unite all those microservices and get only data that we need for particular screen.
BUT. The question is, how to make them live together.
On the one hand, we have dynamically returned data from graphql. It
means that, for example, if we have list of people on the one screen,
we will request id, name, surname and role of the person. On the
different screen for dropdown of people we will request the same data
but without role.
On the other hand we have class Person that has static set of fields Name, Surname, Role and Id, which person has in "real life"
If we use the same Person class with graphql, converting data from JSON to model Person, both screens will work fine, but behind the scene one screen that doesn't need Role wouldn't request it from graphQL. And we will have a situation when model class Person will have field Role but it will be just empty (which is i believe is kind of smells. At least I don't feel like it would be easy to maintain such a code. Developer needs to add some information to the screen, opens model, sees that Role is there, bind the field to the screen and goes to drink cofee. And then oops, there is the fields but there was no data assigned ).
Two variants I have on my mind are:
either to not use models and DDD and map data directly to ViewModel
(which personally feels like ruining everything we had before).
or we map that dynamic data to our existing models and different field for different screens (for the same class Person e.g.) will be
empty (because not requested).
Maybe somebody has already used such a combination. How do you use it and what pros and cons are?
It's a fairly common situation where you have a data layer returns many columns but only some are used in a given view.
There is no absolute "best" solution independent of how much impact the full set of columns will have on performance. Which might in turn be linked to things like caching.
You could write services that return subsets of data and then you only use the necessary bandwidth. Sort of a CQRS pattern but with maybe more models than just read + write.
Often this is unnecessary and the complications introduced do not compensate for the increased cost of maintenance.
What is often done is just to map from model to viewmodel (and back). The viewmodel that needs just 4 columns just has 4 properties and any more returned by the model are not copied. The viewmodel that needs 5 has 5 properties and they are copied from the model.

What's the preferred way to go about using backbone with non-crud resources?

New to backbone/marionette, but I believe that I understand how to use backbone when dealing with CRUD/REST; however, consider something like results from a search query. How should one model this? Of course the results likely relate to some model(s), but they are not meant to be tied to said model(s).
Part of me thinks that I should use a collection using a model that doesn't actually sync with a data store through the server, but instead just exists as a means of a modeling a search result object.
Another solution could be to have a collection with no models and just override parse.
I assume that the former is preferred, but again I have no experience with the framework. If there's an alternative/better solution than those listed above, please advise.
I prefer having one object which is responsible for both request and response parsing. It can parse the response to appropriate models and nothing more. I mean - if some of those parsed models are required somewhere in your page, there is something that keeps reference to this wrapper object and takes models from response it requires via wrapper methods.
Another option is to have Radio (https://github.com/marionettejs/backbone.radio) in this wrapper - you will not have to keep wrapper object in different places but call for data via Radio.

Wijgrid will not play with Angular and Breeze together

Wijgrid will not work with breeze and angular. To save me repeating myself, please have a look at this post:
http://wijmo.com/topic/wijgrid-will-not-play-with-angular-and-breeze-together/
Your model, like most models, has circular references. We are not wijgrid experts (at all!) but we suspect that the wijgrid component - at least as you have it configured - is unprepared for objects with circular references.
Objects with circular references are not bad. In fact they are normal and expected in entity models. A customer has orders and each order has a property that returns its parent customer. If you don't
We suggest you consult the vendor on this topic.
If you are using the grid read-only, you can write a simple function to copy just the data you want to display from the queried entities into special purpose "ItemViewModel" objects; then load those into the grid. It's even easier to query with a Breeze projection because that returns raw data, not entities; just remember that Breeze isn't caching those non-entity results either.
The situation is more complicated if you want to edit the grid.
I want to reiterate that this is not a Breeze problem. It is a problem with the grid control.

Passing data from main application view to some subviews

Please consider following Backbone application structure:
AppView
Subview
FirstSubviewInQuestion
Subview
Subview
SecondSubviewInQuestion
Application view creates and stores a collection of special items. At a certain time the first and the second subview needs to get access to that collection. Now what is the best way to pass the collection it to them?
I myself see some ways, yet each has a downside:
1) Create instance of that collection not inside the App View but even more globally and then pass it around as dependency (project uses RequireJS; so it would be passed to AppView and both Subviews).
Downsides: app view is supposed to be the top most element in application. Yet suddenly we have some instances of collections floating above.
2) Or I can do following:
// some subview
var collection = {};
Backbone.trigger( "item:getSpecialItems", collection);
// now the collection has data
// app view
this.listenTo( "item:getSpecialItems", function(obj) {
// copy collection to passed object
_.extend(obj, this.specialCollection);
});
Downsides: we are triggering a global event. We know that only the app view would respond, but it feels like a bad design. Plus, this way to pass the collection seems like a hack.
Maybe there are some other clever ways to do it?
I would say #1.
app view is supposed to be the top most element in application
Right, but you're talking about (I think) a Collection, not a View; the two are totally separate parts of your application (in MVC the first is the "M" and the second is the "V"). By definition, if your views are driven by your data, the data must be "higher" (in the dependency tree) than any view, so I don't see anything wrong with that approach.
If your goal is to get a shared instance of the collection, my approach would be to pass that down from the parent to "subview" and from subview into its children. However, I wouldn't necessarily use require to pass around a singleton of this collection.
You could also pull out all of the logic from the view regarding the special methods for "creation and storage of special objects" into a helper class whose sole domain is just this. That helper then becomes a utility available from outside the view hierarchy, perhaps even globally or via require.

Django models generic modelling

Say, there is a Page that has many blocks associated with it. And each block needs custom rendering, saving and data.
Simplest it is, from the code point of view, to define different classes (hence, models) for each of these models. Simplified as follows:
class Page(models.Model):
name = models.CharField(max_length=64)
class Block(models.Model):
page = models.ForeignKey(Page)
class Meta():
abstract = True
class BlockType1(Block):
other_data = models.CharField(max_length=32)
def render(self):
"""Some "stuff" here """
pass
class BlockType2(Block):
other_data2 = models.CharField(max_length=32)
def render(self):
"""Some "other stuff" here """
pass
But then,
Even with this code, I can't do a query like page.block_set.all() to obtain all the different blocks, irrespective of the block type.
The reason for the above is that, each model defines a different table; Working around to accomplish it using a linking model and generic foreign keys, can solve the problem, but it still leaves multiple database tables queries per page.
What would be the right way to model it? Can the generic foreign keys (or something else) be used in some way, to store the data preferably in the same database table, yet achieve inheritance paradigms.
Update:
My point was, How can I still get the OOP paradigms to work. Using a same method with so many ifs is not what I wanted to do.
The best solution, seems to me, is to create separate standard python class (Preferably in a different blocks.py), that defines a save which saves the data and its "type" by instantiating the same model. Then create a template tag and a filter that calls the render, save, and other methods based on the model's type.
Don't model the page in the database. Pages are a presentation thing.
First -- and foremost -- get the data right.
"And each block needs custom rendering, saving and data." Break this down: you have unique data. Ignore the "block" and "rendering" from a model perspective. Just define the data without regard to presentation.
Seriously. Just define the data in the model without any consideration of presentation or rending or anything else. Get the data model right.
If you confuse the model and the presentation, you'll never get anything to work well. And if you do get it to work, you'll never be able to extend or reuse it.
Second -- only after the data model is right -- you can turn to presentation.
Your "blocks" may be done simply with HTML <div> tags and a style sheet. Try that first.
After all, the model works and is very simple. This is just HTML and CSS, separate from the model.
Your "blocks" may require custom template tags to create more complex, conditional HTML. Try that second.
Your "blocks" may -- in an extreme case -- be so complex that you have to write a specialized view function to transform several objects into HTML. This is very, very rare. You should not do this until you are sure that you can't do this with template tags.
Edit.
"query different external data sources"
"separate simple classes (not Models) that have a save method, that write to the same database table."
You have three completely different, unrelated, separate things.
Model. The persistent model. With the save() method. These do very, very little.
They have attributes and a few methods. No "query different external data sources". No "rendering in HTML".
External Data Sources. These are ordinary Python classes that acquire data.
These objects (1) get external data and (2) create Model objects. And nothing else. No "persistence". No "rendering in HTML".
Presentation. These are ordinary Django templates that present the Model objects. No external query. No persistence.
I just finished a prototype of system that has this problem in spades: a base Product class and about 200 detail classes that vary wildly. There are many situations where we are doing general queries against Product, but then want to to deal with the subclass-specific details during rendering. E.g. get all Products from Vendor X, but display with slightly different templates for each group from a specific subclass.
I added hidden fields for a GenericForeignKey to the base class and it auto-fills the content_type & object_id of the child class at save() time. When we have a generic Product object we can say obj = prod.detail and then work directly with the subclass object. Took about 20 lines of code and it works great.
The one gotcha we ran into during testing was that manage.py dumpdata followed by manage.py loaddata kept throwing Integrity Errors. Turns out this is a well-known problem and a fix is expected in the 1.2 release. We work around it by using mysql commands to dump/reload the test dataset.

Resources