AngularJS, directive element is null - javascript

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.

Related

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?

How to dynamically nest directives in AngularJS

New to Angular and need some assistance.
I have a block of HTML content that will be coming from a database that will contain a group of widgets. These are simple widgets that will essentially render out various elements, but for the purposes of this question we'll assume they're all basic HTML inside.
Those widgets are included in an unpredictable way, so my first thought was to use directives to render the HTML. So, we'd have something like:
<div widget data="This is the content."></div>
So I've got a directive that will place the value of data into the div. Easy enough!
Now, how would I go about nesting those widgets? So, how would I get something like:
<div widget data="Welcome! ">
<div widget data="This is some inside content."></div>
</div>
to render out:
Welcome! This is some inside content.
... because the issue I'm noticing is that if I place anything inside the directive HTML, it essentially gets ignored since it gets replaced with its own result (thus only echoing out Welcome!).
I realize I may be going the wrong direction on this in the first place, so any insight would be greatly appreciated. Thanks so much!
This is where you need to use the transclusion feature of the directive combined with ng-transclude directive.
Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
A very basic version of transclusion of content based on your example might look something like this:
.directive('widget', function() {
return {
transclude: true,//Set transclusion
template: '{{text}} <section ng-transclude></section>', <!-- set where you need to present the transcluded content -->
scope: {
text: "#"
}
}
});
Demo
angular.module('app', []).directive('widget', function() {
return {
transclude: true,
template: '{{text}} <section ng-transclude></section>',
scope: {
text: "#"
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<span widget data-text="Welcome! ">
<div widget data-text="This is some inside content.">
<span widget data-text="This is some inside inside content."></span>
</div>
</span>
</div>

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

How to load <script> inside AngularJS Application

I need to add google preview wizard api (https://developers.google.com/books/docs/preview-wizard) script inside my angularJS application.
<script type="text/javascript"> GBS_insertEmbeddedViewer('ISBN:0738531367',600,500);</script>
When I try to do this directly (or inside a div tag) inside a controller, it over writes the whole web page and just displays the preview button.
Same is the scenario with I try to load this piece of script with directives. (AEC)
Could not use ng-templates as keeping a script anything inside ng-template didn't make sense.
Closest I ever got to the problem is when I try to load this piece of script in the main index.html page. It gets loaded, but I'm not able to pass a variable ISBN in to the script (even keeping the isbn attached to scope ).
Data binding with the controller is not happening when ever the variable is inside the script tag
Please suggest a way out of this.
angular has directives you can build one like this
angular.module('app')
.directive('embedViewer', ['$compile',function($compile) {
return {
restrict: 'AE',
template: '<div></div',
replace: true,
scope: {
isbn: '=embedViewer'
},
link: function(scope, el, attrs) {
var rawTmpl = '<iframe src="books.google.com/books/previewlib.js">; <script type="text/javascript"> GBS_insertEmbeddedViewer("{{isbn}}",600,500);</script> </iframe>';
scope.$watch('isbn', function(val) {
if (val) {
var template = $compile(rawTmpl)(scope);
el.html(template);
}
})
}
}
}]);
and you can use in any template like
<div embed-viewer="book.isbn"></div>
<!-- OR -->
<div data-embed-viewer="book.isbn"></div>
<!-- OR -->
<embed-viewer="book.isbn"></embed-viewer>
this code isnt tested

Implementing a page load performance directive in AngularJS

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.

Categories

Resources