Implementing a page load performance directive in AngularJS - javascript

I am very new with AngularJS so I need quite some pointing in the right direction.
The task is to create some kind of widget that displays how much time it takes from any user action until the requested page finishes rendering.
We are going to be using AngularJS at the presentation layer and the back-end will be Microsoft's Web API.
So I figured I could use the browser's Navigation Timing API and wrap it on an AngularJS directive so I tried this:
angular.module('performanceDirective', [])
.directive('pagePerformance', function(){
return {
restrict: 'AE',
replace: 'true',
template: '<div><label id="loadTimeEllapsed">Total Load Time:{{totalLoadTime}}</label></div>',
scope: {},
link: function (scope, elem, attrs) {
scope.$watch('window.performance.timing', function (newValue, oldValue) {
var timing = window.performance.timing;
var userTime = timing.loadEventEnd - timing.navigationStart;
scope.totalLoadTime = userTime;
});
}
};
});
But it seems that there is something missing because even though I am doing actions that call the back-end the number that gets displayed after the home page loads is never updated.
Is this something that actually would work, provided we fix whatever is failing, or is this a dead end and we need to find another option?
UPDATE
The use of the directive has nothing to it, basically it is just the element thrown on a page:
<body ng-app="myApp">
<div class="navbar">
<div class="navbar-inner">
<ul class="nav">
<li>Some Action</li>
</ul>
</div>
</div>
<div class="row">
<div class="span4"><data-page-performance /></div> <!-- The Directive -->
<div class="span10" ng-view></div>
</div>
</body>
Apparently this directive only works if I refresh the page after I have already navigated to it but if I click on an element that will trigger an action on the AngularJS controller the performance number is completely unaffected.

Related

View is getting initialized again and again

I'm building a dashboard similar to this one Admin Dashboard Theme
I'm implementing the Right Side SideBar as show in the screenshot
My App code structure is something like:
index.html:
<div ui-view=""></div>
main.html
<div ui-view=""></div>
<div ng-include='"app/dashboard/sidebar.html"'></div>
I have done nesting of view in my main.html where I'm injecting different views. Also, since my right side sidebar is fixed, I want it to be common in all the main views. So, I have just included it in my main.html.
Now, the problem is somehow my sidebar.html is getting initialized again and again no matter if I scroll my page down or perform any action inside sidebar. I have verified it by printing console logs for every controller function which are used in sidebar.html view.
This problem is related to my this post: Earlier, I wasn't able to figure out the actual issue.
Following is my controller and jade code:
angular.module('myApp')
.controller('SidebarCtrl', function($scope, $rootScope) {
$scope.message = {};
$scope.addSideBarToggleClass = function() {
console.log("addSideBarToggleClass");
return true;
}
$scope.getStatusClass = function(status) {
console.log("getStatusClass");
return 'is-online';
}
$scope.openChat = function(receiver) {
console.log("openChat");
}
// etc...
});
<aside ng-class="{ 'control-sidebar-open' : addSideBarToggleClass()}"
ng-controller="SidebarCtrl">
<ul>
<li ng-class="{active: isTabSelected('chat')}">
<a data-toggle="tab" ng-click="updateCurrenTab('chat')"></a>
</li>
<li ng-class="{active: isTabSelected('home')}">
<a data-toggle="tab" ng-click="updateCurrenTab('home')"></a>
</li>
</ul>
<div>
<div ng-class="{active: isTabSelected('home')}">
<h3>Recent Activity</h3>
</div>
<div ng-class="{active: isTabSelected('chat')}">
<div>
<h4>Chat {{noOfUsersOnline}}</h4>
<div>Friends
<a href="#" ng-repeat="user in users" ng-click="openChat(user)">
<span ng-class="getStatusClass(user.status)"></span>
{{user.name}}</a>
</div>
</div>
</div>
</div>
</aside>
I see many logs of "addSideBarToggleClass", "getStatusClass" and every time I click on openChat, I see a log of "openChat" and then again "addSideBarToggleClass" and "getStatusClass"
Can anyone please point out what can be the possible problem for this behavior?
You need to familiarize yourself with the concept of a digest loop in Angular.
In short, every time a digest loop runs, all expressions, e.g. {{name}} or ng-show="isActive && isEnabled", that are being "$watched" by Angular are evaluated (sometimes more than once). This means that if you are invoking a function inside an expression:
<div ng-show="isShown()">
$scope.isShown = function(){
console.log("expect to see this text a lot of times");
return true;
};
the function will be executed on every digest loop.
A digest loop runs any time that something in Angular calls $scope.$digest, which by default happens on things like ng-click or ng-change or $http.then, etc..

