I am in doubt with the usage of MVC in context to the example as follows :
Consider an example of a little drawing application.
Now say for example, there is a textbox where user can enter ANGLE for selected shape, Expected result should be, that each selected shape should get rotated as per the angle specified in textbox.
Say I have a shape object that is a View named as ShapeView
The given view has its own data like its position, current angle of rotation , stroke color, fill color, etc...
So I have a model for the shape as ShapeModel
So now I have Controller, that handles this textbox and the multiple shape views. A change in textbox value lets controller take necessary steps to rotate the shape.
QUESTION:
So the doubt is , should the controller directly access the shapeview's shapeModel and call the rotate method?
OR
should the Controller call shapeView's rotate method, that internally calls shapeModel's rotate method?
In short, should any outside entity access a view's model directly? Or should it go through View only? Is it a good idea to access model directly? Any issues or concerns if I access so.?
The view in Backbone is behaving as a controller.
E.g.
ShapeView = Backbone.View.extend
el: "input#angle"
events:
"onkeypress" : "update_angle"
update_angle: (ev)->
angle = $(ev.target).val()
#rotate(angle)
Most of time you don't need additional controller for the same view. The ShapeView controller can handle everything on its own. It's in charge of the views it's created for. If you need access other view controller or model, you can hook them on a global namespace, e.g. App = App || {}
If you have many values and those values needs to be accessed from other controller, you can create a Shape model, that model should not be kept within the View Controller, instead you should bind it to global namespace and set its attributes through global namespace.
You also can bind event on the model like this
shapeView = new ShapeView
shape = new Shape
shapeView.listenTo shape, "change:angle", shapeView.update_angle
This way, you can have multiple controller, listening to the same data model and get views updated accordingly.
Model and View controllers should be decoupled as much as you can.
Related
I would like to know if it is possible to completely change the model, not only it's values but it's actual reference on a View that´s already been initialized. I have seen this question but it only refers to model values.
I have created a reusable view (Which renders a Google Map) and it is used by other different views, not by creating the reusable module as a child view but navigating to a URL which shows a full screen map, which is an instance of my view.
This map View, receives a model on initialization which is later modified within this view, and since it is the same model reference it also updates the view's model that invoked (requested the navigation to) the map, I am invoking all views from my router and it is aware of which views are created and holds references to all of them so i can share models between view this way.
var myRouter= Backbone.Router.extend({
routes : {"viewA(/)" : "invokeViewA"
"viewA/map" : "invokeViewAMap"
//... same with ViewB
},
//...
invokeViewAMap : {
if (mapViewInstance){
//if the map already exists i want to update its model with the viewAinstance.model
} else{
//there is no map view yet so let's create it and send the model i need updated later on
mapViewInstance = new AddressFinderMapView({model : viewAInstance.model});
}
},
invokeViewBMap {
//similar to the above but using viewBInstance's model
}
});
var AddressFinderMapView = Backbone.View.extend({
//initialize function
//render function
//init google map
events : {"click button" : "updateModelWithAddress"},
updateModelWithAddress : function(){
//Here is where i need to update the model values, of the model that this view has received, so far it works with the model that was sent on initialization
this.model.set({
//latitude and longitude from the map
})
}
});
Additional thoughts:
I can stop reusing this Map View instance and create more instances, but that would defeat the purpose of calling Google Maps just once, at the end of the day, the map is used to select a location and return it to the view that invoked it.
I used to have an event being triggered from the Map View, so the other views would listen and update their own models, but since different views can live at the same time, it would update all models that were listening which is not what i wanted
I could send the current route on the trigger along with the latitud and longitude, and let each view filter whether it's their model that must be updated, but this feels more like a hack rather than an structured solution.
Just assign the new modal to the view instance's model property, like:
viewInstance.model = newModelInstance;
I am trying my hands on the new ExtJs 5.
I have created a small app as per the defined MVC pattern of ExtJs5.
Am using ViewControllers for each View.
Problem Statement: Now suppose I have two VCs (Controller1 & Controller2). Each has its own methods. I wish to call a method of Controller2 from Controller1. I want to update the View associated with the Controller2 from Controller1.
E.g. Suppose there is a separate view for Status Bar and a ViewController(StatusBarController).
This VC has a method to update the view based on whatever message it receives as input parameter.
All the other controllers in the application will call this VCs method to update the status of the application on the status bar.
In the previous versions, this.getController('StatusBarController') was used to get the handle to any controller and then call its method.
But this is not working in my case when I use a ViewController.
Can anyone guide me how to achieve this thing? And also whether it is the correct/ideal way to do such a thing or is there any better option?
Here is my code:
StatusBarView:
Ext.define('MyApp.view.statusbar.StatusBarView', {
extend : 'Ext.panel.Panel',
controller: 'StatusBarController',
region : 'south',
xtype : 'status-bar-panel',
html : 'This is a status bar'
});
StatusBarController:
Ext.define('MyApp.controller.StatusBarController', {
extend : 'Ext.app.ViewController',
alias: 'controller.StatusBarController',
updateStatusBar : function(message) {
this.getStatusBarView().update(message);
}
});
Some Other Controller in app:
Ext.define('MyApp.controller.ResourcesPanelController', {
extend : 'Ext.app.ViewController',
alias : 'controller.ResourcesController',
onItemClick : function(tree, record, item, index, e, eOpts) {
// here I am calling the other controller's method.
this.getController('StatusBarController').updateStatusBar(
record.data.text + ' has been clicked');
}
});
ViewControllers are tightly related to their views, they are even created and destroyed together with views, and they should be controlling only their own views. The idea is to separate logic from UI on the view level.
Calling methods of one ViewController from another is not a good practice and, for big applications, it is route to hell as it inevitably leads to unmaintainable spaghetti code.
The correct approach is minimize the number of ViewModels, ViewControllers and Controllers and let them work in their own areas of responsibilities.
For example: Suppose you want a grid and form in a container. Form would allow editing of the record selected in the grid. Plus some buttons. These three views (container, grid and form) together form a unit. Thus:
only one ViewController at container is needed, all views can use it
only one ViewModel at container is needed, all view can use it
if you want to let this trio to communicate with the outer world of the rest of the application, the container's view controller can fire events and can have API methods to call
Thus, if needed, you can have an MVC (global) Controller(s) that would coordinate functions of units, like our trio.
Also, data binding simplifies the logic to a great degree so controllers and listeners are not needed that much.
See Binding Grid and Form in ExtJS 5 example.
my answer is simple and short:
Ext.app.ViewController.fireEvent()
while one can add any type of custom event with the listeners config of the ViewController - the docs of the listen config state "event domains", so I'd assume, that both controller need to reside within the same domain in order to be able to interact, event-wise.
the 2nd argument of .fireEvent() might need to imitate the element which ordinary triggers the event.
well, it should also be possible to access it like that (in the secondary controller):
this.getApplication().getStatusBarController().updateStatusBar('...');
I am trying to retrieve the underlying model object from a controller so that it can be persisted (I am not using ember-data). The obvious way would simply be:
controller.get('content');
But this doesn't work. The problem can be summed up as follows:
controller.set("content", model);
sets the content as expected and at this point
controller.get('content');
works as expected. But if I then decorate the controller with other properties eg.
controller.set('IamNotPartOfTheModel', false);
then suddenly the 'content' includes this new property. I would've expected the content to remain unchanged and the new property to only be applied to the controller itself. I understand the controller is a proxy for the model so for the most part they are treated as one and the same but surely they should still be separable when needed? The whole point of this pattern is to separate data that should be stored from data that is just temporary. Am I missing something?
To have your display specific properties out of the model, just specify them explicitly in the controller... Otherwise the controller acts as a proxy for its model... just have the property "iamNotPartOfTheModel" in your controller
App.IndexController = Ember.ObjectController.extend({
iamNotPartOfTheModel: null
})
Sample fiddle here
Your controller needs to interface to some kind of model. You can't separate the persisted model from the controller except by some kind of object reference. Even if you don't use ember-data, you'll still need to create objects which then plug into the content of the controller. Have a look at Evil Trout's blog for an implementation of ember without ember-data. Its a great starting point.
I'd like to manage all hash state attributes (#) in one Backbone model.
StateModel //Pseudo
attributes
layout : string
modelType1 : model
modelType2 : model
This way I could consistently update history entries just by serializing this single model.
HistoryController
StateModel.bind("change", this.updateHistory);
[...]
state = StateModel.toJSON()
[...]
appRouter.navigate('v1' + state, false);
How do I make the HistoryController trigger change when the nested models (in the StateController) change?
And if the hash changes - and I'd like to update my StateModel - how do these changes propagate down to the nested models? (without causing a feedback loop)
Nested models in Backbone can be tricky because the getter and setter methods do not have built-in functionality to operate at depth. However, I have found that the best way to handle this is to store Backbone models inside other Backbone models. In your example, you would instantiate StateModel and then set its modelType1 and 2 to be, say, TypeModel instances. You can then stateModel.get("modelType1").bind("change",this.updateHistory) and stateModel.get("modelType2").bind("change",this.updateHistory). Alternatively, if you are going to be creating a lot of TypeModels, you can put this binding in the initializer function.
Secondly, you can stateModel.get("modelType1").bind("change",stateModelInstance.updateFoo) or whatever method you would like to call when the modelType model changes.
The nice thing about this pattern is that if you need stateModel to change one of the modelType models you can do stateModel.set({modelType1:newModel3}) or something of that ilk. If you have set the binding action in the ininitializer of TypeModel, everything will stay synced up. If you don't want to blow out the nested model on a change, just do stateModel.get("nestedModel1").set({"foo"}:"bar"). This shouldn't cause a feedback loop unless you have bound something to your stateModel change action that changes the nested Model again but I don't know why you would do that.
I'm trying to figure out if Backbone.js is the right framework for my current project: a visualization app.
I have a number of questions:
1) State / Routing?
As this is not your typical RESTful app, but rather a visualization application with various chart types and settings for these charts, how do i maintain state in the URL?
Let's say my areaChart model has a number of defaults like this:
AreaChartModel = Backbone.Model.extend({
defaults: {
selectedCountries: [],
year: 1970,
stacked: false
},
initialize: function(){
[...]
}
});
On an update to the model I'd like to serialize some of these attributes so that I can bookmark the specific state: chartApp.html#!year=1970&stacked=false etc.
And vice-versa, when initing the app with this state, how do I "deparam" the url state and set the model? Can I use Backbone's intrinsic routing?
2) Controller and coupling?
It seems as Backbone has a pretty tight view-model coupling?
Is this really how I should bind for example my areaChartView to the model?
AreaChartView = Backbone.View.extend({
initialize: function(){
areaChartModel.bind("change:year", this.render);
}
});
Isn't this normally the role of the controller?
3) Continuation: Model vs. Controller?
Given this scenario:
A change in the "Sidebar" should trigger a sequence of functions:
1) "New data for the current selection should be loaded"
2) "Based on this data, the scales in the Visualization view should be updated"
3) "The visualization view should be rendered"
Where should I place these functions and how can I create an event in the model that I trigger when the state is stable? (i.e. when all the functions have been invoked and it's time to set the view states?)
1) I would use Backbone.js native routing as much as possible using “:params” and “*splats” , read more. You could fit all your queries into the Backbone.js routing but I would personally sacrifice certain things in favor of intuitive UI buttons
e.g. I would have the default as a line bar and you can't preset this with the URL but to change to a stacked graph would be a simple click of a button.
I would probably stray from ever using ? and & in my URL's. I might come back to this point later as it is interesting.
2) Your example is fine and you just need to remember Backbone.js MVC terminology doesn't correlate to traditional MVC.
Backbone Views are essentially the Controller in traditional MVC.
Backbone Controllers are simply a way of routing inside a framework.
The templating engine you use with Backbone.js is the traditional MVC view.
3) Still writing
Regarding question #3, I would create a Model and a View for the slider.
Then I would associate the triggering of the change event on the model to some function in the view that updates the graph's view (like changing the scales). Something like:
var Slider = Backbone.Model.extend({})
var SliderView = Backbone.View.extend({
initialize: function() {
this.model.bind('change', this.render);
}
render: function() {
// load data, change scales, etc.
}
});
var slider = new Slider();
var slider_view = new SliderView({ model: slider });
Maybe a good idea would be to put the bindings in a parent view, that would then dispatch to sub-views, coordinating their work.
Do sit down for a while and consider if maintaining the entire state is at all a good idea ? The key motivations for having url-based state management is being able to support browser based navigation buttons and being able to bookmark a page. In a visualization app, your data would probably change every moment. This is not something you want to persist in your app-url. Do you really want that when a user bookmarks your app and comes back to it three days later - he sees the visualization for three days old data ? For your scenario, assuming I have not misunderstood your requirements, I would recommend to keep the data state in your model itself.
Also regarding synchronization of views with model data, Yes you can code all the binding logic on your own. In that case your View class will take care of setting up the bindings on the first render. And upon subsequent calls to render, which can be invoked in response to any change event in the model, will refresh the DOM/canvas where the visualization is present.
Probably you should be look forward to a plugin for data-synchronization that takes care of much of boilerplate for you. This page lists some of the data-binding extensions available. Orchestrator is another solution that I have been working on, which might be helpful in this regard.