I am having a serious performance issue in my application. I am using angular and ng-grid. After some reading for why my app is slow, I was directed to use bindonce directive to overcome potential Angular performance issues.
So I added bindonce.js in my solution and injected the directive in my module
HomeIndexModule = angular.module("HomeIndexModule", ['ngGrid', 'pasvaz.bindonce']);
and I am using as below in markup
<div class="gridStyle " bindonce data-ng-grid="gridOptions"></div>
I am not sure whether this is actually unbinding the grid.
Question 1: Has anyone undergone the process could direct me how to do this as I could find examples only for ng-repeat in the bindonce website.
Question 2: how to verify whether the bindonce is actually working?
I have mentioned this twice in other posts, I have created my own bind-once directive that is tiny and does a perfect job, personally i think the plugin is OVER-COMPLICATING things.
Check this out
app.directive('bindOnce', function() {
return {
scope: true,
link: function( $scope ) {
setTimeout(function() {
$scope.$destroy();
}, 0);
}
};
});
<div class="gridStyle" bind-once ng-grid="gridOptions"></div>
Demo: http://plnkr.co/edit/4cBOEG?p=preview
Similar Post:
Genuinely stop a element from binding - unbind an element - AngularJS
This change fixed the performance lag, the change is commenting out the self.resizeOnData() in ng-grid.js line number 1420.
$scope.$on("ngGridEventData", function () {
//self.resizeOnData(temp);
Chrome event pro-filer showed this method being called too many times and looks like it is re sizing all the cells in the grid on change of data-source. I am still testing to find the side effects but till now all the previous functionalities are working and the performance was increase 5X than my previous one.
if you see this change break any thing else let me know
You should read the documentation thoroughly. Using just bindonce won't give you the effect you want. Look at this example I've created: http://plnkr.co/edit/GXkLWfFpfdJvPVyRMtpO - $timeout is used to call $apply every second. Two elements have bindings to the same functions which logs to console the text passed as a parameter. As you can see, using just bindonce doesn't work - the just bindonce text is still being logged, whilst with bo-text appears only once. One of bo-text, bo-html etc. must be used to achieve binding only once.
So, in your case, you need to modify templates of the ngGrid directive and replace every normal binding you want with bo-* directives. Here: How to render html formatted text in ng-grid column header I've explained how to do that.
Related
I have been trying to use transclusion to create a directive which makes two copies of it's contents and appends them inside the original template. I have failed in my attempts to modify the two copies before they slotted back into the DOM and I think it's because I have misunderstood how transclusion works.
I have another question here which I don't think is going to be answered because I think the premise may be wrong.
Transclude function needs to modify clone properly
I need to take a new approach to this and I was wondering if it would be sensible to ditch transclusion and get my hands dirty inside a compile function.
My new question is, can you take the contents of "elem", make a couple of copies of it using JQlite then compile them manually against the directive's parent scope and add them back into the original template?
So, if my toolbar directive is used like this, where the contents of the toolbar tag can be any HTML the user wants...
<div ng-controller="myController">
<toolbar>
<form name="formName" submit="myController.submit()">
<div>
... some controls...
</div>
</form>
</toolbar>
</div>
And the template for the directive is this....
<toolbar-inner>
<div class="toolbar">
<div transclude-main></div>
</div>
<div class="overflow">
<div transclude-overflow></div>
</div>
</toolbar-inner>
My compile function of the toolbar directive needs to take a copy of the contents of the element, clone it, rename any forms so that we don't have duplicate form names then compile one copy against the parent controller and slot it into the main slot then do the same with a second copy and slot it into the overflow slot.
The key things is that at the end of it I should have a single directive with two copies of it's contents and my controller should have two forms on it - myController.formName and myController.formName2
Please let me know if I haven't explained something correctly here. I'm pretty sure the solution here should not involve transclusion, hence posting the second question. It is not a duplicate.
If I can explain anything in further detail please ask.
Many thanks.
EDIT:
I have tried to do this in the following Plunkr
https://plnkr.co/edit/eUIdaPiOIISDdXGLBTKJ?p=preview
I have a few problems with this:
A) I am getting a JS error in the console - TypeError: Cannot read property 'childNodes' of undefined
B) I was assuming I could just mess with the template in the pre-compile phase and replace the contents of the directive with new HTML consisting of a new template merged with two copies of the original contents. I can see though that I have to compile them against the $parent scope because my directive uses an isolate scope (although not strictly necessary in this cut down example)
C) I get another error when replacing the original contents of the directive element anyway.
I think I am half way there and hopefully this plunk shows what I an prying to achieve, but i have run out of knowledge.
For completeness, here is the plunk where I tried to do it with transclusion, which didn't work because the transcluded contents are already compiled by the time I started messing with them in the transclude function.
https://plnkr.co/edit/XE7REjJRShw43cpfJCh2?p=preview
You can see the full conversation about this in my previous question:
Transclude function needs to modify clone properly
I got your transcluded example working. See here.
I had to call the below to get it working.
$compile(clone)(scope.$parent);
For the ngTransclude:orphan problem, in this version by compiling just the form elements it works when the child transclude is outside of the form.
This plunker was prior to Angular 1.5 which introduce Tranclusion.
link: function(scope, element) {
if (!element.find('ng-transclude').children().length) {
element.find('button').append('<b style="color: red">Button1</b>');
}
}
Plunker
I've recently switched from jQuery to Angularjs and I am in the process of re-coding some pagination logic for the links ("Next", "Previous", etc.) that were written in jQuery-style Javascript previously.
Each link has an ngIf condition (for example, the "Previous" link won't show if you're on page 1) plus an ngClick event, which essentially updates a scope variable called $scope.pagination.position that determines which results are displayed in the table.
My original code was something like this (simplified for clarity):
Template
<a ng-if="pagination.position > 0" ng-click="pagination.first()">First</a>
Controller
$scope.pagination = {
first: function() {
this.position = 0;
}
}
Then I learned more about directives, and how most DOM elements that aren't static HTML should be created using a directive. So I switched each link (since each has it's own display rules and behaviour on clicks) to its own directive, like so:
Template
<a pagination-first></a>
Directive
app.directive('paginationFirst', function() {
return {
link: function(scope,el,attr) {
scope.pagination.first = function() {
scope.pagination.position = 0;
}
},
replace: true,
template: '<a pagination-first ng-if="pagination.position > 0" ng-click="pagination.first()">First</a>'
}
});
I'll cut straight to the chase : am I doing directives wrong? All that's happened, from my perspective, is I've flipped from having logic in my template to having a template in my logic, and I've defined the click event function in the directive rather than in the controller.
Is this even an appropriate time to be using a directive?
I'd like to learn best practices, so I'd love to know if I've missed the point and if the original templated-based ngIf and controller function approach was fine, even with longer and more complex ngIf conditions than the one shown.
If I want to add specific behaviors to a dom or dom list then I normally create a directive. As per angular js perspective the dom manipulation should only be done through directive (For me it is the best place, sometime I have to disobey this due to my lack of knowledge ). I specially found directive use full while creating a widget. In one of my project there was a part where a section is dedicated to display an image and also upload the image. I just use the directive on the top div, with the help of link function I attached the event handlers to various child dom. And as my project doesnot require an isolated scope (as this widget was all used in a single project and the outer scope was under my control) . So it worked like a charm. I cerarted the directive once. And used that widget through rest of the project as it's behavior and design (of the widget ) was same through out the project. For the pagination widget you can create a directive. Take the directive attibutes value as the input of the pagination parameters. Like calling script, limit offset. Container identifier to update the content. Then you can solely concentrate on the pagianation behavior. But from my experience (as I am also not so experienced in angular js), sometimes it becomes a little hectic to develop a directive and and use that throughout the project. As in some places we need to modify the behavior of the directive. And for this it may breaks elsewhere. But I know as I learn more I will be more efficient to handle this kind of situation. Hope my experience will help you.
I'm new to AngularJS and I want to change the behavior of a button dynamically.
As I am used to JQuery, I use element.attr('ng-click', 'controller.function()');
But, as I understood AngularJS needs to compile the new DOM.
But, in my head "compile" = "cost".
What is the best practice :
Change/Add DOM elements then compile when needed.
Hide another button, and just show/hide the one I need.
Thank you !
I prefer to use ng-switch for these type of things. It ends up being much simpler and its always a good idea to make use of the existing angular directives where possible. Other developers working on your project will appreciate the simplicity.
http://jsfiddle.net/L9T6J/2/
the html...
<div ng-switch="state">
<button ng-switch-when="0" ng-click="updateState()">Label 1</button>
<button ng-switch-when="1" ng-click="updateState()">Label 2</button>
</div>
and the javascript...
$scope.state = 0;
$scope.updateState = function () {
$scope.state = $scope.state === 0 ? 1 : 0;
}
i generally hand that type of thing in angular like so
markup
<button ng-click="myButton.actions[myButton.state]()">
{{myButton.labels[myButton.state]}}
</button>
scope
$scope.myButton = {
state: 0,
labels: ['label 1','label 2'],
actions: [
function() {$scope.myButton.state = 1;},
function() {$scope.myButton.state = 0;}
]
};
here is a fiddle to see it in action
http://jsfiddle.net/pixelchemist/L9T6J/
It depends entirely on the application and scope of the directive, I go either two ways:
If it's between different buttons (if-elseif-else etc), the directive is very simple and explanatory by just looking at it, I use ng-if or ng-switch.
If it's a complex directive that lives on its own, meaning it has its own scope and functions, there's no reason for it to be cluttering the DOM. For instance, if you search through the DOM a lot, you're going to be adding more data to search through, then instead if you destroy and rebuild it only when its used. It also clutters the DOM if you have tons of custom directives that you show/hide.
If your application has several heavy directives that contain complexity and you only use them after you click on a button, it makes more sense to create/destroy, than to hide/show the directive (helps if the directive contains data that it is getting from a service).
Also it just feels more right to invoke sub applications when you need them, rather than hiding/showing them, though this is a personal preference and I might be wrong.
The majority of custom directives you use from 3rd parties, they create/destroy their directive rather than show/hide.
Not entirely sure how the $watchers work with custom directive when hidden, but you removed that possibility when you create/destroy directives which helps with performance. For instance, if you render your HTML often, then $watchers pile up if you have a lot of ng-if/ng-switch, on the contrary, if you create/destroy, then when the html DOM is destroyed, Angularjs won't check the $watcher because it doesn't exist so you gain performance.
I'm using AngularJS to design a small app for searching. I have a div that's currently empty, and after running a function, for it to replace it with a div that has ng-include. The div replaces just fine, but ng-include doesn't load up.
I'm currently running the following in console for testing to see get it running. How would I go about getting this to work? Any help is appreciated!
document.getElementById("resultsDiv").innerHTML = '<div ng-include="" src=" \'sr.html\' "></div>';
read about angularjs $compile function
I don't know why you need to make this JavaScript-call, but its definitely no the 'angular-way'. If you need to conditionally include html, i would recommend using ng-if, ng-hide, ng-show or even ng-switch.
You could do something like this:
// somewhere in your controller
$scope.triggerSomeInclude = true;
// in your template
<div ng-if="triggerSomeInclude">
<div ng-include ="'someFile.html'"></div>
</div>
Another approach would be using a directive. They are the only place, where selecting an element directly could make sense (although even there it usually doesn't to select an element via id). But as I said, it's hard to stay what the best method would be, as I'm not sure what you're trying to achieve.
Although you're not using jQuery, what you're trying to do looks very jQueryesque (awful word) as you're selecting an element directly seemingly totally detached from the $digest and $compile-cycles of angular, so I also would recommend to read this answer:
"Thinking in AngularJS" if I have a jQuery background?
In the end, the method I used was templating example used for ngInclude.
http://docs.angularjs.org/api/ng.directive:ngInclude
Inside my controller, for example, by default it would set the following:
$scope.mainDivTemplate = {url: 'partials/test1.html'}
If I wanted to switch it to something else, I'd call a function that would look a little something like this.
$scope.loadHome = ->
$scope.mainDivTemplate = {url: 'partials/home.html'}
$scope.$apply()
I have this AngularJS app. Everything works just fine.
Now I need to show different pop-ups when specific conditions become true, and I was wondering what would be the best way to proceed.
Currently I’m evaluating two options, but I’m absolutely open to other options.
Option 1
I could create the new HTML element for the pop-up, and append to the DOM directly from the controller.
This will break the MVC design pattern. I’m not happy with this solution.
Option 2
I could always insert the code for all the pop-ups in the static HTML file. Then, using ngShow, I can hide / show only the correct pop-up.
This option is not really scalable.
So I’m pretty sure there has to be a better way to achieve what I want.
Based on my experience with AngularJS modals so far I believe that the most elegant approach is a dedicated service to which we can provide a partial (HTML) template to be displayed in a modal.
When we think about it modals are kind of AngularJS routes but just displayed in modal popup.
The AngularUI bootstrap project (http://angular-ui.github.com/bootstrap/) has an excellent $modal service (used to be called $dialog prior to version 0.6.0) that is an implementation of a service to display partial's content as a modal popup.
It's funny because I'm learning Angular myself and was watching some video's from their channel on Youtube.
The speaker mentions your exact problem in this video https://www.youtube.com/watch?v=ZhfUv0spHCY#t=1681 around the 28:30 minute mark.
It comes down to placing that particular piece of code in a service rather then a controller.
My guess would be to inject new popup elements into the DOM and handle them separate instead of showing and hiding the same element. This way you can have multiple popups.
The whole video is very interesting to watch as well :-)
Create a 'popup' directive and apply it to the container of the popup content
In the directive, wrap the content in a absolute position div along with the mask div below it.
It is OK to move the 2 divs in the DOM tree as needed from within the directive. Any UI code is OK in the directives, including the code to position the popup in center of screen.
Create and bind a boolean flag to controller. This flag will control visibility.
Create scope variables that bond to OK / Cancel functions etc.
Editing to add a high level example (non functional)
<div id='popup1-content' popup='showPopup1'>
....
....
</div>
<div id='popup2-content' popup='showPopup2'>
....
....
</div>
.directive('popup', function() {
var p = {
link : function(scope, iElement, iAttrs){
//code to wrap the div (iElement) with a abs pos div (parentDiv)
// code to add a mask layer div behind
// if the parent is already there, then skip adding it again.
//use jquery ui to make it dragable etc.
scope.watch(showPopup, function(newVal, oldVal){
if(newVal === true){
$(parentDiv).show();
}
else{
$(parentDiv).hide();
}
});
}
}
return p;
});
See
http://adamalbrecht.com/2013/12/12/creating-a-simple-modal-dialog-directive-in-angular-js/
for a simple way of doing modal dialog with Angular and without needing bootstrap
Edit: I've since been using ng-dialog from http://likeastore.github.io/ngDialog which is flexible and doesn't have any dependencies.
Angular-ui comes with dialog directive.Use it and set templateurl to whatever page you want to include.That is the most elegant way and i have used it in my project as well.
You can pass several other parameters for dialog as per need.