How to catch an event after all content is loaded when all controllers are initialized inside an ng-repeat in Angularjs?

<!-- Begin page content part of a pageCtrl -->
<div class="container" ng-init="pageInit();">
<div class="template {{block.type}}" ng-repeat="block in pageBlocks" my-repeat-directive>
<div ng-controller="templateCtrl" ng-include src="'partials/components/' + block.type + '.html'" ng-init="initTemplate(block);"></div>
</div>
</div>
Inside my ng-repeat I initialize dynamically some small templates with my templateCtrl passing some data from my pageCtrl scope.
My goal is to be able to execute some javascript after all the content of my templates is loaded and compiled by Angularjs.
But at the moment I cannot find an "after all is compiled" event to manage some simple callback operations.
I tried directives & on-last-repeat operations on the ng-repeat, i tried scope.$watch, $timeout, $scope.$on('$viewContentLoaded')... but any working behaviours.
any idea, suggestions? thanks in advance
after some other attmepts i end up in this solution, that seems to work really fine.
this in the html
<div ng-controller="templateCtrl" ng-include src="'partials/components/' + block.type + '.html'" ng-init="initTemplate(block);" template-directive></div>
and this in javascript
app.directive('templateDirective', function($timeout){
return {
restrict: "EA",
compile: function(element, attributes){
return {
post: function(scope, element, attributes, controller, transcludeFn){
if(scope.$last){
$timeout(function(){
//operations in the callback
}, 250);
}
}
};
}
};
});
do the experts agree?

Can I use a JQuery plugin from within an angularjs directive?

I'm quite new to javascript in general. I've spent the past couple of weeks building a front end with Angular.js.
I have a number of directives I've defined that sit on my page, Angular has been great for this.
Here's what my main page looks like:
<body class="body" ng-controller="OverviewController as overview" font-size:1em>
<sidebar-menu ng-controller="PanelController as panel"></sidebar-menu>
<div id="content" >
<div>
<div class="list-group">
<div class="list-group-item" ng-repeat="site in overview.sites" ng-click="">
<div class="item-heading">
<h3>{{site.name}}</h3>
<p>Address: {{site.address}}</p>
Click Here
</div>
<installationsite-panels ng-controller="PanelController as panel"></installationsite-panels>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function(){
$('.paulund_modal').paulund_modal_box();
});
</script>
</body>
Note the javascript function to call a modal box at the bottom, using this tutorial.
I've spent the past few days trying different tutorials to get modals to work in my webapp, but with no success. I think it's down to my lack of understanding of Angular and Javascript in general.
In any case, I've managed to get this tutorial to work using JQuery, and when I click on the link, the modal opens as expected.
However, I don't want to call this modal from here. I want to call it from a directive that's embedded within the <installationsite-panels> directive in the above code, which looks like this (just a single section shown here):
Device Statuses
<div>
<div class="device-icon-group">
<div class="device-type1-icons" ng-click="panel.showDevices(3)" ng-show="showtype1Red"><img src="img/type1red.png" style="width:50%; height:50%;"/></div>
<div class="device-type2-icons" ng-click="panel.showDevices(3)" ng-show="showType2Red"><img src="img/type2red.png" style="width:50%; height:50%;" /></div>
</div>
<div class="service" ng-click="panel.showDevices(3)" ng-show="showService">
<b>{{panel.getServiceDeviceCount()}} device needs servicing</b>
</div>
<div ng-show="showServiceList">
<device-list-service></device-list-service>
</div>
</div>
The directive <device-list-service> shows a list of items like so:
<div ng-controller="DevicesController as deviceList" font-size:1em >
<div id="device-list-group">
<div id="device-list-group-item" ng-click="" ng-repeat="device in deviceList.devicesService">
<div ng-class="device.status"><img src="{{(device.type == 'type1') ? 'img/type1white.png' : 'img/type2white.png'}}"> </div>
<div class="device-params">
<b>ID: </b> {{device.id}}<br />
<b>Type: </b> {{device.type}}
</div>
<div class="device-params">
<b>Location: </b> {{device.location}}<br />
<b>Action: </b> {{device.action}} <br />
</div>
</div>
</div>
</div>
I want to show the modal when the user clicks on one of the list-group-item 's, and display some data relating to that item.
The modal works fine from the top level in the main app, but I cannot call it from within any of the directives. How can I do this?
Is it possible, or do I need to scrap my JQuery modal and do it the Angular way, which hasn't worked for me for the past few attempts.
Don't use jquery modals. You can, but you shouldn't.
Instead, I recommend using Angular UI, which has a pretty usable modal implementation: https://angular-ui.github.io/
Second alternative: if you don't like Angular UI, then use AngularJS + Bootstrap, and create your own custom directives
Third alternative: Use jQuery.
If you still want to go with the 3rd alternative, despite my advice against it, then here is how you do it:
var app = angular.module('app', []);
app.directive('modal', function($http, $timeout) {
return {
restrict: 'A',
link: function(scope, element, attr) {
$timeout(function() {
element.paulund_modal_box();
}, 0, false);
}
};
});
Usage:
<div modal></div>
Some explanation is needed here.
Why is the $timeout service necessary? jQuery plugins often require the DOM to be fully loaded in order to work properly. That is why most jQuery plugins are wrapped inside of a $(document).ready block. In AngularJS there is no concept of DOM ready, and there is no easy way in AngularJS to hook into the event. However, there is a well-known hack, which is to use the $timeout service. In Angular there are three phases:
1. compile - Angular walks the DOM tree looking for directives
2. Link - Angular calls the link function for each directive to setup watch handlers.
3. Render - Angular updates the views
Using $timeout within the Link function queues the $timeout function to be executed until after the current closure is done executing. It just so happens that the Render phase is within the current closure's scope of execution. Hence, the $timeout function will execute after the render phase, when the DOM has been loaded.
Mixing JQuery and Angular in that way is maybe a little messy, but sometimes you do want to use a well-built component. You could try to find a similar modal in Angular - angular-modal - or you could try and build the component into your Angular directive itself - jQuery Plugins in AngularJS

