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.
Related
I'm making a forum web site(html+Vue3+php+sql), and there have a post system.
Mostly, when we need to custom dropdown object, it always needs to initial the fake dropdown object with js. (or have the other way that IDK)
for example, I'm using the css framework「Tocas-UI」,and I got the some html objects below:
<div class="ts floating dropdown icon button">
<i class="vertical ellipsis icon"></i>
<div class="menu">
<div class="item">Add</div>
<div class="item">Edit</div>
<div class="item">Share</div>
</div>
</div>
And <div class="ts floating dropdown icon button">...</div> can be used js ts('.ts.dropdown:not(.basic)').dropdown(); to make it be a "dropdown" object.
Although it can only be used once to initial every object which have class .ts.dropdown, but I'm using the js framework Vue3, and when I using v-for to render every post (have one fake dropdown object in every post), it's not working on the object which are be created after I call the function .dropdown().
So I was tried to put ts('.ts.dropdown:not(.basic)').dropdown(); into loop, it stupid and also not working.
Here is the code in the v-for loop:
<template v-for="post in posts">
<div class="item">
<div class="ts mini circular image">
<img src="default.png">
</div>
<div class="content">
<div class="header">{{ post.poster.nickname || post.poster.username }}</div>
<div class="middoted meta">
<a>#{{ post.poster.username }}</a>
<div>{{ Init.timeToStatus(post.datetime) }}</div>
</div>
</div>
<div class="actions">
<div class="ts separated secondary icon buttons">
<div class="ts floating dropdown icon button">
<i class="vertical ellipsis icon"></i>
<div class="menu">
<div class="item">Add</div>
<div class="item">Edit</div>
<div class="item">Share</div>
</div>
</div>
</div>
</div>
</div>
</template>
So the question is... How can I make those fake dropdown object be initial with ts('.ts.dropdown:not(.basic)').dropdown(); even have new post be added after page loaded.
I have an html which contains many (80) small boxes with some info. All these boxes are displayed in grid.
All these boxes and tags are identical just Text is different inside each block. Each box has lets say 3 values: name, email and mobile number.
I want to swap the position of email and mobile number. It will take so much time to do manually. Is there any way or automatic process or tool by which I can just change one block and it will be applied to all others?
Code of boxes:
<div class="row gutters-40">
<div class="col-12 col-md-4 col-sm-4">
<div class="thumbnail">
<h4 class="service__title">Private limited Registration</h4>
<h2 style="font-weight: bold; color: purple; font-family: sans-serif;"><i class="fa fa-rupee"></i> 7500 Rs</h2>
<h5 class="doctype">Documents Required</h5>
<div class="docs">
<ul>
<li>Aadhar card & Pancard of Directors</li>
<li>Photo of Directors</li>
<li>Address Proof of Directors</li>
<li>Address Proof of Company address</li>
<li>Qualification & occupation of directors</li>
<li>Bank statement of Directors</li>
</ul>
</div><!--docs ends here-->
<div class="price_time">
<p class="service__paragraph" align="left" id="price"><i class="fa fa-info-circle"></i> Know More</p>
</div>
</div><!--Thumbnail ends here-->
</div>
<div class="col-12 col-md-4 col-sm-4">
<div class="thumbnail">
<h4 class="service__title">limited Liability partnership</h4>
<h5 class="doctype">Documents Required</h5>
<div class="docs">
<ul>
<li>Aadhar card of Directors</li>
<li>PAN Card of Directors</li>
<li>Passport size photo of Directors</li>
<li>Residential address proof of Director</li>
<li>Registered Office address proof</li>
<li>Appointment of auditor (ADT-1)</li>
</ul>
</div>
<div class="price_time">
<p class="service__paragraph" align="left" id="price"><i class="fa fa-rupee"></i> 7199</p>
<p class="service__paragraph" align="right" id="days"> <i class="fa fa-clock-o"></i> 10-15 days</p>
</div>
</div><!--Thumbnail ends -->
</div><!--box ends-->
</div> <!--row ends-->
As you can see my price is in last div inside p tag. I want it above h5 tag.
I have almost 80 such boxes. With multicursor and moving line up it will still take time.
I thought find and replace with regex can work but I can't evaluate how to do that. There is div class = docs is between these two elements which I want to swap, so I haven't figured out should such regex look like.
Well, I guess this is somewhat too custom task to find an out-of-box tool, so I'd suggest to write simple one of your own. Here's an outline:
open you page in browser
open dev tools Console, run a script to modify html
open dev tools Elements, copy the modified html bit
substitute the boxes part of html in your page with the modified one
if your html is modified also by some other JS, in step 1. use instead a dedicated html containing just the piece that's of interest.
Now, the script should do the following:
find all the boxes (document.querySelectorAll)
for each of them, do the necessary operations with elements, like
const elementToMove = ...
const destinationElement = ...
destinationElement.appendChild(elementToMove)
const elementToRemove = ...
elementToRemove.remove()
I have a website that i am trying to personalize and I am trying to use the AnimatedModal.js framework. I have been able to display some content in one modal, but when it comes to make several modal it gets tricky, because there is just one ID. My question i, how can i use the same ID and change the content for other modals(demo03,demo04..etc.), in order to personalize each.
I will put some code in order to understand the problem
I have been reading the documentation but I am still stuck in this problem.
<!-- single work -->
<div class="col-md-4 col-sm-6 ads graphics">
<a id="demo02" href="#animatedModal" class="portfolio_item">
<img src="img/portfolio/03.jpg" alt="image" class="img-responsive" />
<div class="portfolio_item_hover">
<div class="portfolio-border clearfix">
<div class="item_info">
<span>Should open here </span> <em> ads / Graphics </em>
</div>
</div>
</div>
</a>
</div>
<!-- end single work -->
Then I have the demo where it displays the content of the modal, where it has the #animatedmodal ID
<div id="animatedModal" class="popup-modal ">
<!--THIS IS IMPORTANT! to close the modal, the class name has to match the name given on the ID -->
<div id="btn-close-modal" class="close-animatedModal close-popup-modal">
<i class="ion-close-round"></i>
</div>
<div class="clearfix"></div>
<div class="modal-content ">
<div class="container">
<div class="portfolio-padding" >
Hello World
</a>
</div>
</div>
</div>
</div>
this is my Js file where there is just one element assigned to it, to avoid showing the same content into all different classes.
$("#demo02").animatedModal();
I don't think it can be done without hacking the plugin.
As a matter of fact, the script jQuery.animatedModal ALWAYS TARGETS the page element which has id="animatedModal"
You can see the plugin source code here:
https://cdn.jsdelivr.net/npm/animatedmodal#1.0.0/animatedModal.js
...
//Defaults
var settings = $.extend({
modalTarget:'animatedModal',
...
Here is the AnimatedModal reference:
https://joaopereirawd.github.io/animatedModal.js/
At the bottom of the page, I can't see any OPTION regarding how to specify a different target, all options are about styles and animation features.
At this point, I think the only way to allow multiple modals on the same page is to rewrite the plugin, but I'm pretty sure you don't want to choose this way.
I am using Socialite Social Network Laravel Script by BootstrapGuru. You can see the demo here to understand exacly what's happening. Try to open a chat window with anyone and enter some text and some symbols (for example: "how are you?") You will see the text now is "?how are you":
To solve that I am still waiting for the support answer but I am trying to solve it from my end.
I see this on my website's Vue Panel:
And this is how it's on the front end side.
<ul class="list-unstyled chat-conversation-list">
<li class="message-conversation" v-bind:class="[({{ Auth::id() }}==message.user.id) ? 'current-user' : '', ]" v-for="message in chatBox.conversationMessages.data"> <!-- Messages -->
<div class="media">
<div class="media-left">
<a href="#">
<img v-bind:src="message.user.avatar" alt="images">
</a>
</div>
<div class="media-body ">
<p class="post-text" v-html="message.body"></p>
</div>
</div>
</li>
</ul>
I've also tried to use the same way to get the message:
<div class="media-body ">
<p class="post-text">
#{{ message.body }}
</p>
</div>
But it is the same. Something important to mention maybe, is that the message is well displayed in the "receiver" window.
So, any clues to show the symbols where should be?
You can override it with some css:
.media-body .post-text{direciton: ltr;}
I need to start working in a directive.
I have 5 different views where the html is exactly the same, so I need to do the directive in order to minimize my HTML.
<div class="list betslip-item">
<div class="item betslip-header"
ng-class="slip.active == '1' ? 'betslip-header-active' : 'betslip-header-inactive'">
<div class="row">
<div class="col slip-name" ng-click="openMoreInfoSlip(slip)">
{{:: slip.teamName}}
</div>
<div class="col-30">
<a class="button-size select-button"
ng-click="swapLineSelection(slip)">
<i class="fa" ng-class="slip.active == '1' ? 'fa-check-circle' : 'fa-plus-circle'"></i>
</a>
<a class="button-size close-button"
ng-click="removeSlip(slip)"><i class="fa fa-times"></i>
</a>
</div>
</div>
</div>
<div class="item item-input" ng-class="(slip.lines.length > 1) ? 'item-select' : ''">
<div class="input-label">
{{:: slip.nss}} - {{::slip.gameDate}}
</div>
<span class="pick-label"
ng-init="currentLine = getCurrentLine(slip)"
ng-if="slip.lines.length < 2">{{:: currentLine.pick}}</span>
<select
ng-if="slip.lines.length > 1"
ng-init="currentLine = getCurrentLine(slip)"
ng-model="currentLine"
ng-options="line as line.pick for line in slip.lines"
ng-change="updateSelectionLine(slip, currentLine.pointsBought)">
</select>
</div>
</div>
That is the HTML I am talking about, I should include css classes and everything, is that possible?
Any help is very welcome.
You don't need to use a custom directive. If all you want to achieve is to be able to reuse some html you can always put it into another template and use it by ng-include:
<ng-include src="'/templates/reusableComponent.html'"></ng-include>
Reusing the same HTML file as 5 different views is possible but you will need to place all the value or data that you require to show on all the 5 pages in one and use javascript or jquery to display according. so instead of doing this creating 5 views would be better, it isn't using up any server processing time, only memory and since it's just 5 views i dont think it would affect it so much also.
i've also included a link of the Angular project that i was working on below, which had aorund 2-3 views so incase you require as to go about that, with ng-route etc.
GitHub link for AngularJS Project
Hope this helps