Set zIndex programmatically on a Div in AngularJS - angularjs

I have a number of Div's on a layout, each of them draggable, so there's the possibility of the user dragging them into positions where they will overlap.
I want to set the most recently made visible div to have a z-index value that's +1 from the last, and for this I'm using a $scope.nextIndex variable that's incremented each time one of the div's is made visible.
Each div has its own variable to track its own z-index value: $scope.one_zIndex, $scope.two_zIndex, $scope.three_zIndex, and each correctly gets assigned an incrementally larger value as each div is shown, hidden and shown again.
Each div has its own class: one_z, two_z, three_z
What I can't make work is assigning the variable to the z-index style on the divs themselves via the controller.
var myVar = document.getElementsByClassName("one_z");
If I log this to the console, I get what I expect - an array with one element [0]
[]
0 : div#one_z
length : 1
one_z : div#one_z
__proto__ : HTMLCollection
I would assume that I could set the z-index simply like this:
myVar[0].style.zIndex = $scope.one_zIndex;
However this throws an error:
Uncaught TypeError: Cannot set property 'zIndex' of undefined
What am I missing? Or is there a better way to accomplish this?

Here is a working plunker https://plnkr.co/edit/aaDipIWdqpajghc2F2Da?p=preview
You can set zindex via ng-style:
<div ng-style="{'z-index': $lastIndex;}">...</div>
This example completely useless in your case, but there is an example of ng-style usage. In your case I'd set index as property of you div element data source.