AngularJS, directive element is null

I'm working on porting a jQuery plugin to AngularJS just because it seems fun.
In the past, when using jQuery, I was using jQuery to manipulate the DOM. So, I have a function in jQuery to load the plugin and in that function is was maniuplating the DOM.
Now, when using AngularJS, I've read that there are directives for that specific purpose, but I don't manage to find the solution.
I have the following html:
<body class="officeui-space-no-margin officeui-space-no-padding">
<!-- Defines the OfficeUI section. In this section, all the contents for the OfficeUI user interface will be written. -->
<div ng-controller="OfficeUIController" id="OfficeUI">
<div class="title officeui-align-center">
<span>Here the title can go.</span>
</div>
<!-- Defines the main holder for the ribbon. -->
<div id="ribbonHolder">
<!-- Render the template for the ribbon. -->
<ng-include src="'Partials/Templates/Ribbon/tabs.html'"></ng-include>
</div>
</div>
<!-- Bottom scripts: Used for Initialization. -->
<script type="text/javascript">
// Initialize the 'ribbonHolder' element as a ribbon.
$('#ribbonHolder').ribbon();
</script>
</body>
You see here that I'm loading a template to render, of which the contents can be found below:
<ul role="tablist" class="officeui-space-no-margin officeui-space-no-padding">
<!-- Render all the tabs in the collection. -->
<tabs-container>
<li role="tab" class="officeui-display-inline-block officeui-align-center" ng-repeat="tab in tabs">{{tab.Name|tabs}}</li>
</tabs-container>
</ul>
In the template above, I do have an element tabs-container which should be my directive.
I've the JavaScript that defines the AngularJS stuff, including registering this directive:
var officeUIApplication = angular.module('OfficeUI.Ribbon.Controllers', []);
officeUIApplication.directive('tabsContainer', function() {
var functionLink = function(scope, element, attrs) {
console.log(element.children());
};
return {
restrict: 'E',
link: functionLink
};
});
According to my very limited knowledge of AngularJS, I'm just learning it, it's in the variable functionLink where I should manipulate the DOM of attach event handlers to specific parts.
But inside functionLink I call the following code:
console.log(element.children());
But in the console I do see that this particular element is empty.
Why is that, and is this the good approach?
Another approach of which I've tought it to include something in jQuery so that the code is only executed after AngularJS has finished it's work, but I don't like that particular idea, on the other hand, how do I create a jQuery plugin that that passes options and event handlers, or isn't that possible anymore and am I in fact creating an AngularJS plugin?
Thanks for the response.
Try this:
officeUIApplication.directive('tabsContainer', function($timeout) {
var functionLink = function(scope, element, attrs) {
$timeout(function() {
element.ribbon();
});
};
return {
restrict: 'E',
link: functionLink
};
});
jQuery plugins usually require the DOM to be ready for it to initialize properly. In angular, there is no real concept of DOM ready. $timeout is executed after the render phase, which is why it works.

