This question concerns either an issue with BB.Subviews and me not knowing how to control when it renders the subview, or my code is designed incorrectly. My issue is one of my subviews fetches it's own collection of models. It then (is supposed to) renders this collection of models. My console.log lines show me that BB.subviews is rendering the child before it has finished fetching it's collection. Is there a BB.subview setting I'm not seeing in the instructions or is my design bad and I should be fetching the collection for the child and passing it in? The latter would work but seems wrong to me.
Here is at least one experieced BB coder that agrees with my design. Fetch Collections withn Views.
In my older code (pre BB.subviews) the child subview called it's own render after it had finished fetching it's collection. I can't do this with BB.subviews.
Ok my answer is to drop Backbone.Subviews and Backbone.Courier. No offense to these awesome coders intended. None. I want to include all code (including fetching) relevant to the child view within the view, not pass it from the parent. I need to control when the child views render. The parent talks directly (calls their methods) to each child, the children use global messages to talk to their parent and siblings.
Related
I'm re-working a website that has a very nested interface requiring several child views. Think of a shopping site, with paging, results and filters. What I'd like to do is render default content for said children views while the results are being resolved from the back-end. However, I can't find a way to insert default content past the first <ui-view>, which of course, makes sense.
To get around this, we are currently using $broadcast in the child state controllers. We moved the resolution out of the resolve event into the controller, which is working, but requires us to make all of our directives use $broadcast as well, or they don't work since the data isn't loaded before they are. It also seems like a very inelegant solution to the issue.
What's weird, is that when I move the resolve function into a child view, neither the parent nor it's siblings views load before the results child view loads. I can't understand that whereas I can understand children views not loading before the parent is resolved.
Is there a way we can work around this? Building in broadcasting into all aspects of our code base seems like an extremely poor practice. Is there a way to show children default content before the parent is resolved, or even render child states' views before their controllers are instantiated?
Use ng-cloak class:
https://docs.angularjs.org/api/ng/directive/ngCloak
The ngCloak directive is used to prevent the Angular html template from being briefly displayed by the browser in its raw (uncompiled) form while your application is loading. Use this directive to avoid the undesirable flicker effect caused by the html template display.
What is the best way to switch to a different view when user navigates to a different url. In angular there is ng-view that takes care of this and inserts corresponding templates and in ember its all route based.
Is it better to just hide other views elements on routing using css or destroying other views and inserting current view?
EDIT
It would be great if someone could give an example how to re-render the view on navigating back to it again and restoring its previous state.
Eg.
if you have a check-box in a view that user can select to add some item to the cart , but in the middle he/she moves to some other url and then comes back, that check-box should be checked.
I would have a main content view with subviews and call remove on it, which is responsible for cleaning up any subviews too (calling remove on them first and going up the hierarchy tree). The concept of subviews doesn't come for free with backbone but isn't hard to implement. And finally attach a new content view.
This ensures you can cleanup and the browser is using a consistent amount of resources.
I would abstract this into some kind of layout view which has a content subview and a function like setContent(view) which handles the remove of any existing content view and the attach of the new one.
Personally I would have a router with sub routers in modules, e.g. a main router which finds a route starting with "checkout" and passes it over to a sub router in the checkout module which is responsible for attaching a new content view.
In Backbone the implementation is up to you which is both good and bad, depending on how nice you do it ;)
Always remove the view as opposed to just hiding it. If you don't remove (and unbind) your views properly, all bindings, handlers and references to models/DOM elements will linger around.
Depending on the size of your app, you can have a module that handles layouts (as suggested by dominic-tobias), or have a method on the router that takes care of this for you. At its most basic, this method (let's call it _switchView) takes a view and holds onto an instance of the currentView. Upon view change, it removes the current view, sets the new view to the current view and then renders it to the DOM.
Something like this:
_switchView(view) {
this.currentView && this.currentView.remove();
this.currentView = view;
this.$rootEl.html(view.render().$el);
}
I'm trying to accomplish something that seems easy at first glance, but ends up being quite the challenge. I have an accordion-like section of an app, where each accordion item should open a child state when activated, with a few extra requirements:
child states can (and should) be the same child state, with different parameters.
child states are not know up-front, they're loaded dynamically.
deeplinking to a child state should work as expected. The accordion item expanded and the proper content loaded.
The idea is easy, there should be one child state, which loads/shows different data depending on the passed parameter, but the template of that child state should be place in the activated accordion item, not in one fixed place.
I partially tried the multiple named views option from ui-router, but doesn't look promising, since it would actually load all those named views at the same time. Plus, I need them to by dynamic, and even though is possible to define states dynamically with for example with Future states, it doesn't seem to be the right choice here.
Right now, I can only see 2 options:
Re-parent the ui-view inside the desired accordion item (didn't work at first try but did if I re-parent the container of the ui-view) but has buggy side-effects right after the testing, and I fear some critical side effect later. Basically the parent controller get's reloaded for a second time, keeping the previous instance in memory. Plus i've seen some duplicated DOM content in places outside the scope of even the parent. I don't like this approach of course, but has the advantage of actually placing the content I need inside the container I need.
Leave the ui-view outside the accordion, position absolute-it, and manage it's position based on the current state when navigating. The position absolute is not a big deal, but I need to keep measuring the content's height, and dynamically set the height of the expanded accordion item to make it look like it's inside of it :S. To make things more difficult, I'll probably have to place some complex rules to position this correctly for the responsive design this needs.
In the end, it's a route/code hack vs a visual hack. I'm taking the visual hack since it sounds safer, but it's definitely going to be more work. Any other approach or comments will be highly appreciated.
-- Edit --
There's a better option at least for the 2 options I had in mind: create the different child states (1 child state, with params) but not associate it with a template. Just leave all the DOM in the parent state/view, and manage visibility with ng-if
Did you think about the option to use only one child state for all this and pass the additional information you need using parameters of the state?
so have URL-paramter for the accordion-section-id you want to open and other parameters for the different data to show.
Then open the correct accordion-section based on the stateParamter. Write a directive with private scope to render the content of the section and in each accordion reuse the same directive and pass it the correct data.
Then if the user clicks to open another accordion section, instead of the normal 'open'-action use a $state.go('myState, {accordion-id: 'newidtoopen', datatoShow: dataids}).
I'm wondering why you need to put a object.on(event, callback, [context]) specifically within an initialize method in backbone, and not somewhere else?
Is it because the initialize method runs automatically -> and the listener starts to listen automatically for that reason?
A better way to design a backbone application is to separate data loading code and views construction.
This can be done by using a separate controller(A custom javascript object) that does the data load part i.e, initializing your models or collections. The controller loads the data (models/collections) necessary for a particular view.
The view instance is then created. Data required for the view can be passed as parameters to the view. One of the better ways is to pass it to use the initialize method which gets invoked automatically during the instantiation of the view.
Also we will expect the view to reflect the changes that model/collection undergoes. Its good to define what events of the model/collection that must be listened to in the intialize method since:
You know that it will be called for sure.
You know for sure that the event binding will be set, since you can reach a view from more than one way like thru a route or a click event on tabs for instance (though better designing is that you have a center piece of code which handles all the ways you reach a view).
Also set the rules for event bindings during the initialization itself.
You can also refer to the below link that talks about decoupling view from data loading.
Should the backbone router or view handle fetching data and displaying loading status?
What is the best way to let subviews/child views know when they are already in the DOM?
Consider we have following hierarchy of views:
MainView
SubView
ChildView
Here for example MainView is already in the DOM. Then as a part of render process MainView creates SubView which in turns creates ChildView.
As a result ChildView won't be in the DOM until MainView finishes its' rendering. And I need to know (via event or handler) when it is inserted and displayed.
The most straightforward answer is for MainView to trigger some event when it is done rendering. If it were the only case where I needed it - it would be alright. But there are lots of places and in general suddenly subviews dictate their parents what to do, probably not good.
Update:
In what cases this is required? When we are using widgets which expect element to be in the DOM (to get height, etc). In example: Accordion widget, jqGrid. So the correct time to init them when their container is already in the DOM.
I'm not sure what your use case is but I'm assuming its finding a DOM element within the Childview and manipulating it. In this case, it would not matter if its in the DOM or not.
What I typically do is grab the reference to the element - $el
and then find the sub-element
$target = $el.find('.target')
This has the added benefit of only allowing me to grab classes or ids within the ChildView ensuring a proper management of views.