The setting
I have Backbone.Marionette.ItemView which renders some content. When the content is rendered I'd like to apply a jQuery plugin which turns part of the view into a container with a scrollbar.
The scrollbar is implemented completely in javascript and upon initialization it has to check the height of the scroll-container as well as that of the content inside the container.
If the content is higher then the container, a scrollbar should be enabled.
The problem
While this all sounds simple, I've ran into a strange problem:
If I initialize my scrollbar plugin directly in the onRender callback it seems to think the .scroll-container element has a height 0 and maxHeight of 0.
If I wrap the initialization code inside a 0ms timeout though, everything works as it should, the .scroll-container element's height property is returned by jQuery correctly and the scrollbar plugin works great.
The code
onRender: function() {
var that = this;
setTimeout(function() {
that.onLayout();
var $scrollContainer = that.$el.find('.scroll-container'),
scrollPane = new ScrollPane($scrollContainer, {
maxHeightProperty: 'maxHeight',
scrollUpButton: false,
scrollDownButton: false
});
}, 0);
},
The question
I'm assuming the problem occurs because the browser didn't finish actually rendering the newly inserted html when the onRender callback is executed.
Is this assumption correct? And if so, is my solution of using a 0ms timeout reliable under normal circumstances?
Since the plugin in question depends on the DOM, onRender won't do what you need. This callback kicks off after the view has been rendered, but there is no guarantee that the view's el has been added to the DOM yet. In fact, you are safe assuming the opposite - that it has not been added yet.
If you're using a Marionette Region to show the view, you can implement an onShow method in your view. This method gets called by the region, after the region has added the view to the DOM. It was implemented specifically to handle this situation.
For a little more info on this, and on working with jQuery plugins in general, see this blog post: http://lostechies.com/derickbailey/2012/02/20/using-jquery-plugins-and-ui-controls-with-backbone/
Related
I just recently started tinkering with AngularJs, so my question is fairly basic. For concreteness's sake, let me start with the minimal setting: a page with banner, body and footer. The goal is to make banner and footer stay at the top and bottem of the page (css position: fixed) and the body will fill in the rest.
If I were to use jQuery, I could set position: fixed for body, and listen to window's resize event to determine where I should put my body.
Presumably, I could do the same in AngularJs. But I've read many places that one shouldn't try to manipulate the layout from the code, since it's not in the philosophy of AngularJs. So, what's the best way to achieve this in the AngularJs way? Thanks.
Dynamic layout usually refers to a layout that changes depending upon the current route. Routing is how AngularJS associates a URL with a Controller and data. It becomes an important part of an app once you go beyond just 1 page.
Multiple Views
If you use something like ui-router (http://angular-ui.github.io/ui-router/) it allows you to define views that can have child views.
In your example, the header, body and footer would all be child views of a parent view. Each view would have it's own template, controller and data. When routing changes you can have just one or more of those view change, while the other views remain the same.
Binding Events
You can do all the same event bindings in AngularJS as you can in jQuery. The difference is the life-cycle of those bindings.
If you bind to the click event for a directive using the directive's element, then you don't have to worry about that binding when the directive is destroyed. AngularJS will remove the binding automatically.
When you bind to something like the window.resize event, then you have to be more careful.
link: function($scope, $el, $attrs) {
// this doesn't have to be unbind
$el.bind('click',function(e) {
// do stuff
});
// this has to be unbind on destroy
angular.element($window).bind('resize.mybinding',function(e) {
// do stuff
});
$scope.$on('$destroy',function(){
angular.element($window).unbind('resize.mybinding');
});
});
I've seen a lot of AngularJS source code bind to window.resize when it wasn't necessary. Only you know when it's needed, but sometimes you can get away with just using a watcher.
link: function($scope, $el, $attrs) {
$scope.$watchGroup(function(){
return [$window.innerWidth,$window,innerHeight];
},function(values){
// do stuff
});
});
The above does the same thing as binding. It executes a closure function when the size of the window changes, but the difference is that it's triggered only during digest and not by the window resize event. This can sometimes be more efficient for performance. As the resize is relative to only when the directive is digested.
A lot of AngularJS applications use JavaScript code to manage the layout of an application. How much JavaScript code you use, and why you do it that way is up to you, but keeping layout separated from the JavaScript side has a lot of advantages. It's better to let CSS handle it.
Bootstrap provides a nice plug-in called Affix that does just that.
<div id="banner" data-spy="affix">Fixed Banner</div>
<div>Body</div>
<div id="footer" data-spy="affix">Fixed Footer</div>
More on affix here for example:
http://www.tutorialrepublic.com/twitter-bootstrap-tutorial/bootstrap-affix.php
What does 'viewport>panel' mean in extjs4? When will it be called? What is the significance of using it?
init : function() {
console.log('In init function');
this.control({
'viewport > panel' : {
render : this.onPanelRendered
}
});
}
Since it is in init function, I know that it is called during the application startup. But my code does not enter into the onPanelRendered method so I assume that the condition 'viewport>panel' fails, but I want to know what exactly does it do and What are the other options that can be used.?
Thanks in advance
ExtJS comes under the category of single page web applications(In general these projects will have only entry point). These single page web applications take control of browser's visible area, this area is called as 'viewport' in ExtJS. ExtJS4 starts rendering your application right from the viewport. It is an extended container. You can check its documentation and config parameters that are applicable for viewport here.
Documentation says
The Viewport renders itself to the document body, and automatically sizes itself to the size of the browser viewport and manages window resizing. There may only be one Viewport created in a page.
Blockquote
Thats all about viewport.
Coming to your second question
"When will it be called? ".
When ever ExtJS application is ready then it searches for viewport.js and starts rendering it on body. That means it will be called as soon as your application is ready.
And finally lets see the usage of viewport in your controller.
'viewport > panel' : {
render : this.onPanelRendered
}
Here you are calling dom query to get hold of panel's events. Above statement says, go to the first panel of viewport's items and execute the render event. In this case, this.onPanelRendered will be called whenever panel gets render.
Possible problems could be
'onPanelRendered' is not available at controller's scope
viewport may not have panel
If viewport has panel, its 'render' event might have been overridden
Application configuration may not be correct
Still you are not able to figure it out, post your complete code and errors, if any.
Viewport in extjs is the entire browser window in which your application gets rendered. viewport > panel is not a condition. You are just finding any panel inside the viewport using a CSS selector and hooking up the render event with the onPanelRendered method.
I am using AngularJS v1.2.16, got simple animations like fadeIn working with ng-show using the class conventions, but also want to fadeIn (or blink) a given div when a given object of the model changes (info of this object is display inside that div).
Note that my code uses $scope.$apply(); as the source of the change is not AngularJS.
$scope.someObject = newData; // Update of the model.
$scope.$apply(); // Force changes.
Maybe there is a way to force the animation before or after the apply or should this be done in a directive ? Finding information on this is quite confusing due to the changes that occurred on the animation system.
I need to run some code (let's say to show message box) right after the view is displayed. I tried to override OnInitialize, OnViewLoaded or OnViewAttached but it's always the same. The View is not fully displayed yet.
I use some animation when displaying view but at the same time need to load some data into grid. If I put data load into OnInitialize, OnViewLoaded or OnViewAttached the animation is not smooth as it's kind of happening the same time when loading data.
Any thoughts?
Give something like this a try - use a couroutine to wait for the animation to complete before binding the grid:
private IEnumerator<IResult> ViewModelStartup()
{
yield return new WaitForAnimation("AnimationName");
BindData();
}
(note - you can load the data async, but just don't assign it)
Then when your form loads:
private void OnViewAttached()
{
Coroutine.BeginExecute(ViewModelStartup(), new ActionExecutionContext() { View = this.GetView() });
}
(the code above might not be 100%... I think View must be FrameworkElement in ActionExecutionContext so cast as needed or create a wrapper class)
The implementation of the WaitForAnimation coroutine would search the view for a named animation and wait for it to complete before firing the callback. You should probably just fire the callback if the animation could not be found. The couroutine could be used on multiple views.
(Coroutines must implement IResult have a look at the docs on the CM codeplex site for info)
http://caliburnmicro.codeplex.com/wikipage?title=IResult%20and%20Coroutines&referringTitle=Documentation
Since the ComboBox and FilteringSelect use a 'dijitPopup' whose DOM element gets inserted just before the closing body tag (presumably to help with ensuring it appears above everything else z-index-wise) this means that if the ComboBox is contained in an element that scrolls independent of the window itself and the user opens the dropdown and then scrolls the window (or whatever containing element) using the scroll wheel, that the menu part doesn't move with the control itself.
Is there a straightforward way to ensure that the menu part of the view remains positioned correctly relative to the control itself rather than simply assuming that its starting position is ok?
EDIT: appears to be a known issue (http://bugs.dojotoolkit.org/ticket/5777). I understand why they put the dijit popup just before the closing body tag for z-index stacking and overflow clipping issues, but it seems like it's maybe not the ideal way to do things given the bug in question here and things like:
You can restrict the Dijit theme to only small portions of a page; you
do this by applying the CSS class of the theme to a block-level
element, such as a div. However, keep in mind that any popup-based
widget (or widgets that use popups, such as dijit.form.ComboButton,
dijit.form.DropDownButton, and dijit.form.Select) create and place the
DOM structure for the popup as a direct child of the body
element—which means that your theme will not be applied to the popup.
~ from http://dojotoolkit.org/documentation/tutorials/1.6/themes_buttons_textboxes/
Not sure if this is the very best solution, but here's what I came up with:
Since the widget may be programmatically added/removed, and to avoid coupling a solution with some particular surrounding markup that we can't always count on in all cases, what I did was to hook the _showResultList and _hideResultList methods of ComboBox and when the popup opens, traverse up the DOM till we reach the <html> tag, adding onscroll listeners on each ancestor.
The handler for the onscroll event is simply:
var myPos = dojo.position(this.domNode, true);
this._popupWidget.domNode.parentNode.style.top = '' + (myPos.y + myPos.h) + "px";
where this is the widget in question. I scope the handler to the widget using dojo.hitch. In the close method I remove the listeners. I have to clean up the code a bit before it's presentable, but when it's finalized I'll add it to this answer.
Note: I only show updating the y position here. Part of the cleanup is to add x position updating in case someone scrolls horizontally.
Though its old I just faced this same problem and it looks like a Dojo issue and the fix is available here https://bugs.dojotoolkit.org/changeset/30911/legacy