How to speed up display of modal in Angular app? - angularjs

I'm making a web app with AngularJS and Angular Bootstrap. I am using a modal to allow the user to select one of many items in a list. The list is rendered with ng-repeat.
On some mobile platforms, there is quite a delay (3-5 seconds) between the user pressing the button that launches the modal and when the modal finally appears. I'm not sure whether the wait is caused by Angular rendering the DOM for that modal before the modal displays, or just by the sheer amount of content in the modal. It could be both. When I remove the ng-repeat list, the modal loads instantaneously.
I need to do one of the following:
(1) Cause Angular to build the DOM after the modal has displayed. It's not ideal, but at least the display of the modal would let the user know that their touch registered and something is happening.
(2) Cause the modal to display more quickly despite the large amount of content.
Any thoughts on how to make either of those happen?

The modal instance has a property rendered which is a promise that is resolved when a modal is rendered. You may delay assigning large dataset to the modal scope until that promise is resolved.
$uibModal.open({
controller: function ($uibModalInstance, $scope, data) {
$uibModalInstance.rendered.then(function () {
$scope.data = data;
});
},
...
});
https://angular-ui.github.io/bootstrap/#/modal
But in my opinion, modals should not be used for large content blocks.

Related

angular ng-repeat inside modal

I have a link which on clicking opens a modal. Am using angular ui modal. Inside modal I have a row with two inputs and an Add button. On clicking Add button, a new row is added with the text I entered in inputs. I have a done button which closes the modal. On opening the modal again, my newly added row is missing.
Everytime you create a new Modal, you'll be running the code behind it again, effectively erasing any changes you make to it, even if you are using the same controller as the page you create the modal from.
To do what you want to do, you'll have to;
var modal= $uibModal.open({
templateUrl: ''
}
});
modal.result.then(function(array){
$scope.rowArray = array
}
Your modal close function will then be;
$uibModalInstance.close($scope.rowArray);
This example will work if you're using the same controller for the modal and main HTML page, otherwise you'll need to have a resolve for your $uibModal.open to pass it through to the modal controller, but also add your rowArray to the dependencies of that controller.
Hope it helps!

Is there any way to tell Angular to just temporarily stop updating the DOM?

Use case is that we've got a page with lots of Angular. Sometimes we pop up a modal, and as the user interacts with the modal, it sends messages that result in the the main page (which is behind a semi-transparent overlay, but still partially visible) updating.
This is distracting, and I'd like to "lock" the main page while the modal is open.
I could do this by having some property on $rootScope and making the individual Angular controllers aware of that, but I really want to just select the DOM elements on the main page and essentially unhook their scopes from them temporarily, and then re-hook them when the modal closes.
Create a copy of your data using
$scope.modalData = angular.copy($scope.originalObject);
then update this object on the modal. Once you are ready to close the modal, at that step copy the data back to the DOM scope object.
$scope.originalObject = angular.copy($scope.modalData);

Dynamic layout using angularjs (the angularjs way)

I just recently started tinkering with AngularJs, so my question is fairly basic. For concreteness's sake, let me start with the minimal setting: a page with banner, body and footer. The goal is to make banner and footer stay at the top and bottem of the page (css position: fixed) and the body will fill in the rest.
If I were to use jQuery, I could set position: fixed for body, and listen to window's resize event to determine where I should put my body.
Presumably, I could do the same in AngularJs. But I've read many places that one shouldn't try to manipulate the layout from the code, since it's not in the philosophy of AngularJs. So, what's the best way to achieve this in the AngularJs way? Thanks.
Dynamic layout usually refers to a layout that changes depending upon the current route. Routing is how AngularJS associates a URL with a Controller and data. It becomes an important part of an app once you go beyond just 1 page.
Multiple Views
If you use something like ui-router (http://angular-ui.github.io/ui-router/) it allows you to define views that can have child views.
In your example, the header, body and footer would all be child views of a parent view. Each view would have it's own template, controller and data. When routing changes you can have just one or more of those view change, while the other views remain the same.
Binding Events
You can do all the same event bindings in AngularJS as you can in jQuery. The difference is the life-cycle of those bindings.
If you bind to the click event for a directive using the directive's element, then you don't have to worry about that binding when the directive is destroyed. AngularJS will remove the binding automatically.
When you bind to something like the window.resize event, then you have to be more careful.
link: function($scope, $el, $attrs) {
// this doesn't have to be unbind
$el.bind('click',function(e) {
// do stuff
});
// this has to be unbind on destroy
angular.element($window).bind('resize.mybinding',function(e) {
// do stuff
});
$scope.$on('$destroy',function(){
angular.element($window).unbind('resize.mybinding');
});
});
I've seen a lot of AngularJS source code bind to window.resize when it wasn't necessary. Only you know when it's needed, but sometimes you can get away with just using a watcher.
link: function($scope, $el, $attrs) {
$scope.$watchGroup(function(){
return [$window.innerWidth,$window,innerHeight];
},function(values){
// do stuff
});
});
The above does the same thing as binding. It executes a closure function when the size of the window changes, but the difference is that it's triggered only during digest and not by the window resize event. This can sometimes be more efficient for performance. As the resize is relative to only when the directive is digested.
A lot of AngularJS applications use JavaScript code to manage the layout of an application. How much JavaScript code you use, and why you do it that way is up to you, but keeping layout separated from the JavaScript side has a lot of advantages. It's better to let CSS handle it.
Bootstrap provides a nice plug-in called Affix that does just that.
<div id="banner" data-spy="affix">Fixed Banner</div>
<div>Body</div>
<div id="footer" data-spy="affix">Fixed Footer</div>
More on affix here for example:
http://www.tutorialrepublic.com/twitter-bootstrap-tutorial/bootstrap-affix.php