What wound up working was this:
First, I switched to IDs instead of classes, which likely didn't impact the solution but as each div was only ever going to exist once, IDs was the more correct way of identifying them.
Then, in the function that displays the div, I used this:
_.defer(function(){
jQuery('#one_z').css('z-index', $scope.one_zIndex);
});
There appeared to be an issue where I was showing the div and trying to set its z-index before the DOM had updated to include the div, so _.defer (I'm using Underscore.js) prevented the accessing of the z-index until everything had updated once, and it works.
As I mentioned in another comment - using ng-style wasn't working for me, and I had tried the _.defer with that approach as well initially without success.
For the record - I've used jQuery() instead of $() so it's clear in the code that this is a jQuery and not an Angular solution, and while I'd ideally have liked a purely Angular solution to this problem, this is clean enough for me, and doesn't do any DOM manipulation outside of Angular's purview that impacts the rest of the application in any way, it's purely display candy.

Related

AngularJS - div still shows even without condition

Ok, I know that title could use some work, but I'm not sure how else to put it.
Here's the setup.
I have a (potentially) massive table that gets generated via an ng-repeat. All the rows need to be editable but, when the dataset is so large, all those bindings slow things to a crawl. I could literally be waiting upwards of 20 seconds for large sets to load!
We noticed that dumping the data in a read-only state significantly decreased the load time. So, we came up with the idea of loading it read-only, but, when the table row was clicked, it became editable. This is accomplished like so. I have two cells output. editableRow is initially false. When the row is clicked, editableRow becomes true. The idea being that, when editableRow becomes true, I see the other cell.
(proprietary code obfuscated)
<TABLE-CELL class="value-col" ng-if="readtime.editableRow === true">
<input type="text"
name="readingTime"
ng-model="<data model>"
ng-disabled="<param>"
ng-change="<function>"
ng-class="<classes>"
/>
</TABLE-CELL>
<TABLE-CELL class="value-col" ng-if="readtime.editableRow === false">
<input type="text"
placeholder="{{<data model>}}"
ng-class="<classes>"
/>
</TABLE-CELL>
The problem is, on the click, for a tiny fraction of a second both cells are visible. It really is only visible on the first click. Subsequent clicks still do it, but it goes so fast that the human eye can't catch it. I know it's there since I slowed everything down with a breakpoint on the mouse click. This also revealed that this happens as the value turns true - turning on the first cell, but the second one doesn't disappear in the same moment. So, it causes a "flicker" of sorts. This seems to happen outside my actual code, inside the jQuery, so I'm not sure how to short circuit it.
I've tried playing with using ng-show/hide instead, which worked a little bit, but also totally negated the time-saving aspect, since it actually renders everything, and it took a long time. I've also tried ng-cloak with no effect whatsoever.
The breakpoint that it keeps stopping on (when I told it to stop on event listeners to do with the mouse click) is the following code in jquery.js:
if ( !(eventHandle = elemData.handle) ) {
eventHandle = elemData.handle = function( e ) {
// Discard the second event of a jQuery.event.trigger() and
// when an event is called after a page has unloaded
return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ?
jQuery.event.dispatch.apply( elem, arguments ) : undefined;
};
}
It hits that line about 4 times, and, on the last one, both cells are visible. Then, the second one disappears.
I'm out of ideas and would appreciate any thoughts on this.
I finally found an answer that works!
On this page: disable nganimate for some elements the answer right BELOW the accepted answer is what finally worked!
To disable ng-animate for certain elements, using a CSS class, which follows Angular animate paradigm, you can configure ng-animate to test the class using regex.
Config
var myApp = angular.module("MyApp", ["ngAnimate"]);
myApp.config(function($animateProvider) {
$animateProvider.classNameFilter(/^(?:(?!ng-animate-disabled).)*$/);
})
Usage
Simply add the ng-animate-disabled class to any elements you want to be ignored by ng-animate.

Why is my angular grid so slow?

So, I have made some custom directive which draws kind of a data-grid, based on floated divs (because nested flex implementation in FF sucks - but it's not the point).
How it works :
I pass some data collection to the directive via something like <the-grid data-list="parentController.displayedRows">
Inside this first directive, I have columns via something like <a-grid-column data-value="row.value"></a-grid-column> with many attributes I won't precise here.
The data-value value can be a direct expression, bound to the row on which the the-grid directive controller is ng-repeating in order to display each columns, or a function which have to be $eval-uated in order to display the intended value from the parentController.
In my <the-grid> directive controller, I have the rendering template of my grid which make a nested ng-repeat div (first one on the rows of the data-collection, second one on the columns, created in the directive), it looks like :
<div data-ng-repeat="row in list">
<div data-ng-repeat="cell in theGridColumns"
data-ng-bind-html="renderCell(row, cell)">
</div>
</div>
I have some keyboard nav in order to quickly select a row or navigate within pagination or many tabs, which does nothing more than applying some class on the selected row, in addition to update some "selectedRowIndex".
I'm using angular-vs-repeat in order to have the minimum of row divs in my DOM (because the app is running on slow computers). This works well.
The problem is that every time I'm hitting some "up" or "down" key on my keyboard, Angular is "redrawing" EVERY cells of the list.
So, let's suppose I've 200 rows in my data list, and 7 columns for each rows. First load of the page, it passes ~3000 times in the renderCell() function. Ok , let's accept that (don't really understand why, but ok).
I hit the down key in order to go to the second line of my list. It passes ~1100 times in the renderCell() function.
So yes, the result is very slow (imagine if I let the down arrow key pressed in order to quick navigate within my rows)... I can't accept that. If someone could explain that to me... Any help would be greatly accepted :)
If I make the same thing without a directive (direct DOM manipulation, with columns made by hand and not in a ng-repeat within a ng-repeat), every thing is smooth and clean.
Yes, I've look into every angular grid on the market. No one is satisfying me for my purpose, that's why I've decided to create my own one.
And no, I really can't give you some JSFiddle or anything for the moment. The whole app is really tentacular, isolating this is some kind of complicated).
Try to use bind once (angular 1.3+)
<div data-ng-repeat="row in ::list">
<div data-ng-repeat="cell in ::theGridColumns"
data-ng-bind-html="::(renderCell(row, cell))">
</div>
</div>

ExtJS: dynamically changing element styles

I'm trying to dynamically assign styles to my elements (in this case, a ExtJS displayfield).
I can't use CSS classes since I don't know what the colors will be until runtime.
I'm trying:
myDisplayField.getEl().applyStyles({ color: '#ff0000' }); //fail
myDisplayField.getEl().setStyle('color', '#ff0000'); //fail
Just to get the mechanism right, but neither seem to work.
It works using Ext.get(<div id>).setStyle(...), but that doesn't seem as clean to me. Is there a reason the former attempts don't work?
What am I missing?
Thanks.
The reason is simple: you're trying to set the styles on a wrong element. Each field, including displayfield, has quite complex table-based DOM structure that encapsulates the field label, the actual input element (or a div for display fields), and the supporting elements. field.getEl() returns the top, or main, element for a component; in this case that's the top <table> tag. What you need is the input element instead.
Now if you take a look at the DOM structure you'll notice that the <div> that you need to set styles on has an id of displayfield-123-inputEl. The -inputEl suffix is there for a reason; in most cases it indicates that the element has a Ext.dom.Element shortcut in its respective Component. For a field, that would be field.inputEl property that is available after the field has been rendered to the DOM. You can use that as well:
myDisplayField.inputEl.setStyle(...)
Or just use the convenience method available for the fields:
myDisplayField.setFieldStyle(...)
I would also suggest not hardcoding the colors but rather use CSS classes instead. In most cases there is a limited choice of colors depending on a condition, like invalid input, etc. Using CSS will require a bit more work upfront but will save you a lot of headache down the road, when someone will come up with a bright idea of changing the shade of red used for the invalid input, or somesuch.

How to remove draggability from an element with Ext JS?

I have a div that I want to make draggable or not, depending on the state of some other stuff on my page. I seem to be able to easily make it draggable, but I can't seem to figure out how to best remove the draggability from the div.
I am making it draggable with:
var dd = Ext.create('Ext.dd.DDProxy', mydiv, 'myDDGroup', { isTarget: false });
And I've tried to then remove the draggability by removing the only group it's a member of
dd.removeFromGroup('myDDGroup');
and just destroying the dd object with
delete dd;
Neither of these seem to actually keep me from starting a drag on the element. I suspect I should be able to use the b4Drag override in some way to simply cancel a drag of my div before it even begins, rather than toggling the draggable state of the div at all, but I can't seem to find docs on how I might cancel the drag during the b4Drag call.
So, how can I make a div undraggable after I have already made it draggable?
Seems to be working for me.
Here is the JSFiddle http://jsfiddle.net/01gx33h0/
var dd = Ext.create('Ext.dd.DDProxy', 'test123', 'myDDGroup', { isTarget: false });
Ext.fly('btn123').on('click', function() {
dd.removeFromGroup('myDDGroup');
});
Can you give me the sample code where it is not working. And what version of ExtJs are using?
You have to unreg. Not removeFromGroup.
It just removes from group. But events are not removed on that element.
dd.unreg();
https://fiddle.sencha.com/#fiddle/1eun
When looking at something specific that needs to be dragged, you might consider that allowing dragging is something users expect, for general ease of use you might try the ondragstart="return, this could be appended to your images, or links like so:
<a ondragstart="return false" href="#link">link</a>.

Stop AngularJS inserting <span class="ng-scope"></span> using ng-include

I'm using the Foundation layout framework, which automatically floats the last sibling of .column to the right and I really appreciate this is a behaviour. However, AngularJS takes it upon itself to insert span.ng-scope after every div.column, which somehow causes browsers to consider the last span the last sibling of .column (even though it is not).
Specifically the css in Foundation responsible for this is:
[class*="column"] + [class*="column"]:last-child { float: right; }
As I understand it, [attribute*="substring"] should select only siblings that match, so, for the above, only elements whose class attribute contains column (including columns). I would think a span tag whose class attribute that does not contain column should not match (and thus be ignored by :last-child). However, this does not seem to be the case.
Regardless, the span is causing the problem:
Angular buggering it up (jsfiddle)
Works fine without Angular (same jsfiddle, no ng-include)
Is there a way to configure angular to stop inserting those span tags? I would, begrudgingly, modify the css selector to somehow ignore all span tags; however I might eventually need/want to use a span tag.
Since you indicated the div can be moved inside, this works:
<ng-include src="'main.tmpl'"></ng-include>
Then in your template:
<div class="row">
<article id="sidepanels" class="four columns">
...
</div>
I'm not aware of any way to prevent angular from inserting the span tags (I think it keeps track of scopes that way -- for garbage collection).
Also you can try my version of include directive that does not creates a scope: Gist source.
As no scopes are created, AngularJS should not create additional element to mainain scope (it actually use data attributes to store link to scope).

Resources