Good, therefore, in my opinion, there are many ways you can fix this. I have listed everything that I thought about, and I hope you will work with you, or at least it will inspire you to find the best solution.
I am not completely opposed to the answer of T J. If you just go in and do collection.fetch () on all products when the website loads (usually users expect that there will be some loading time), then you have all your data , and you can just pass this data round as he mentioned. The only difference between what he offers and what I usually do is that I usually have a link to the application in all my views. So, for example, in my initialize function in app.js, I will do something like this.
initialize: function() { var options = {app: this} var views = { productsView: new ProductsView(options) }; this.collections = { products: new Products() }
Then, in my productsView.js initialize function, I would do the following:
initialize: function(options) { this.app = options.app; this.views = { headerView: new HeaderView(options), productsListView: new ProductsListView(options), footerView: new FooterView(options) }; }
The mobilities that I create in the initialization in productsView.js are arbitrary. I basically just tried to demonstrate that I continue to pass this parameter object also in view representations.
What this does allows each view, whether it is a top-level view or a deeply nested preview, each view knows about all other views, and each view has a link to the application data.
These two code examples also introduce the concept of defining your functionality as accurately as possible. Do not try to have an idea that does everything. Transfer functionality to other views so that each view has one specific goal. This will facilitate the reuse of views. Especially complex modalities.
Now back to the current topic. If you were going to go ahead and load all the products in front, where should they be taken? Because, as you said, you do not want the blank page to just sit there in front of your user. So my advice is to trick them. Download as much of your page as possible and block only the part that requires data loading. In this way, the page looks like it is loading while you are really doing work behind the scenes. If you can fool a user into thinking that the page is steadily loading, they are much less likely to look impatient when loading the page.
So, referring to the initialization from productsView.js, you can continue and enable the rendering of headerView and footerView. Then you can make your selection in the rendering of productsListView.
Now I know what you are thinking. I lost my mind. If you make a selection in the rendering function, then there is no way to return the call before we hit the line that actually displays the productsViewList template. Well, fortunately, there are several ways. One way is to use Promises . However, as I usually do, just use the render function as my own callback. Let me show you.
render: function(everythingLoaded) { var _this = this; if(!!everythingLoaded) { this.$el.html(_.template(this.template(this))); } else {
Now, by structuring our rendering in this way, the actual template will not load until the data is fully loaded.
As long as we have a rendering function, I want to introduce another concept that I use everywhere. I call it postRender. This is a function in which I execute any code that depends on the DOM elements in place after the template has finished loading. If you just encoded a simple .html page, then this is the code that is traditionally included in $ (document) .ready (function () {}); . It may be worth noting that I do not use .html files for my templates. I am using javascript embedded files (.ejs) . Continuing to work, the postRender function is a function that I basically added to my boiler room code. Therefore, whenever I call the rendering for presentation in the code base, I immediately click on it postRender. I also use postRender as a challenge for myself, as I did with the render. So essentially the previous code example would look something like this in my code base.
render: function(everythingLoaded) { var _this = this; if(!!everythingLoaded) { this.$el.html(_.template(this.template(this))); } else { // load a spinner template here if you want a spinner this.app.collection.products.fetch() .done(function(data) { _this.render(true).postRender(true); }) .fail(function(data) { console.warn('Error: ' + data.status); }); } return this; }, postRender: function(everythingLoaded) { if(!!everythingLoaded) { // do any DOM manipulation to the products list after // it loaded and rendered } else { // put code to start spinner } return this; }
By linking these functions in this way, we guarantee that they will be executed sequentially.
==================================================== =========================
So, this is one way to solve the problem. However, you mentioned that you do not want all products to be loaded in advance, fearing that the request may take too long.
Side Note: You should really consider getting some kind of information related to the product call, which can cause the call to take a considerable amount of time, and make most of the information a separate request. I get the feeling that users will be more asking for the data to take some time to load, if you can get their basic information very quickly, and if the thumbnails associated with each product take a little longer to load, it should not be then Peace. This is just my opinion.
Another way to solve this problem is that you just want to go to a specific product page, and then just implement the render / postRender template , which I described above on a separate ProductView. However, note that your productView.js will probably look something like this:
initialize: function(options) { this.app = options.app; this.productId = options.productId; this.views = { headerView: new HeaderView(options), productsListView: new ProductsListView(options), footerView: new FooterView(options) }; } render: function(everythingLoaded) { var _this = this; if(!!everythingLoaded) { this.$el.html(_.template(this.template(this))); } else { // load a spinner template here if you want a spinner this.app.collection.products.get(this.productId).fetch() .done(function(data) { _this.render(true).postRender(true); }) .fail(function(data) { console.warn('Error: ' + data.status); }); } return this; }, postRender: function(everythingLoaded) { if(!!everythingLoaded) { // do any DOM manipulation to the product after it // loaded and rendered } else { // put code to start spinner } return this; }
The only difference here is that productId was passed in the options object for initialization, and then fetched and used in .fetch in the rendering function.
==================================================== ======================== In conclusion, I hope this helps. I'm not sure that I answered all your questions, but I think I made a pretty good pass. For the sake of this, I’m going to stay here too long and let you digest it and ask any questions you have. I assume that I will probably have to do at least 1 update to this post to clear it even further.