Using AngularJS 1.2, how to animate items inside partials (when using single ng-view in your index file)?

I had an app that I got 80% through building a few weeks back and it uses a lot of jQuery to do animations. I stopped working on that and started rebuilding it from scratch using AngularJS.
I'm now at the point where I'd like to try to add some of the animations that I WAS using in the old app. When leaving from the "main page" to the "details page", I used to have one div go flying offscreen to the left, while the main table seemed to shrink upwards and sort of "merge with" a drop-down box that was coming into view for that new details page. (The table and the drop-down have essentially the same information, which is why the animation made sense. The drop-down allows them to jump from record to record, without having to go back to the main table to navigate.)
Anyways, the way I built this app in Angular is that it is working off of one index file with a single "ng-view" div in it. And then the router determines what template-page to pull in.
I see from the various tutorials out there on animating with AngularJS 1.2 that the "ng-view" fires off events that can be harnessed for animation. But that's ONLY if you're going to add your .css class to be animated to the div that holds the "ng-view".
<div class='whatever' ng-view>
and then your css would contain something like:
.whatever.ng-enter{
opacity: 0;
}
But what can I do if I want to animate the way various PIECES of a view template enter or leave the view? (Most are just divs that act as containers for small tables of data [ as divs, not tables].) I'm using "ng-repeat" in those templates to populate the tables, but I really don't want to animate any of the rows. That's about the only other directive that fires Angular's animation that I'm currently using in the templates.
You can set animations to different parts of your template in it's controller using Jquery itself.
myApp.controller('sampleController', function($scope, $timeout, $location){
$("#submit_button").click(function(){
$("#yourdiv").fadeOut();
$timeout(function() { $location.path('/newurl'); }, 3000);
});
});
Display all your animations then change the route. I have used $timeout so as to set a delay for displaying the animations.

Sharing Functions between two Angular js Controllers

I have one requirement. I am using Angularstrap Modal for Modal Dialogue(User's Terms and Condition).
On this modal Dialogue Two buttons are there "Agree" and "Don't Agree".
I have two links. on clicking on each links different views are opened.Both views are associated with different controller.
And My requirement is like,I need to open Terms Dialogue whenever new user comes and that needs to open on click event of both link.
Currently I have to write modal code in each controller.(Same function to open modal and for the modal's buttons)
Now,I want to generalize the code for the modal and modal's button.I want to write once and I want to use that code whenever I need to open (any) Modal
What can be the Approach or is it possible?
This is skeleton pseudo code for my dialog service:
//modal container is the template with backdrop
//and other modal elements (title, body, buttons, close button)
loadContainerTemplate()
//the actual html content for the modal body
.then(loadDialogTemplate)
.then(init);
function init() {
//append container to body
//append dialog template to modal body
//create new scope for the modal
//set up buttons, title, width, etc.
//listen for ESC keypress
//initialize modal controller if needed
//prepare close function (destroy scope, remove dom, execute close callbacks, etc)
//compile modal html against scope
}
My service would accept arguments such as modal template's url, title, width, buttons and their callbacks, parent scope etc. In your controller you would just call the dialog like this:
function($scope, Dialog) {
Dialog({scope: $scope, templateUrl: 'path/to/modal/body', buttons: {
'agree': agreeCallback,
'disagree': disagreeCallback
}, title: 'Modal title'});
}
Or you could go one step further and create TermsModalService that would abstract details like template, buttons, callbacks, title, etc and use the underlying Modal service to show the actual dialog. The Terms modal would be easily usable between all controllers with one-liner: TermsDialogService.show();

Resources