I have the following code:
<div ng-repeat="item in selectedCategory">
<div class="repertoire-composer">
<div data-toggle="collapse" data-target="#compositions{{$index}}"
class="repertoire-composer-title">
<span class="fa fa-chevron-right"></span>
{{item.composer}}
<span class="fa fa-plus"></span>
</div>
<div id="compositions{{$index}}" ng-repeat="composition in item.compositions" class="repertoire-compositions collapse">
<div class="repertoire-composition">
{{composition}}
<span class="fa fa-remove" title="Remove"></span>
</div>
</div>
</div>
</div>
So what I basically want is to have a tree of multiple .repertoire-composer divs, each of which with one or many child .repertoire-composition inside its .repertoire-compositions div. So in the second repeat, angular should create one or more .repertoire-composition divs. However, what it seems to do is create more divs of the one the directive is on. Hope I've been explicit enough, here's the unwanted result:
So basically, why is my .repertoire-compositions div getting duplicated?
EDIT:
I was misunderstanding ng-repeat. This is the intended behaviour of it: to repeat the element the directive is on. Thanks for the clarification.
.repertoire-compositions is not being duplicated it's being actually ng-repeated by this part of your template:
<div id="compositions{{$index}}" ng-repeat="composition in item.compositions" class="repertoire-compositions collapse">
I guess it's because of the second ng-repeat on item.compositions, can you provide a set of data ?
I'm not sure but you can try this in the inner repeater:
<div id="compositions{{$index}}" class="repertoire-compositions collapse">
<div class="repertoire-composition" ng-repeat="composition in item.compositions">
{{composition}}
<span class="fa fa-remove" title="Remove"></span>
</div>
</div>
Related
I need one help. I want to add some animation effect while click on the link using Angular.js. I am explaining my code below.
<body ng-controller="demoController">
<div class="panel-group accordionsection" id="accordion" style="width:100%;">
<div class="panel panel-default">
<div class="mainaccordion_title-width panel-heading" role="tab" ng-click="active = !active">
<a class="panel-title sky-blue-light" role="button" data-toggle="collapse" data-parent="#accordion" aria-expanded="true">
<i class="glyphicon glyphicon-plus"></i> Description
</a>
</div>
<div id="collapse1" class="panel-collapse collapse in" >
<div class="panel-body" ng-hide="active">
<p style="white-space:pre-wrap;">
Demonetization of currency means discontinuity of the particular currency from circulation and replacing it with a new currency. In the current context it is the banning of the 500 and 1000 denomination currency notes as a legal tender.
</p>
</div>
</div>
</div>
</div>
</body>
script.js:
var app=angular.module('demo',[]);
app.controller('demoController',function($scope){
$scope.active=false;
})
Here when i am clicking on the link the below div is hiding/showing but here I need to give some transition effect like slide up/down. Here is my full plunkr code. Please help.
You can use ng-animate to achieve this
Here is the documentation with a working example
http://www.nganimate.org/
You can also create your own custom css animation class
and use $animate to add or remove your css class on the click action
I am working on Angular Js web app using Laravel 5.2 as API and I have a big issue in the performance specially in the load time of the home page.
I have a request that get 5 sections from database, each section has one or tow items and each item has many comments. I loop through sections then items then comments to render it via nested ng-repeat directive.
I also have a list of filters and another of friends each has its own request and ng-repeat to render it as well.
Is this considerable as huge data to use over Angular JS, How I can enhance the performance of it?
I also have about 20 modules in the dependencies of the main module, Is there a better way to load this module only when I need them?
Notes:
Angular Js version 1.5.1
Angular Js and jQuery loaded via CDN
all other js files are concatenated by GULP.js in one file and it about 66000 lines of code
there is 25 module in the dependencies array of the main module
the time of load is about 25-35 seconds
here is code of my home page section that cause the issue:
<div ng-class="{'col-sm-6':currentUser,'col-sm-9':!currenUser}" fc-loading="getCoupons" class="allCoupons lbm">
<section class="fc-section text-center fc-direction" ng-init='current="all"'>
<button type="button" class='btn btnp btnm0 srm' ng-class='{"btnr":current=="all"}' ng-click='updateCoupons();current="all"' name="button">كل الكوبونات</button>
<button type="button" class='btn btnp btnm0' ng-class='{"btnr":current=="my"}' ng-click='updateCoupons("user");current="my"' name="button">الكوبونات المخصصة لك </button>
</section>
<div ng-repeat='(key,section) in coupon_sections' class="{{key}} fc-programs">
<div class="header">
<div class="clearfix">
<h3 class="pull-left">{{::section.type}}</h3>
<ul ng-init="active = 'newset'" class="pull-right">
<li ng-click="sortCoupons(1,key); active='newset'" ng-class="{active:active == 'newset'}"><span>{{::'sort_by.newest'| translate}}</span></li>
<li ng-click="sortCoupons(2,key); active='oldset'" ng-class="{active:active == 'oldset'}"><span>{{::'sort_by.oldest'| translate}}</span></li>
<li ng-click="sortCoupons(3,key); active='popular'" ng-class="{active:active == 'popular'}"><span>{{::'sort_by.popular'| translate}} </span></li>
</ul>
</div>
</div>
<div class="fc-section">
<p>
{{::section.description}}
</p>
</div>
<div class="lucky_hour centerContainer" ng-if='key =="luckyHour"' ng-init='getTimeRemaining("December 31 2016 23:59:59 GMT+0200")'>
<div class="centerContent">
<ul>
<li class="hh" ng-repeat='hour in hours track by $index'>{{hour}}</li>
<li class="dots">:</li>
<li class="mm" ng-repeat="minute in minutes track by $index">{{minute}}</li>
<li class="dots">:</li>
<li class="ss" ng-repeat="second in seconds track by $index">{{second}}</li>
</ul>
</div>
</div>
<section class="fc-section text-center" ng-if="!section.coupons.length ">
<p class="title">{{::'no_coupons'| translate}}</p>
</section>
<div ng-repeat="coupon in section.coupons">
<fc-coupon ng-init='showCarousel();'></fc-coupon>
</div>
<div class="text-center" ng-if="section.coupons.length ">
<button class="btnloadmore icon-plus-circled btni lbm" ng-click="loadMore(key)">{{::'load_more'| translate}}</button>
</div>
</div>
</div>
here is the fc-coupon template
<div class="fc-coupon lbm" id='{{coupon.coupon_data.slug}}'>
<div class="header bb">
<div class="rippon">
{{::coupon.coupon_data.type}} </div>
<img style="background-image: url('{{::absolute_url+'images/brands/100x100/'+ coupon.brand_data.logo }}')" />
<h3>{{::coupon.brand_data.name}}</h3>
<fc-stars rating="{{::coupon.coupon_data.rate}}" ></fc-stars>
<div class="pull-right">
<!-- <label>{{::'available_for'| translate}}</label> -->
<div class="fc-badge sPadges" ng-repeat="customer in ::coupon.coupon_data.users_type">
<img uib-tooltip="{{:: 'available_for' | translate}} {{::customer.name.name}}" ng-src="{{::absolute_url}}/images/customer-types/50x50/{{customer.image}}">
</div>
</div>
</div>
<div class="sp" ng-if='key !="challenges"'>
<h4>{{::coupon.coupon_data.title}}</h4>
<p hm-read-more
hm-text="{{:: coupon.coupon_data.description }}"
hm-limit="100"
hm-more-text="{{::'read_more'|translate}}"
hm-less-text="{{::'read_less'|translate}}"
hm-dots-class="dots"
hm-link-class="links" class="sbm mtp "></p>
</div>
<div class="sp mtp" ng-if='key =="challenges"'>
<p>
<i class="fc-red icon-award"></i> <span class="fc-purple srm">التحدي </span>
{{::coupon.coupon_data.challenges.name}}
</p>
<p>
<i class="fc-red icon-gift"></i> <span class="fc-purple srm">الجائزة </span>
<span>{{::coupon.coupon_data.type}}</span>
</p>
</div>
<div class="couponImage">
<div class="fc-overlay centerContainer">
<div class="centerContent">
<i ng-if="coupon.coupon_data.in_wallet" class="icon-heart"></i>
<i ng-if="!coupon.coupon_data.in_wallet" ng-click="addToMyCoupons(coupon.coupon_data)" class="icon-heart-empty"></i>
<label>{{::coupon.coupon_data.no_of_users_in_wallet }}</label>
</div>
<div class="couponFooter">
{{::'details'| translate}}
<ul class="socials">
<li><span ng-click="shareCoupon(coupon.coupon_data.slug,'facebook')" spantarget="_blank" class="icon-facebook"></span> </li>
<li><span ng-click="shareCoupon(coupon.coupon_data.slug,'twitter')" class="icon-twitter"></span> </li>
<li><span ng-click="shareCoupon(coupon.coupon_data.slug,'gplus')" class="icon-gplus"></span> </li>
</ul>
</div>
</div>
<img ng-src="{{::absolute_url}}/images/coupons/747x390/{{coupon.coupon_data.image}}"/>
</div>
<div class="couponDetails sp">
<div class="owl-carousel mbm">
<div class="item " ng-repeat="friend in ::coupon.coupon_data.coupon_users">
<div style="background-image: url('{{::absolute_url}}/images/users/75x75/{{friend.picture}}')" class="roundImages mProfilePics"></div>
</div>
</div>
<div ng-init="coupon.coupon_data.isMapCollapsed =true;coupon.coupon_data.isMapOpened=false">
<div class="clearfix bb mbp mtp bt sbm" >
<div class="pull-left srm" ng-click="openBranches(coupon.coupon_data)">
<i class="icon-location"></i>
<label>{{::'branches'| translate}} {{::coupon.coupon_data.no_of_branches}}</label>
</div>
<div class="pull-left">
<i ng-class="{'icon-cancel':coupon.coupon_data.feed_w_estafeed_id ==null,'icon-ok':coupon.coupon_data.feed_w_estafeed_id != null}"></i>
<label>{{::'benefit'| translate}}</label>
</div>
<div class="pull-right">
<i class="icon-share"></i>
<label for="">{{::'share'| translate}} :</label>
<i class="icon-facebook" ng-click='shareCoupon(coupon.coupon_data.slug,"facebook")'></i>
<i class="icon-twitter" ng-click='shareCoupon(coupon.coupon_data.slug,"twitter")'></i>
</div>
</div>
<div class="fc-map " uib-collapse="coupon.coupon_data.isMapCollapsed" >
</div>
</div>
<div class="clearfix">
<div class="buttons add_to_my_coupones pull-right">
<button ng-if="coupon.coupon_data.in_wallet " type="button" class="btn btnr btnp btni" ng-click='couponConditions(coupon.coupon_data.slug)'>{{::'in_my_coupons'| translate}}</button>
<a ng-if="!coupon.coupon_data.in_wallet && key=='challenges' " href='{{absolute_url}}/{{lang}}/coupons/{{coupon.coupon_data.slug}}/{{coupon.coupon_data.challenges.title}}' class="btn btnr btnp btni" >ابدء التحدي</a>
<button ng-if="!coupon.coupon_data.in_wallet && key!='challenges'" analytics-on="click" analytics-event="addToMyCoupons" analytics-properties="{coupon_id:{{coupon.coupon_data.coupon_id}}}" ng-click="addToMyCoupons(coupon.coupon_data)" class="btn btnr btnp btni icon-heart">{{::'add_to_my_coupones'| translate}}</button>
</div>
<div class="pull-left">
<i class="icon-calendar"></i>
<label class="srm">{{::'available_to' | translate}}</label>
<!-- <fc-countdown date='coupon.coupon_data.available_to.date'></fc-countdown> -->
</div>
</div>
<div class="fc-couponComments" >
<!-- <span>{{coupon.comments.length}} {{::'comments'|translate}}</span> -->
<button ng-init="isCollapsed = true;" ng-click="isCollapsed = !isCollapsed;"><i class="icon-comment-1"></i> </button>
<ul uib-collapse="isCollapsed" >
<li>
<img src="{{ currentUser?absolute_url+'/images/users/50x50/'+currentUser.picture:absolute_url+'/images/user.png'}}"/>
<div class="userComment">
<textarea ng-model="addComment.newComment" ng-enter="sendComment(coupon)"></textarea>
</div>
</li>
<li ng-repeat="comment in coupon.coupon_data.comments track by $index">
<img ng-src="{{::absolute_url}}/images/users/50x50/{{::comment.user.picture}}"/>
<p>
<label>{{::comment.user.firstname}} {{::comment.user.lastname}}</label>
<span> {{::comment.comment}} </span>
<label>{{::comment.created_at}}</label>
</p>
</li>
</ul>
</div>
</div>
</div>
home controller js :
var url = user ? "user/home/coupons" : "home/coupons";
fcDB.query(url, "GET", data).success(function(res) {
console.log('home', res);
$scope.coupon_sections = res;
});
You seem to have way too many bindings. Back in the days of Angular 1.2, I remember it was a rule of thumb to have up to 2000 bindings on the page. That's gone up by now, but I doubt it scaled exponentially.
You have 5 sections, each having numerous ng-click bindings, several nested ng-repeats which multiply this, then undetermined number of comments per coupon.
You can deal with all this in several different ways. Some of them:
join the click listeners,
lazy-load the nested comments, hours etc, if possible,
also render as html those social shares,
pre-translate content,
one-time-bind more then you do now,
reduce dependencies.
E.g. you can have one top-level ng-click instead of a dozen per section (+ possibly more in those fc-coupon repeats). So create a single top-level ng-click, and have that function find out its intended target and act accordingly. I'd say you can shave off a little bit of time, but not significant there.
Lazy-load the nested repeats - ie. don't render them immediately. When you initially render the sections, you go on and render those hours, minutes and seconds. Dunno why, but it seems like you do really a lot of those. Have those not even show until the main content is rendered. Just one way to do that is to hide the whole block with an ng-if switch and when you know you've loaded and rendered the initial 5 sections, flip one (or all) the switches and wait for those to be rendered too. You can even leave those hidden ("Loading comments...") until you know they're rendered with a single css flip. Furthermore, maybe you can do them one-at-a-time. First fully resolve the top section, then the second, then the third etc.
Remember, a lot of performance is in perceived performance.
I see you have many of the social shares that you bind to a function. Why not render those as simple HTML directly? Depending on those coupons, you can again remove a lot of bindings.
I see you use ng-translate - which introduces yet more bindings. If you know the user's language in advance, (ie. settings), maybe you can have pre-translated per-language template? E.g. If the user initially chose english, he'd load a page called home-en.html and fc-coupon-en.html instead of one generic one. Don't know if this is viable though, but considering you have a lot of nested content, it might prove useful. And you don't even have to do it by hand, I bet you can write a one-time script where you write your source template like this, and the additional build-step would generate per-language template for all this on build time.
Also try to one-time bind the nested content. E.g. ng-repeat="hour in ::hours" might help a bit. Depending on how many times you have to bind hours, minutes and seconds, there may be thousands of bindings to introduce into the page.
Reduce dependencies if you can. E.g. I see you use a "read-more" module. Maybe you can do without it. Just use a css elipsis. And have a second click listener that finds the targeted section and extends/collapses on demand. I bet that's what the original module does anyway, but yours would be one-time top-level click listener, and not a per-item angular binding that has to get sorted out.
Now, these are all guesses, because you don't show how many actual items and what exactly does it do. So take your pick on one of the suggestions, and try it or open up a new question with more details on it.
Personally, I'd think lazy-loading everything will grant you the most "perceived" performance.
I am having a toolbar directive using angular material, I am using a transclude at the end of the directive and I want the transcluded element to come at the right end of the banner. But what ever I do, it wont work, its coming after the elements of the directive?
The code for template is like below.
<md-toolbar>
<div class="md-toolbar-tools">
<h2>
<a ui-sref="#">Test - <span ng-bind="header.title"></span></a>
</h2>
<span flex="5"></span>
<div ng-transclude></div>
</div>
</md-toolbar>
Please find the example here. I wan to move Test123 to the right most end of the toolbar. Please help me
Try this as your banner.html:
<md-toolbar>
<div class="md-toolbar-tools">
<h2>
<a ui-sref="#">Test - <span ng-bind="header.title"></span></a>
</h2>
<span flex="5"></span>
<div ng-transclude style="position:absolute;right:0;top:16"></div>
</div>
</md-toolbar>
Not sure if this was what you were looking for but it does do what you asked.
You are in the right track.
In your banner.html you need to modify the <span flex="5"> to <span flex> this will make it expand all the way to the right instead of just 5 units and push everything inside the ng-transclude with it.
<md-toolbar>
<div class="md-toolbar-tools">
<h2>
<a ui-sref="#">Test - <span ng-bind="header.title"></span></a>
</h2>
<span flex></span>
<div ng-transclude></div>
</div>
</md-toolbar>
Also notice I cleaned up the index.html file so you can easily add content in between the banner tags.
<banner header-title="Title 1 Test">
<span>Test 123</span>
</banner>
You can see the full solution here
If you are interested, you can find more information about the md-toolbar directive in the official documentation.
Even more values are pushed into the array:
.controller('MyCtrl', function($scope) {
$scope.mottos = [];
$scope.mottos.push('One');
$scope.mottos.push('Two');
$scope.addMotto = function(motto) {
$scope.mottos.push(motto);
}
});
and
<span ng-repeat="motto in mottos"> {{motto}} </span>
ng-repeat does not show all elements as expect: codepen, why?
...
HTML
<ion-content>
<button class="button button-positive" ng-click="addMotto('More')">
<i class="icon ion-quote"> Click me to add more ...</i>
</button>
<div class="card item-text-wrap">
<div class="item">
{{mottos.length}}
<span ng-repeat="motto in mottos"> {{motto}} </span>
</div>
</div>
</ion-content>
From the documentation:
By default, ngRepeat does not allow duplicate items in arrays. This
is because when there are duplicates, it is not possible to maintain a
one-to-one mapping between collection items and DOM elements.
If you do need to repeat duplicate items, you can substitute the
default tracking behavior with your own using the track by
expression.
So if you update your code as follows, things work as expected:
<span ng-repeat="motto in mottos track by $index">
{{motto}}
</span>
Instead of this
<span ng-repeat="motto in mottos"> {{motto}} </span>
try something like this
<span ng-repeat="motto in mottos track by $index"> {{motto}} </span>
you can see this in more detail on this page
Your issue occurs because ng-repeat does not have a unique identifier for each object. If you change the code to append the length on each object (more1, more2, more3, etc since lengths/indexes are unique) it works fine. I've seen a similar issue in WPF.
because you need to use $track by
he solution is actually described here: http://www.anujgakhar.com/2013/06/15/duplicates-in-a-repeater-are-not-allowed-in-angularjs/
http://codepen.io/anon/pen/doRKWY
<span ng-repeat="motto in mottos track by $index"> {{motto}} </span>
I have an accordion in my HTML that is dynamically populated as such -
<div class="accordion">
<div class="accordion-panel">
<div class="accordion-heading>
<a data-target="#collapse" data-toggle="collapse">{{x.header}}</a>
</div>
<div id="collapse" class="accordion-body">
....
</div>
</div>
</div>
The problem is that the data-target remains static. So all the open/close buttons only open one piece of the accordion! The solution would be to enumerate the data-target/ids based on $index, but I don't know how to do that.
Is there a way to enumerate attributes in the way that I described? Or is there another solution I can use?
Just replace data-target value with #collepse-{{$index}} and ID of body with 'collapse-{{$index}}'
Hope this might be helpful to you!!