AngularJS + jQuery Mobile w/ No Adapter & Disabled Routing - Used For UI Styling Only

I am learning AngularJS and have built a small application. Now that it's functionally complete, I'd like to style it up using jQuery Mobile.
Originally I dropped in tigbro's jquery-mobile-angular-adapter, but ultimately decided it was more complicated and involved than I needed. I'm not after any fancy screen transitions or page management features in jQuery Mobile - I just want to use it for styling the application and let AngularJS handle the rest.
I read this post, which has the same goal in mind, albeit with another framework, and contains a code snippet to disable the jQuery Mobile routing.
I've applied that snippet to my application in this order of script loading, just before the close body tag:
jQuery
snippet
jQuery Mobile
AngularJS
This snippet placement is the only one that works, or somewhat works anyway, in that anything in my index loads styled properly (header and main nav, basically), and my AngularJS routes work just fine, but any dynamically loaded templates that populate my ng-view, despite having jQuery Mobile data-roles (ul as listview, etc.), are not styled by jQuery Mobile; they're just plain HTML.
Does anyone have an idea as to how I could get those dynamically loaded templates to also be styled?
My index HTML structure looks like this:
<body>
<div data-role="page">
<div data-role="header" data-position="fixed">
<h1>MyApp</h1>
Home
Add
</div>
<div data-role="content" ng-view></div>
</div>
<!-- Scripts -->
</body>
And here's an example of one of my templates:
<ul data-role="listview" ng-controller="MyListCtrl">
<li ng:repeat="item in things">
{{ item.title }}<br/>{{ formatDateForDisplay(item.addDate) }}
</li>
</ul>
Thank you!
I ended up putting together this directive:
angular.module('myApp.directives', []).
directive('applyJqMobile', function() {
return function($scope, el) {
setTimeout(function(){$scope.$on('$viewContentLoaded', el.trigger("create"))},1);
}
});
Then inside of each template, wrap the template content in a div and apply the directive there, i.e.:
<div ng-controller="MyController" apply-jq-mobile>
<!-- Template Content to be jQ Mobilezed -->
</div>
This works, but because of the setTimeout, the content flashes for a split second when loading. I'm still working on figuring how how to get rid of the flash.
To note, without the setTimeout in place, the data-role="listview" wouldn't be styled (I'm guessing since it had to be populated by ng-repeat still), but any static content in the view was styled.
For some reason, el.trigger("create") doesn't work for me. After looking at the angularjs-jqm-adapter, I found it uses el.parent().trigger("create"), which it works for me.
I'm pretty much working on the same thing (no jqm angular adapter). Here's my directive that gets triggered after the last element of the repeat:
Application.Directives.directive('jqmCollapsibleRepeat', function () {
return function (scope, element, attrs) {
if (scope.$last) {
$(element).parent().trigger("create");
}
};
});
and here's part of my view that uses it:
<div data-role="collapsible" data-collapsed="true" data-transition="slide" ng-repeat="document in documents" jqm-collapsible-repeat>
<h3>{{document.FileName}}</h3>
...
</div>
For me this worked:
html:
<ul data-role="listview" data-filter="true" data-filter-placeholder="Suchen" data-inset="true">
<li ng-repeat="alert in alerts" li-jq-mobile>
{{name}}
</li>
</ul>
js:
angular.module('alertList', [])
.directive('liJqMobile', function() {
return function($scope, el) {
$scope.$on('$viewContentLoaded', el.parent().listview('refresh'));
});
for jqm pages and lists for me worked:
For pages:
<div applyjqmobile data-role="page" >
and for the list:
<li lijqmobile ng-repeat="aviso in avisos" data-icon="false" >
And directives:
.directive('applyJqMobile', function() {
return function($scope, el) {
console.log('applyJqMobile');
$(el).hide();
setTimeout(function(){$scope.$on('$viewContentLoaded', el.parent().trigger("create"))},1);
$(el).show();
}
}).directive('liJqMobile', function() {
return function($scope, el) {
console.log('liJqMobile');
$(el).hide();
setTimeout(function(){ $scope.$on('$viewContentLoaded', el.parent().listview('refresh'))},1);
$(el).show();
}
})

Categories

Resources