How to fix overlapping bricks with Angular + Masonry? - javascript

I'm using passy's angular-masonry directives to render tiled elements in my app. Unlike a lot of the masonry examples, my bricks don't include images, just static text and layout content rendered through a custom directive. My setup looks like:
<div data-masonry
data-column-width="250"
data-load-images="false"
data-preserve-order
data-reload-on-show
data-masonry-options="{ gutter: 15, isFitWidth: true, transitionDuration: 0 }">
<div class="masonry-brick"
data-ng-repeat="event in vm.events | orderBy: 'startTime.toDate()' | filter: (vm.showOnlyRegistered && { going: true })">
<div data-event-item="event"></div>
</div>
</div>
data-event-item is my directive that renders something like:
<div class="panel panel-default panel-thin light-shadow bgcolor-override event-item-card">
<div class="panel-body" data-ng-class="{'bg-success': event.going}">
<div>
<p class="text-medium text-thin">{{event.name}}</p>
</div>
<p>
<strong>{{event.computed.locationName}}</strong><br />
<span data-ng-if="!event.virtual">{{event.city}}, {{event.state}} {{event.zipCode}}</span>
</p>
<span class="center-block">
<span data-discover-pill data-type="default">
<span class="text-thin">{{event.computed.registrationLabel}}</span>
</span>
</span>
<button type="button"
class="btn push-to-bottom bottom-center"
data-ng-class="{'btn-default': !event.going, 'btn-success': event.going}"
data-ng-click="toggleGoing(event.id)">
I'm Going <i class="fa fa-check"></i>
</button>
</div>
In my CSS, I have a defined width and height for the elements that go into the bricks, so that (plus the fact that I'm explicitly setting masonry column-width) should let masonry know how big all my bricks are.
Everything works fine except that sometimes (randomly?) on pageload all the bricks are rendered stacked on top of one another in a big pile on the left edge, as if the layout routine didn't trigger. If the window is manually resized, everything snaps to normal and stays that way. This seems to be a problem that some others have run into: https://github.com/passy/angular-masonry/issues/82
I've tried all the combinations of preserve-order and load-images="false" and so on. I think I need to manually trigger a reload/relayout, but as far as I know with the passy directive, you can't directly call masonry methods.

I kept running into issues with Passy's directive. Perhaps it was due to the number of bricks I was loading or my styling but I kept getting a lot of sporadic overlaps, delayed reloading of bricks and so on.
I switched to klederson's angular-masonry-directive and have been extremely happy with the decision. I haven't benchmarked to confirm but it seemed to have sped up load times.
angular-masonry-directive
A very simple and 100% compatible masonry directive for AngularJS ... do you know how to use masonry? Good! You know how to use this.
This directive is meant for the raw masonry lib and not the jQuery one.

For anyone running into a similar problem, I figured it out with some help from these answers. As a bonus, this helps me with refreshing after using Angular filters to modify which bricks are shown (the second linked answer deals with this).
Here's my working code, which I trigger when my async data has loaded on pageload, and also anytime the data is filtered or resorted:
function refresh() {
// We need to give Masonry a little jump-start, otherwise the bricks
// will render in one big overlapped stack sometimes
common.$timeout(function () { $scope.$broadcast('masonry.reload'); }, 100);
}
Without $timeout, it looks (to my poorly-trained eyes) like the reload message is being consumed before the digest cycle completes, so the filtering or sorting may not have already taken place. I'm not sure that this is the best solution, but it seems to work in my testing so far. If anyone has a better one, I'd love to see it.

Tried 3 framework and only this one works :
https://github.com/s-yadav/angulargrid
No 3rd party lib required !! and smootly with twitter bootstrap. Hope it may help someone in the future
Note on others :
https://github.com/passy/angular-masonry -> work quite smooth on dev but failed on PROD.
https://github.com/klederson/angular-masonry-directive -> works but all the bricks are invisible !!?

Related

Writing a directive to encapsulate multiple directives Angular

I'm using Angular 1.x and I have a section of code that I'm looking to repeat quite a bit, so I wanna throw it in a directive. The trouble is, it's somewhat complicated and I'm not sure how to begin writing it.
Essentially, it's a section of the page that displays various card directive and with infinite scrolling and perfect scrollbar.
<perfect-scrollbar refresh-on-change="myScope.data">
<div class="limit-columns">
<masonry masonry-options="{gutter: 30, isFitWidth: true}">
<user-card class="masonry-brick" ng-repeat="item in myScope.data"></user-card>
</masonry>
<div class="infinite-scroller" infinite-scroll="myScope.showMore()" infinite-scroll-tolerance="5"></div>
</div>
</perfect-scrollbar>
Perfect-scrollbar and masonry are both angular libraries on GitHub. Infinite-scroller is one I wrote myself, but works as you'd expect.
myScope contains a data attribute that is a list of objects containing a card's data. myScope.showMore is a function that adds items to that myScope.data list. Perfect-scrollbar also takes the refresh-on-change attribute which watches for changes on a particular object, in this case the list.
Ideally my directive would look something like this:
<card-scroller gutter="30" tolerance="5">
<some-card ng-repeat="achievements.data"></some-card>
</card-scroller>
But I'm not sure how feasible this is. Thanks!

(Why) does having a directive that "calls itself" in a finite ng-repeat lead to a stack overflow from infinite recursion?

EDIT: See this plunker for a simplified version of the problem (an sscce).
The following code seems to cause an infinite recursion problem:
statement.directive.html
<textarea></textarea><br />
<button ng-click='statement.newStatement()'>New Statement</button>
<button ng-click='statement.newSubstatement()'>New Substatement</button>
<hr />
<statement
ng-repeat='statement in statement.curr.sections'
curr='statement'
parent='statement.curr'>
</statement>
I don't know why though. statement.curr.sections is zero (when I tested it). So wouldn't that directive not get "instantiated/implemented"?
<p ng-repeat='statement.curr.sections'>x</p>
Doesn't cause any problems.
Wrapping it in an ng-if doesn't fix the problem.
<div ng-if='statement.curr.sections > 0'>
<statement
ng-repeat='statement in statement.curr.sections'
curr='statement'
parent='statement.curr'>
</statement>
</div>
Edit: this doesn't work either (but I realize I chose bad variable names).
<textarea></textarea><br />
<hr />
<statement
ng-repeat='item in statementCtrl.curr.sections'>
</statement>
Code on GitHub.
I took a look at your github code but it's really confusing. The naming is definitely throwing my brain in circles, so I would start by clarifying that.
For a directive, you should use a prefix so that it's clear you're referencing a directive: ie. myStatements, elStatement, whatever.
For a service and controller, use a suffix: StatementService, StatementController.
What I don't understand:
<button ng-click='statement.newStatement()'>New Statement</button>
What is statement. referring to here? When does this become initialized/binded to the view? Edit: nevermind I see that's the controller.
OK, so you've got the template for the directive, referencing the directive itself...
Hmm, not sure if a directive can have a child directive that is itself. Seems like quite the infinite loop when the template is trying to render. Because even if the next layer/directive doesn't have data, it still needs to bootstrap the directive and its template, so that leads it to load the next directive... and it never finishes.
Seems like an anti-pattern. What is the use case? Are you trying to draw a fractal? lol
In terms of fractals, you need to pass a limit (ie. when a fractal becomes so small you can't see it, there's no longer a point to draw it). So by imposing some limit, you can stop it from infinitely trying to draw.
tl;dr: Even if the data is empty, the child directive is still being initialized/rendered.

div has different width after page refresh

When someone opens the page a <div> gets full width (1200px+). If I reload the page, the width is changed to the right one (550px). The browsers have their cache cleared, so this isn't a cache issue.
First visit:
After refresh:
This is a "custom HTML code" module in a Joomla 2.5 site. This is the code of divs that have their widths swapped:
<div class="art-nostyle">
<div class="custom">
<div id="script_nn_tabs54bfa417561de" class="script_nn_tabs" style="display:none;"></div>
<div id="nn_tabs_container_1____685___" class="nn_tabs_container outline_handles outline_content align_left nn_tabs_container_1_">
<div class="nn_tabs_nav" style="display: block;"></div>
At first sight I thought that the div id="nn_tabs_container_1____685___" was the problem, so I added this jQuery script at the end of the module :
var j = jQuery.noConflict();
j(document).ready(function () {
j("div[id^='nn_tabs_content_1____']" ).css("width","550px");
});
After it failed to fix it, I noticed that the problem was at the <div class="art-nostyle">. That div has no style at all! I can't use the above script for the art-nostyle div because it is added before every single module in the site. Could someone explain how it is possible when this probably isn't a cache issue - an element getting fixed width after a page refresh? I tried it on 5 different PCs that never visited the url before.
P.S. I can't recreate the problem in JSFiddle: that's why I didn't post a fiddle link.
Edit: Link of the site if someone want to check with his own eyes. Its the in middle of the index.
Edit2: I noticed that if i disable cookies the div wont change width after refresh. It will keep the full width.
If you're using jQuery, maybe you could remove the ".art-nostyle" class that may be inheriting weird styles from Joomla. You could give the one <div class="art-nostyle"> a unique ID (e.g. id="navigationLinks"), and then use this:
$(function() {
$("#navigationLinks").removeClass("art-nostyle");
$("#navigationLinks").css("width","550px");
});
You could also check to see if there's any other Javascript that references this div, because it seems weird that the problematic div would inherit the strange behavior just from HTML/CSS.
I had the same issue. However, I have found the answer. Use $(window).load() out instead of $(document).ready().
See also: window.onload vs $(document).ready()

How to customize the OpenERP 6.1 WebClient HTML DOM?

The page of OpenERP web client can get really wide with many columns in a list view. On short screens, this is a trouble as the centered menu runs out of reach to the right with the content. I decided to find a quick fix for this: Make the menu align to left. With normal websites, this would be a piece of cake with standard JQuery, but this OpenERP web thing is pretty much completely generated in JS!
The generated HTML has the following structure for the menu:
<div class="menu" id="oe_menu">
<table align="left">
<tbody>
<tr>
<td>
<a href="#" data-menu="3">
Settings
</a>
</td>
<!--other menus...-->
</tr>
</tbody>
</table>
</div>
The way to go with JQuery is (tested in JS console):
$('div.menu table[align=center]').attr('align','left');
Though the usual $(document).ready() will fail because the time the DOM is loaded is only the initialization of the OpenERP web client.
My requirement is that this needs to be managed from a module. Simahawk got his answer for a similar topic - hooking into the logout event which pointed me in the right direction, but did not fix my task.
I found and modified a piece of code from the web_livechat module which finally worked. My module is called camara and that is important, because the new method of the openerp object must be called after your module - static/src/js/tweaks.js:
// openerp.camara(openerp) is executed when the module is loaded - UI is not rendered yet!
openerp.camara = function(openerp) {
// so we hook into (override) the Header do_update() call
// which is executed upon session validation by the WebClient
// we have to call the overriden parent method
// then we hook onto the update_promise
// which executes our code after the update is done.
openerp.web.Header.include({
do_update: function() {
var self = this;
this._super();
this.update_promise.then(function() {
// change menu alignment to the left
// because with wide views, it runs out of reach
$('div.menu table[align=center]').attr('align','left')
});
}
});
}
The js file needs to be included in the __openerp__.py:
'js': ["static/src/js/tweaks.js"]
Questions remaining:
Do you like this and find it an appropriate approach?
If not, please offer other solutions.
I myself find this rather clumsy, that's why I ask. I thought of using CSS but did not manage to override the table's align attribute.

Nesting resizable elements

I am using jQuery UI's resizable for nested divs, like so:
<div id="resizable1">
<div id="resizable2">
</div>
</div>
I'm running into a problem where disabling resizable 1 also disables resizable 2. So, if I call the following...
$("#resizable1").resizable("disable");
...then I can no longer resize resizable2 either.
Has anyone else encountered this, and know of a way around this behaviour?
Thanks,
Travis
I was having trouble using nested resizables as well.. after setting up the second (nested) I lost the ability to resize the top level one.
To work around this I initialize, and destroy the nested one upon hover over/out:
$(".the-nested-elements").each(function() {
$(this).hover(function() {
$(this).resizable();
},function() {
$(this).resizable("destroy");
});
});
It's not the most elegant solution, but it works.
A little late, as I'm sure you've moved on, but I ran into the same issue. This is related to a known issue: http://bugs.jqueryui.com/ticket/5973
According to rdworth, you can do a workaround for this:
$("#resizable1").resizable("disable")
.removeClass("ui-state-disabled ui-resizable-disabled")
.children(".ui-resizable-handle").hide();
You can check out the original post at: http://forum.jquery.com/topic/trouble-with-nested-resizables, or check out the fiddle at: http://jsfiddle.net/rdworth/vaD8v/

Categories

Resources