I have an array of items populated by an AJAX call in a knockout ViewModel, which displays a few fields of data for each item on a web page.
Now I need to enable the user to click on one item populating a side bar with data which was received from the previous AJAX request (a few fields plus a lot more).
I suppose typically one would take an id and do an item specific AJAX request, routing it through Sammy.js, but we don't need to.
I'm new to knockout; best policy I imagine is to have a ViewModel for the various divs to display the data, but how to get the ViewModels to pass data between themselves? Is this taboo?
Making reference to the other window via the window object?
Using the with: keyword? It keeps cropping up, but I can't see how to apply that in this context.
Perhaps going via Sammy.js, and caching the data in Amplify?
This is an example of drill-down functionality, and I've read a number of StackOverflow Q&A about this but couldn't find up something I can use. I've got up to this stage by following John Papa's PluralSight tutorial.
You may want to go with a pub/sub model either with Amplify's messaging or the library the #RPNiemeyer mentions above. Both are excellent.
However it sounds like you just want to grab the data from the server, then use that data in multiple view models. Perhaps even sharing some of that data in multiple view models. The datacontext concept in my SPA tutorial allows you to host data in the datacontext and reference it from other view models.
You could also use a library like Breeze to help do this (Breeze would replace the datacontext in my SPA ... and be better at it as I will show in an upcoming course).
These are just a few options
You may also want to checkout the "Share an EntityManager" post under "Cool Breezes" in the breeze documentation.
The sharing a single EntityManager is probably all you need. But if you think you need more than one, read "Multiple managers".
IMO The easiest way to do it is to simply pass "params" with an observable containing data to your sidebar component.
http://knockoutjs.com/documentation/component-custom-elements.html#passing-observable-expressions
As was mentioned in comments, the good choice would be to use knockout-postbox
knockout-postbox is a Knockout.js plugin designed to use Knockout's basic pub/sub capabilities to facilitate decoupled communication between separate view models / components.
This allows you to set up simple topic-based communication like:
var ViewModelOne = function() {
this.isEditable = ko.observable();
};
var ViewModelTwo = function() {
this.editable = ko.observable(false);
};
var one = new ViewModelOne();
var two = new ViewModelTwo();
var editableTopic = "myEditableTopic";
one.isEditable.subscribeTo(editableTopic);
two.editable.publishOn(editableTopic)
Related
I am fairly new to Angular and trying to build an Angular application.
I have a lot of data that needs to be used by multiple controllers throughout the app. As I understand it, that is the perfect situation to use a service.
I am planning on storing this kind of data in services. For example I plan on having a users service which all controllers that need user data will inject.
I would like the users service to hold the master list of users and any controller that needs users to just use the one instance of service list.
I am having trouble envisioning the pattern though. I mean:
1) What is the standard way of having the service refresh its data from the server? I realize that I could just go and request the entire list of users every 10 seconds from the server but that seems kind of heavy weight...
2) Ideally I would like to be passing around only a single instance of each user. This way if it gets updated in the service, it is sure to be updated in all of the controllers. I guess the other option is to have the service broadcast an event every time it updates a user? or to use watchers?
3) What is the pattern by which the controllers interact with the service and filters? Do the controllers just request data from the service and filter it in the controller? The other option is to have the service do the filtering. If so how do I communicate the kind of filtering I need done to the service?
I think that by using some kind of solid pattern I can take care of alot of these issues (and more that I am sure will arise). Just looking for advice on some common patterns people employ when using singleton services.
Thanks in advance.
Answer to point 1. A service is just a singleton. How you store and refresh data into it has nothing to do with its nature. Not sure why you want all user data inside a service (unless you are building a user management app), but you could use several techniques like polling (eg. using $timeout ask for new users and append them to the existing ones) or push (eg. socket.io/signalR which will push you the payload of new users when available). This can be done both inside the service itself or by a controller that will add/remove data to the service (eg. a refresh users button in the UI)
Answer to point 2. You can bind/use the reference of the data inside the service directly into your controllers using a getter so that changes to the data are shown instantly (given that are two way binded, if not use events).
Answer to point 3. You can apply filters inside the controllers or in the view it self (not recommended). You can also have a function in the service where you pass the filter or filter params and get the filtered copy of the users collection back (since you will be using the users collection directly in many controllers at once you shouldn't modify that, unless that's desired). If you are reusing the same filters again and again across the controllers you can have a function for each filter that returns the filtered collection with a "hardcoded" filter. You can even have helper function in the service to help you assemble complex filters or have multiple copies of the collection already filtered cached(if you find you are using the same filter again and again)
I'm trying to implement a MVVM based Single Page Application and currently am using the framework Knockout.js to handle the viewmodel/view portion of MVVM. I'm confused though, as every example I've looked at for implementing Knockout involves saving an entire viewmodel to the database. Aren't these examples missing a "model" step, where the viewmodel syncs with the data-layer model and the model does validation / Server synchronization.
I would like to have multiple different templates/views on the single page each with a different viewmodel. Another thing that I find is missing with knockout.js is synchronizing a single model (not viewmodel) across different views. I don't think it makes sense to have one giant viewmodel that every view shares, so I was thinking that each view would have its own viewmodel but each viewmodel would sync with the fields of just a few application-wide models that are needed for each view.
The page I'm working on fetches a giant model (30+ fields, multiple layers of parent/child relationships) and I think it makes sense to just have all of my viewmodels sync with this model. I have investigated Knockback.js (which combines knockout.js and backbone.js) however I ended up re-writing the majority of the functions such as fetch, set, save, because the page is getting data from an API (and I can't just sync an entire model back and forth with the server) so I decided against it.
visual example of my application:
(model layer)
M | M
(viewmodel/view layer) VM-V | VM-V | VM-V | VM-V
another example
An example model would be User = {firstName: "first", lastName: "last", ... }
one viewmodel only needs first name, another viewmodel only needs last name
ViewModelA={firstName: app.User.firstName()}ViewModelB={firstName: app.User.lastName()}
Is the only way to do this to define a pub/sub system for Model and Viewmodel changes? Is this even good/maintainable architecture? Am I missing a basic concept here? All advice welcome.
If I read this correctly, there's a lot of questions in here all focused on how to build a MVVM / SPA with Knockout. There are a few things to tackle, as you pointed out. One is how to communicate between viewmodel/view pairs.
Master ViewModel
One way to do that is to have a master viewmodel as the answer from #Tyrsius. Your shell could have a viewmodel that binds more available data. The master viewmodel could orchestrate the child view models, too. If you go this route then you have to be careful to bind the outer shell to the master viewmodel and the inner ones to specific HTML elements in the DOM. The master viewmodel could facilitate the communication between them if need be.
Decoupled View/ViewModel Pairs
Another option is to use viewmodel/view pairs and no master viewmodel. Each view is loaded into a region of the DOM and bound on its own. They act as separate units and are decoupled from one another. You could use pub/sub to then talk between the, but if all you need is a way for the data to be synched through observables, Knockout provides many options. The one I like is to have each viewmodel surface model objects. So a view has a viewmodel which surfaces data (from a model) that is specific for the view. So many viewmodels may surface the same model in different ways. So when a view updates a viewmodel property (that is in a model) it then ripples to any other loaded viewmodel that also uses the same model.
DataContext
Going a bit further, you could create a datacontext module that manages the data in the models. You ask the datacontext for a model (ex: list of customers) and the datacontext checks if it has them cahced already and if not, it goes and gets them from an ajax call. Either way that is abstracted from the view model and models. The datacontext gets the data and returns a model(s) to the viewmodel. This way you are very decoupled, yet you can share the data (your models) via the datacontext.
I could go on and on ... but please tell me if this is answering your question. If not, happy to answer any other specifics.
** Disclaimer: I'm building a Pluralsight course on SPA's (using Knockout and this strategy) :-)
This is a popular field of interest right now, so I expect you will get some better answers, but here goes.
The Model
Yes, you should absolutely have a server-side representation of the data, which is your model. What this is depends on your server, and your database. For MVC3, this is your entity model. For Django or Ruby, your will have defined db models as part of your db setup. This part is up to your specific technology. But agian, yes you should have a model, and the server should absolutely perform data-validation.
The Application (ViewModel)
It is recommended that your views each have their own viewmodel. Your page could then also have a viewmodel, an App Viewmodel if you will, that keeps track of all of them. If you go this route, the app viewmodel should be responsible for switching between the views, and implementing any other application level logic (like hash bashed navigation, another popular single page tool). This hierarchy is very important, but not always simple. It will come down to the specific requirements of your application. You are not limited to one, flat viewmodel. This is not the only possible method.
Ad Hoc Example:
var ThingViewModel = function(name, data){
this.name = ko.observable(name);
//Additional viewmodel stuffs
};
var AppViewModel = function(initialData){
//Process initial data
this.thing = new ThingViewModel(someName, someData);
};
I am working on a similar project right now, purely for study (not a real world app), that is hosted here on GitHub, if you would like to take a look at some real exmaples. Note, the dev branch is quite a bit ahead of the master branch at the moment. I'm sure it contains some bad patterns (feel free to point them out, I am learning too), but you might be able to learn a few things from it anyway.
I have a similarly complex solution in which I am reworking a WPF application into a web version. The WPF version dealt with complex domain objects which it bound to views by way of presenter models.
In the web version I have implemented simplified and somewhat flattened server-side view models which are translated back and forth from / to domain objects using Automapper. Then those server-side view models are sent back and forth to the client as JSON and mapped into / onto corresponding Knockout view models (instantiable functions which each take responsibility for creating their children with sub-mapping options) using the mapping plugin.
When I need to save / validate my UI, I map all or part of my Knockout view model back to a plain Javascript object, post it as JSON, the MVC framework binds it back to server-side view models, these get Automapped back to domain objects, validated and possibly updated by our domain layer, and then a revised full or partial graph is returned and remapped.
At present I have only one main page where the Knockout action takes place but I anticipate that like you, I will end up with multiple contexts which need to deal with the same model (my domain objects) pulled as differing view models depending on what I'm doing with them.
I have structured the server side view model directories etc in anticipation of this, as well as the structure of my Knockout view models. So far this approach is working nicely. Hope this helps.
During a project i developed a framework (which uses KnockoutJS) which provides decoupled View/ViewModel pairs and allows to instantiate subviews in a view. The whole handling of the view and subview instantiation is provided by the framework. It works like MVVM with XAML in WPF.
Have a look at http://visto.codeplex.com
I'm building a web app that displays a list of customers in a table. There are roughly 15 columns on the table containing different bits of data on these customers. The cells on the table receive user input frequently to update their values.
On page load, the web app sends off an AJAX request with qualifiers to retrieve the appropriate subset of customers out of all active customers. JSON is returned and extended on a global array of objects. Each item in this global array of objects represents a row on the table: var myCustomerList = [{customer_data_object_1},{customer_data_object_2},{customer_data_object_3}].
The HTML for the page is then generated via JavaScript, jQuery, and mustache.js templates. The JavaScript will loop through the global data object (i.e., myCustomerList[i]) and generate rows. I use jQuery's .data() method to link the row itself back to myCustomerList[i]:
$('#tbl_customer_list tr:last').data('cust_data',myCustomerList[i]);
I bind events to UI controls as each cell is appended to a row using jQuery:
$('#tbl_customer_list tr:last td:last a').on('click',function(event) {
custList.cellEvent.status.openDialog(this,event);
});
Event handling functions then refer back to my global data object using jQuery .data():
custList.cellEvent.status.openDialog = function(oLink,event) {
var oCustData = $(oLink).closest('tr').data('cust_data');
//update the global data object
oCustData.status = oLink.value;
}
I have separate code for reconciling changes made to the global data object with the data on the DB tables.
If the above confused you, this page gives a good overview of the client-side MVC approach I'm trying to take: https://sites.google.com/site/jollytoad/mvcusingjquery
So, two questions:
What is a better way of linking model data (browser-side) to the various UI components on the page? Remember I'm using .data() to create a link between the table row DOM element and the global data object.
What is a better way of organizing/storing the model data (i.e., myCustomerList)?
What I'm doing now works, but it seems a little hacky and I'm polluting the global namespace. I don't know how supportable and maintainable it'll be if we ever have to come back to this page and add widgets independent of the customer list.
I've been considering creating a class for the customer list table as a whole (and a new class for any other new widget added to the page) and storing the model data on a property of that class. But I thought I'd come here first to get some tips on best practices in this area.
Use a framework designed to handle this sort of thing, like Backbone, Spine, JavaScriptMVC and so forth.
We use Backbone where I work to handle all of this stuff -- it integrates super well with jQuery.
i think you should take a look at this instead:
http://addyosmani.com/largescalejavascript/
it's a modular pattern to handle various parts of the website. this pattern makes parts of the website independent of one another. each module can have it's own MVC and store it's own states. modules do not have the full logic of the application. it's handled by a sandbox API and core API.
as far as i understand, you load the table's data from the server into the table using AJAX. what i suggest you to do is make that table an independent module.
when it receives the AJAX data, store it in the object
after that, render your table based on that object, store the data into the table. what you put in the table is just the visual data. the actual data remains in that object earlier.
whenever you click on an item, the data in it is an exact reference to the original object you put in. whatever you do to it affects the original object.
Boris Moore is currently working on JsViews & JsRender which is the 'official' jQueryUI way of doing data binding and rendering. It‘s already usable and going beta soon.
I have this Backbone model:
graphModel
attributes
country
indicator
year
With a corresponding view:
graphView
render()
this.model.get(...)
The application also has a general datasource to which csv data is loaded:
dataSource
indicators
indicatorA
country
year
indicatorB
countries
years
Every time the model attributes are changed, I'd like to check if data for that combination of attributes is loaded, before firing change events.
My question is: What is a good way of decoupling the data source from the Backbone model and view so that I can easily try with injected mock data?
I'm a little unclear when you're talking about the application having a data source populated with CSV data whether you're speaking about the server side of an app that Backbone then talks to or something client side.
But either way. I can speak to our experiences on some of this decoupling stuff. We often build models and use Backbone's ability to have "default" values like this example from the Backbone docs:
var Meal = Backbone.Model.extend({
defaults: {
"appetizer": "caesar salad",
"entree": "ravioli",
"dessert": "cheesecake"
}
});
With a model pre-populated like that we can then tie a view to the model and render that data to the page. Similarly, if you have any other source you can think of for easily getting the data you want (a canned file can be loaded with jQuery's .load() function or the contents of a text area could be dumped in via the model's .set() function).
As far as I understood, you have single URL for different models and you want to somehow check it before you set or change model.
You can use parse() method on model to change it's content, see http://documentcloud.github.com/backbone/#Model-parse
I am writing a module based javascript website, backed by a Mysql DB.
Each module talks with the DB through PHP.
On the UI, for simplicity, the module on the left will display all relevant rows, with edit(write) functionality. The module on the right display the data from the same DB, with write access too.
So each would affect the other in each update.
I'm told backbone would be a good framework. Then, I have read the todos example, and understand how there are item->itemList, view->viewList, etc...
But now, the question is, how do I apply it to this situation?
Which is an item, a view in this situation?
The module on the left and the module on the right are Views, each of which can be made up of other views.
Within the model don't store a reference to the view (as I've seen done in some of the examples):
this.view = view; //view being passed in as an arg
The reverse (a view storing a reference to a model) is okay. Your views should be doing most of the work, listening to and responding to model events. Thus, in the view initialize method you might:
model.bind("interesting-event", function(){
//the view updates/reacts to the model.
});
Also, never add a model to two collections (just one ever). When a model is assigned to a collection, Backbone sets a reference on the model that points to the owning collection.
Incidentally, the a-model-cannot-belong-to-two-collections issue is the reason why you don't want a model referencing its view. A model can have many views on one screen.
Backbone is perfect for your needs. Start with a very basic version of your app and keep fleshing it out. All the while keep reading about Backbone online. I read everything I could find (there's not a whole lot, not really). The key concept is simply event based programming (much like you'd use in VB or lots of other platforms). It's a lot of trial and error, but you'll make sense of it with practice.