It is possible to use an ng-repeat to achieve the following compiled DOM:
<div class="container">
<!-- ngRepeat item in items -->
<div ng-repeat="item in items">Item 1</div>
<!-- end ngRepeat: item in items -->
<!-- ngRepeat item in items -->
<div ng-repeat="item in items">Item 2</div>
<!-- end ngRepeat: item in items -->
<!-- ngRepeat item in items -->
<div ng-repeat="item in items">Item 3</div>
<!-- end ngRepeat: item in items -->
<div class="wrapper">
<!-- ngRepeat item in items -->
<div ng-repeat="item in items">Item 4</div>
<!-- end ngRepeat: item in items -->
<div ng-repeat="item in items">Item 5</div>
<!-- end ngRepeat: item in items -->
</div>
</div>
i.e. to have the last n items wrapped in an another element.
It might seem like a strange request and I understand it would be trivial to achieve this using two ng-repeat directives. However, it needs to be a single ng-repeat so that I can move items in and out of the wrapper without them being added and removed from the DOM (in a manner described here).
What I'm trying to achieve is a news-ticker style scrolling effect by giving the .wrapper element overflow:hidden and using javascript animate the top position of the child elements. To be honest I'd rather not have to have a wrapper element at all but I'm not sure there is any other way to achieve the scrolling effect I require. Perhaps manipulating the clip property to achieve the effect could work but I'm not entirely sure.
So it is possible to apply a wrapper element to some items in an ng-repeat?
Unfortunately, when you change the parent of an element in the visual tree, it must be removed and re-added. Among other problems, consider how styling rules might be applied differently depending on who is whose parent. It's also a pretty expensive thing to do in an animation.
Disappointing? Perhaps.
But reading through your use case, I think you'll find Angular is very good at handling this sort of animation with just a tiny bit of code. I've included a fiddle for you to play with:
http://jsfiddle.net/wjxgcb0k/
It's very easy to create and bind your own layout to the elements on the page. We'll first use ng-repeat to spit out one row per item:
<div class="container" ng-app ng-controller="Foo">
<div class="item"
ng-repeat="item in items"
ng-style="{'top': item.top + 'px'}">{{item.name}}
</div>
</div>
Because we want to handle our own layout, each item in the container will be position: absolute. See how we bind the top to item.top + 'px'? All we need to do is adjust those top values in an animation loop. I'm going to use requestAnimationFrame because it's my go to tool for manual animation, but you can use css transitions or animations if you are more comfortable.
I'll initialize the top values in the controller. That's what it's for, holding state:
$scope.items.forEach(function(item, idx) {
item.h = height;
item.top = idx * (height + margin);
console.log(item);
});
And then I'll set up an animation loop:
var tick = function() {
$scope.$apply(function() {
$scope.items.forEach(function(item, idx) {
item.top -= velocity;
if (item.top < -(height + margin)) {
item.top += $scope.items.length * (height + margin);
}
});
});
requestAnimationFrame(tick);
};
And then kick off the whole thing:
requestAnimationFrame(tick);
Some neat improvements you can make with this:
Consider only animating as many items as will fit on the page, rather than all of the items in the collection. Performance will thank you.
Instead of relying on the $apply to propagate changes to the Dom, directly manipulate the style yourself. This can improve animation performance.
When the if condition fires and we reset the item to the bottom of the ticker, we might check to see if there is different content to be put into the ticker item. This way you might have a live updating ticker that changes over time.
Try your hand at making this all work horizontally. Or perhaps adjust the opacity as top approaches zero or the bottom half of the list.
I hope this helps, and I hope that the small amount of code needed to do this will encourage you to leave behind the notion of relying on the HTML to do your layout for you.
Technically, the answer is no because the wrapper exists outside the scope of the ngRepeat. So it's not possible to set conditions on the wrapper based upon a property of each item.
You can on the other hand still reproduce the same markup. It just requires filtering and some creative thinking.
<div class="container">
<!-- items with wrapper = false are outside wrapper -->
<div ng-repeat="item in items | {'wrapper':false}">{{item}}</div>
<div class="wrapper">
<!-- items with wrapper = true are inside wrapper -->
<div ng-repeat="item in items | {'wrapper':true}">{{item}}</div>
</div>
</div>
I actually don't know whether it is possible to wrap N items inside block using ng-repeat. But what can be done is to apply .wrapper class to every element with index higher than 'some value'. That can be done using ng-class and $index.
edit:
hope I understood how new-ticker works. If not, sorry for the wrong answer.
plnkr
<div ng-repeat="op in options" ng-class="{wrapper: $index > limit}">{{op.title}}</div>
in the sample you can change model value of 'limit' variable to change number of visible items.
or better yet. If the items inside of the wrapper should "be appearing", maybe easiest approach would be
ng-hide="$index > end || $index < start"
This would hide items at the start and at the end. Manipulating values of 'start' and 'end' would create the effect.
Can you achieve this using CSS and nth:child to access the last two elements? You can make the visibility: hidden, for these two nodes in the ng-repeat. You can even do CSS animation with delays.
Related
I am very new to CSS..
I would like to know if there is a way to dynamically position a div element based on the length of the previous div element.
Example:
<div id="A">
</div>
<div id="B">
</div>
<div id="C">
</div>
If these div elements were full width each and i want one to come after the other based on the length of the previous one without adjusting the length on the top property myself.
Is this possible or is there any top value that can be used?
Thank you so much.
As of now, there is no way that you can style a div (or any other element for that matter) dynamically on the basis of a previous element. But you have some options here -
either you can use Javascript to manage the styles of elements dynamically, or,
you can use CSS FlexBox Model or CSS Grid to position and change height/width of elements, which is highly recommended and super powerful for making any layout of your choice.
Here are some places which I used to learn flexbox -
freeCodeCamp flexbox tut
Brad Traversy flexbox crash course
Now for CSS grid-
Brad Traversy CSS Grid
freeCodeCamp CSS Grid
this is my first question!
I'm working on a healthcare app and the code is very dynamic, sometimes I have 1 column and sometimes I've 4 so I'm using an incremental Id selector (i)
Inside this container I've divs that can be 1 to 4...
if there are only one the height might be 100%, if it is three 33%.. and so on..
What I need is to fit the content div to his parent container (i).
The problem is that I need and specific function for each column
It cant be solved by CSS because my clients are in a very old version of IE
<div id="column-1"><!--"1" is dynamic -->
<div id="row-1"></div>
<div id="row-2"></div>
<div id="row-3"></div>
<!-- number of rows is dynamic -->
</div>
PS: tomorrow I'll edit with the real code, and sorry for my english ;)
Fix the DOM height and then use 'overflow-y' CSS property to get scroller, if your
list grows.
column-1 {
height: 110px;
overflow-y: overflow;
}
So I have a bit of a head-scratcher on my hands.
I'm using ng-repeat to output a list of items that are positioned inside a widget.
The widgets are resizable, with three available sizes. The first two are only wide enough to have one column of elements, so displaying them in the correct order is trivial. However, when the widget is expanded to twice width, more elements are displayed at 50% of the width. The elements are arranged by default using display:inline-block; float: left which means that I end up with the ordering going from left -> right, whereas I need them to be ordered vertically.
Initially I was performing two ng-repeats with a column wrapper around each, using splice to split the results, however this causes problems with ordering, because each set of elements is only ordered relative to its container column.
So here's some stripped down code from one of the widgets:
<div class="item review" ng-repeat="review in reviews | orderBy:'date':true | limitTo:14">
<div class="header">
...
</div>
<div class="body">
<div class="comment">
<p>{{review.comments}}</p>
</div>
</div>
I would quite happily use ng-if alongside an $index comparison, something like ng-if="($index+1)==7" on the element I'd like to insert before a group, but since the ng-repeat is on the items themselves, I can't wrap them with that method.
I've tried various ideas with no real success so far. There are things I can do outside of Angular but it's messy and I would like to keep in within Angular, I'm sure what I'm after is possible. Can somebody save me?
EDIT: Illustration of what I mean
working plunk:
http://plnkr.co/edit/lP3vc57k98gPUEbShHWT
You can use css3 column-count or its shorthand columns:
ul {
-webkit-column-count: 2; /* Chrome, Safari, Opera */
-moz-column-count: 2; /* Firefox */
column-count: 2;
}
I am using ngInfiniteScroll to enable infinite scrolling on my website. In order to get it to work I have had to set the height of the outer div to a value as shown below. If I don't do this the Infinite Scroll feature is triggered
<div style="height: 1px">
<post post-item="item" feed-items="items.feed" feed-name="feedName" ng-repeat="item in items.feed"></post>
<a style="bottom-padding 7%" infinite-scroll="nextPosts()" infinite-scroll-distance="1" href ng-click="nextPosts()" class="show-more">No more posts to show </a>
</div>
However, setting the height:1px kind of screws up my css styling and I feel like it is technically cheating, especially since I have to do the bottom-padding on the
Does anybody know a way I can get the Infinite Scroll to not be triggered on all scrolling events without using the style="height: 1px
I have already looked at this post but it has not really helped. How do you keep parents of floated elements from collapsing?
Thanks!
http://binarymuse.github.io/ngInfiniteScroll/documentation.html
Typically, you will use the infiniteScroll directive on an element
that contains another element that uses ngRepeat to show a series of
elements based on an array (or object); the expression you pass in to
the infinite-scroll attribute will generally add additional elements
to the array, expanding the ngRepeat.
maybe you could try the base example:
<div infinite-scroll="nextPosts()">
<post post-item="item" feed-items="items.feed" feed-name="feedName" ng-repeat="item in items.feed"></post>
</div>
I have css animations working based on the ng-show directive in Angular.
I'm trying to add ng-if directives to help me reduce the number of DOM elements in my page by removing hidden parts of it.
<div class="some classes" ng-show="isActive()" ng-if="isActive()">
<div class="other elements">
...
</div>
</div>
The problem is that the ng-if removes the element before it gets the chance to disappear "nicely".
The animation is a rollingUp/Down sort of animation and the outer div has a variable height dependent on the content of the inner div. I tried moving the ng-if down, but the problem stays the same as the height suddenly becomes 0 once the ng-if is true.
What would be the (best) strategy to achieve this? My main goal is to reduce the number of elements without sacrificing the animations.
(I'm using angular 1.2.0)