Collections vs Models (and when to use a factory for each)

Hi,
   I can get a basic widget to work when it has a single model. What I'm trying to do is something where I need an array of models, or wait, how about a collection :smiley:   Now when I started looking at the various source in the CSUI SDK (because lord knows there are no examples of this), it appears like using a collection in the context of smart UI means your use case is that you have a single REST call whose response would give you an array of results that become an array of models, or basically your collection contents.
  My use case is a bit different. It is the case where I want to have a small collection of models but each model needs its own REST call to populate. 
  What I've tried doing is in my collection constructor, I pass in a list which tells me exactly how many models I need, and I populate each model.
  Where I am confused is how I get the models to trigger from within my collection, so that when my view loads, it builds the collection, then auto-triggers the appropriate REST call in the url: method in the model...or do I somehow need to do that in the collection? (the url: seems to only allow for one returned URL....I need multiple REST calls).
  It would be helpful if there were some more documentation around this. Also, Factories. I *think* the purpose of using a Model Factory or a Collection Factory is to make sure you get your context object embedded into your model or collection. Could someone confirm that is the case?
Thanks
-Hugh

Comments

  • As expected, I am going to answer my own question :wink:. My use case is a little bit different from your typical collection-model use case. Both collections and models in Backbone allow you to specify a URL function which is used to do whatever REST calls you need, and a parse function to return the results and populate your model(s).  Normally, when you use a collection, you would populate the URL and parse methods of the collection object. However, if you have a use case where (maybe it's a legacy REST API) you need an individual REST call for each model, you can specify the URL and parse functions in the model instead.  Then, once my dialog has loaded, from my collection, I iterate through my models (i.e. in the collection, I wrote a function that iterated through the models, and executed the model's fetch() function. Calling fetch() on the model causes the URL to be called, and the parse function to process the return results and populate the model.
    For those requiring a little syntax (but not too much...y'all have to suffer like I did):
    within your collection object add a function like this:
    refetch: function() {
                  this.each(function(model, index) {
                            model.fetch();
                   });
    }

    Now on the subject of factories. The factories are necessary because a Backbone collection or model created using a factory has an object called context passed in the options that get passed to the constructor.  The details on this are a little murky to me, but the intention of this post is to kind of "blog" my journey through smart UI learning.  
    Within the context object is a connection object.  It is this connection object that is essential to you being able to do a REST call to Content Server. The connection object has the otcsticket as well as information about the base URL (i.e. hostname/OTCS/cs.exe....)
    I *think* that the otcs ticket gets invoked somewhere in the URL object that you pass in to your model.  Your model definition would look a little bit like this at the top
    define(['csui/lib/backbone', 'csui/utils/url'], function(Backbone, URL) {
     'use strict';
    var myModel = Backbone.Model.extend({
    ...your defaults, constructor, etc
    ,
    url: function() {
          var nodeID = some parameter from your model
          // Where this connector is is a bit of a challenge. I found it not always available in the options. You may have to 
          // store it
          var url = URL.Combine(this.options.connector.connection, '/nodes/', nodeID, 'node');
          return url;
    },
    parse: function(response) {
       // Could refactor the response, adding or removing, but we'll just return as is
       return response
    }
    });

    I did find challenging that I can save the connection from the options in the constructor, then later on, it seems to just disappear. I ended up having to pull the connection from the collection directly in my refetch function, and explicitly set it in each model prior to calling fetch().  Not sure why that is.
    -Hugh
  • Hi Hugh,

    Personally I wouldn't consider adding models with different Rest endpoints to the same collection. This will probably make the application unnecessarily complex and error prone. Collections are designed to contain and manage models of the same type and be associated with one Rest call which returns an array of objects of the same type somewhere in the response. If you have multiple separate models, you can just create multiple models and manage them independently. These can each be associated with one factory if you want the Perspective context to fetch them all for your widget on page load. If you look at the csui/utils/contexts/factories/appcontainer component that is used for the standard browse scenario, it has multiple models and collections associated with one factory and one widget. The fetch implementation is overridden to make the other Rest calls and associate them with the factory object as properties.

    For info the context/factories mechanism has some documentation in csui/utils/contexts/context.md.

    There are other ways of getting to the same goal, mostly it would just involve making multiple fetches and performing some action when getting the responses. e.g.

                    $.when(model1.fetch(), model2.fetch(), model3.fetch())
                        .then(function(){
                            // Trigger some event to update your UI if needed.
                        });

    Cheers
    Ian