Page Transition with AngularJS and CSS Animations - angularjs

I'm working on a basic AngularJS app. This app will have pages that are going to be defined within templates. When a user changes pages, I want to be able to show the other page with a basic animation. Currently, my app is doing this. However, the previous page drops below the new page and sort of flickers. I can't figure out why this is happening. Here is my code:
<!doctype html>
<html lang="en" ng-app="app">
<head>
<meta charset="utf-8">
<title>My HTML File</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0rc1/angular.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0rc1/angular-route.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0rc1/angular-animate.js"></script>
<style type="text/css">
.myAnimation { height:480px; width:640px; overflow:hidden; }
.myAnimation.ng-enter { animation-duration: .3s; animation-name: fadeIn; animation-timing-function: cubic-bezier(.71,.55,.62,1.57); }
.myAnimation.ng-leave { animation-duration: .3s; animation-name: fadeOut; animation-timing-function: cubic-bezier(.71,.55,.62,1.57); }
#keyframes fadeIn {
from { opacity: 0; transform: scale(.9, .9); }
to { opacity: 1; transform: scale(1, 1); }
}
#keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
</style>
</head>
<body style="margin:0; padding:0;">
<ul style="list-style-type:none; margin:0px; padding:0px;">
<li style="display:inline;"><a href='#page1'>Page 1</a> </li>
<li style="display:inline;"><a href='#page2'>Page 2</a> </li>
</ul>
<div style="display:block; border:1px solid black;">
<div ng-view class="myAnimation"></div>
</div>
<script type="text/javascript">
angular.module('app', ['ngRoute', 'ngAnimate']).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/page1', {templateUrl: 'views/page1.html', controller: Page1Ctrl}).
when('/page2', {templateUrl: 'views/page2.html', controller: Page2Ctrl}).
otherwise({redirectTo: '/page1'});
}]);
function Page1Ctrl($scope, $http) {
}
function Page2Ctrl($scope, $http) {
}
</script>
</body>
</html>
How do I prevent the scrollbar from appearing when I change pages? And prevent the older page from dropping below the new page?
Thank you!

It's likely that you need to give the animated divs a position:absolute; style during the animations.
What is happening is that the second template is being loaded into the DOM while the first template is still doing the fade-out animation. Without position:absolute they do what is natural and start block stacking until the first template has fully faded out and Angular removes it.
Keep in mind if you add position:absolute you may also have to play with the "Top" value to position it correctly depending on which parent element has position:relative or absolute.
You can put the position:absolute class on the animated div generally, or just specifically on the animating classes depending on what works better for your app structure.

Related

Is it possible to route using HTML tag <button>?

I know how to do Angular ngRoute using <a> tag. I would like to know if it's also possible for me to use <button> tag to navigate between <ng-view>. Let's say I have the following code:
app.js & index.html
var app = angular.module('test', ['ngRoute']);
app.config(function($routeProvider) {
// mapping contacts.html to path /cnt
$routeProvider.when('/cnt', {
templateUrl: "https://www.google.co.za/", // the file to be mapped to
controller: "cctrl" // including the controller for that file
});
// mapping student.html to path /std
$routeProvider.when('/std', { // mapping url
templateUrl: "https://www.facebook.com", // the file to be mapped to
controller: "sctrl" // including the controller for that file
});
});
// creating a controller name cctrl
app.controller('cctrl', function($scope) {
});
// creating a controller name sctrl
app.controller('sctrl', function($scope) {
});
ul.zzz{
list-style: none;
width: 100%;
margin: 10px auto;
}
ul.zzz li{
float: left;
padding: 10px;
}
.mainContent ng-view{
margin: 0;
}
<html ng-app="test">
<head>
<title>Angular Test</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-route.js"></script>
</head>
<body>
<div>
<ul class="zzz">
<li>Student
</li>
<li>Contacts
</li>
</ul>
<br/>
</div>
<div class="mainContent">
<ng-view>
</ng-view>
</div>
</body>
</html>
the code above shows the working html page
the code below is whats running in my mind if i use button
<button name="gotoPage2" ng-click="gotoNext()">Next</button>
<script>
var app = angular.module('test',[ngRoute]);
app.config(function($routeProvider){
$routeProvider.when('button with a name gotoPage2 is click',{
templateUrl:'page2.html'
})
});
// or
app.controller('controllerName', function($scope){
$scope.gotoNext = function(){
app.config(function($routeProvider){
$routeProvider.when('button with a name gotoPage2 is click',
{templateUrl:'page2.html'}
)
});
};
});
</script>
Have you try using ng-click on the button and have a function on the controller to change the route
Html
<button ng-click="changeRoute({view})">Text</button>
Controller
...
$scope.changeRoute(view){
$location.path(view);
}
...
I usually use ui-router, i like it better then ng-route
put the button tag inside <a> it will work
<li><button>name</button>

How to add animation to angularjs uib-alert directive

I want to add fade-in animation for new alerts pushed into array and fade-out for dismissed alerts.
Alerts are dismissed automatically after 5 seconds.
I've already included angular-animate ui-bootstrap and ui-bootstrap-tpls libraries.
How can i get these animations working?
See Demo: http://plnkr.co/edit/ecbCA7h2zVA4XnvUHfFX?p=preview
HTML
<html ng-app="myApp">
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.css" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.js"></script>
<script src="https://code.angularjs.org/1.5.0/angular-animate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/1.1.2/ui-bootstrap.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/1.1.2/ui-bootstrap-tpls.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="MainController">
<h1>Alert With Animation</h1>
<uib-alert
ng-repeat="alert in alerts"
type="{{alert.type}}"
dismiss-on-timeout="5000"
close="alerts.splice($index, 1);">{{alert.msg}}
</uib-alert>
<input type='text' ng-model='msg' />
<button ng-click="addAlert(msg,'danger')">Add Alert</button>
</body>
</html>
SCRIPT
(function() {
var app = angular.module("myApp", ['ui.bootstrap', 'ngAnimate']);
app.controller("MainController", function($scope) {
$scope.alerts = [];
$scope.msg = "No Animation!";
$scope.addAlert = function(msg, type) {
$scope.alerts.push({
msg: msg,
type: type
});
};
});
}());
Answer is based on Pankaj's comment.
Which lead me to this article: http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html
added class='repeat-item' to <uib-alert>..</uib-alert> element.
added proper stylings for animation effects.
<style type="text/css">
.repeat-item.ng-enter,
.repeat-item.ng-leave {
-webkit-transition: 0.5s linear all;
transition: 0.5s linear all;
}
.repeat-item.ng-enter,
.repeat-item.ng-leave.ng-leave-active {
opacity: 0;
}
.repeat-item.ng-leave,
.repeat-item.ng-enter.ng-enter-active {
opacity: 1;
}
</style>
See working demo: https://plnkr.co/edit/CK2lV4mBVGs8q5gyYklE?p=preview
One little glitch
When you click to add an alert for the very first time, there is no fade-in animation. After that, everything seems OK.

Angular materials : Basic Usage template

I would very much like to integrate in my angular app the Basic Usage template
from Angular Materials.
I really like the transition effect when the <> is clicked.
I already did a search for that directive on their website but did not find it. The closest thing I managed to find is the Toolbar, but it's slightly different in the way that the upper corners are not rounded. Also, using a simple ng-show, will not provided that transition.
Any suggestions on how to achieve this?
You can do that with angular animations which is just some simple CSS with transitions. And you are on the right track with md-toolbar. The demos use it, you just need to set some CSS to round the top corners.
md-card md-toolbar {
border-radius: 3px 3px 0 0;
}
Now add some content below the md-toolbar that you want to toggle and use ng-show on it.
<div class="toggle-content" ng-show="open">
The toggled content!
</div>
Then just check the ngShow documentation on how to animate it with CSS. What you want to animate here is the height of the toogle-content element. When it is hidden, height: 0 is applied, otherwise height: 200px.
.toggle-content {
height: 200px;
background: red;
}
.toggle-content.ng-hide-add, .toggle-content.ng-hide-remove {
transition: height linear 0.5s;
}
.toggle-content.ng-hide {
height: 0;
}
And of course you need a md-button in the toolbar that toggles the content.
<md-button ng-click="open = !open">
Toggle
</md-button>
Complete example: http://codepen.io/kuhnroyal/pen/XXZPrE
You need to implement something akin to the slideToggle() method in jquery. The Angular Slideables directive provides this functionality.
The straight corners are a custom style implemented in addition to the md-toolbar directive.
It turns out this is the closest thing that I managed to find that suits my needs.
'use strict';
angular.module('ui', ['ui.bootstrap']);
(function() {
angular.module('ui', [
"ui.bootstrap",
"ngAnimate"
]);
var module = angular.module("ui");
module.controller("controller", function($scope) {
var model = this;
model.oneAtATime = true;
model.Operators = [{
ReportItemName: "asd"
}, {
ReportItemName: "fds"
}];
});
}());
<!DOCTYPE html>
<html ng-app="ui">
<head>
<link data-require="bootstrap-css#3.1.1" data-semver="3.1.1" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" />
<script data-require="angular.js#1.4.8" data-semver="1.4.8" src="https://code.angularjs.org/1.4.8/angular.js"></script>
<script data-require="angular-animate#1.4.8" data-semver="1.4.8" src="https://code.angularjs.org/1.4.8/angular-animate.js"></script>
<script data-require="jquery#*" data-semver="2.1.4" src="https://code.jquery.com/jquery-2.1.4.js"></script>
<script data-require="bootstrap#3.1.1" data-semver="3.1.1" src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script data-require="ui-bootstrap#0.14.3" data-semver="0.14.3" src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.14.3/ui-bootstrap.js"></script>
<script data-require="ui-bootstrap-tpls-0.11.2.min.js#0.14.3" data-semver="0.14.3" src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.14.3/ui-bootstrap-tpls.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div ng-controller="controller as model">
<uib-accordion close-others="model.oneAtATime">
<uib-accordion-group heading="Custom template">
<uib-accordion-heading>
I can have markup, too! <i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': status.open, 'glyphicon-chevron-right': !status.open}"></i>
</uib-accordion-heading>
<h1>Some Content</h1>
</uib-accordion-group>
</uib-accordion>
</div>
</body>
</html>
Other solutions that provide out-of-the-box functionality are very much welcomed.

Include external js widget in angular template

I need to include script tag which will render widget on my angularjs template.
For example I'd include this
<script type="text/javascript" src="http://100widgets.com/js_data.php?id=106"></script>
But angular will not render it.
Since the 100widgets scripts manipulate DOM and add other <script> tags to HTML file it could not work properly.
Morover some of these widgets are Flash based so the script add reference to SWF objects.
I think one possibility is to analyze the output of the request to url in src attribute (in your example http://100widgets.com/js_data.php?id=106) and trying to add the corresponding DOM manipulation and scripts to the template in which you desire the widget will appear.
Following is an example showing a page (page1) NOT WORKING (simply added the script tag as you typed) and a second page (page2) whose template has the insertions needed to show up the calendar widget.
PS: due to sandbox restriction this snippet could not work here on SO; try this CodePen version in debug mode: http://codepen.io/beaver71/pen/MyjBoP, or create your own version deployed on your local web server.
(function() {
'use strict';
angular.
module('myApp', ['ui.router']).
config(configRouteProvider).
controller('AppCtrl', AppCtrl);
function AppCtrl($location, $rootScope) {
$rootScope.$on('$stateChangeStart', onStateChangeStart);
function onStateChangeStart(event, toState, toParams, fromState, fromParams, options) {
console.log('From:', fromState.name,
'to:', toState.name);
}
}
function configRouteProvider($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/',
templateUrl: 'views/home.html'
})
.state('page1', {
url: '/pag1',
templateUrl: 'views/page1.html',
})
.state('page2', {
url: '/pag2',
templateUrl: 'views/page2.html',
});
$urlRouterProvider.otherwise('/');
}
}());
body {
margin: 0px;
}
hr {
margin: 0px;
}
.tabs {
padding: 8px;
background-color: black;
color: white;
}
.tabs a,
.tabs a:visited,
.tabs a:active,
.tabs a:hover {
color: white;
}
.my-tab {
height: 100%;
width: 100%;
position: absolute;
padding: 8px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src='//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
<script src='https://code.angularjs.org/1.4.0/angular.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.18/angular-ui-router.js'></script>
</head>
<body ng-app="myApp" ng-controller="AppCtrl as app">
<div class="tabs">
Home
Page1
Page2
</div>
<hr />
<div ui-view></div>
<script id="views/home.html" type="text/ng-template">
<div class="my-tab">
<h3>Home</h3>
<p>bla bla bla</p>
</div>
</script>
<script id="views/page1.html" type="text/ng-template">
<div class="my-tab">
<h3>Page1</h3>
<p>Using script type="text/javascript"... NOT WORKING:</p>
<script type="text/javascript" src="http://100widgets.com/js_data.php?id=106"></script>
</div>
</script>
<script id="views/page2.html" type="text/ng-template">
<div class="my-tab">
<h3>Page2</h3>
<p>Workaround</p>
<!--code1-->
<div class="scriptcode">
<!--ecode1-->
<a target='_blank' href='http://100widgets.com/calendars/106-calendar.html'>
<embed align="middle" id="calendar" width="170" height="156.111111111" allowscriptaccess="always" quality="high" salign="lt" wmode="transparent" src="http://100widgets.com/js-files/postit.swf?UTCoffset=0&gid=0&text1=St Valentines is on 14th February 2012&&event_time=&rec=&rnd=0&gha=0&ghb=0&ghf=1&gbc=FFB200&gfc=040244&gtc=F9F9FF&gnu=http://mycalendar.org/widget/&fna=&ims="
type="application/x-shockwave-flash" />
</a>
<!--code2-->
</div>
<!--ecode2-->
<!--below commented cause it's not necessary -->
<!--script type="text/javascript">
var js = document.createElement("script");
js.type = "text/javascript";
js.src = "http://100widgets.com/stat.js.php";
document.body.appendChild(js);
</script-->
</div>
</script>
</body>
Due to safety constraints, Angular does not parse <script> tags inside templates.
Then the widget you are referring to, is utilizing document.write. Document.write is unavailable once your page is done loading.
So, there seems to be no easy way out here.
However, as what you are trying to do, is something that is quite usual in add-script, krux created postscribe. A way around this issue. On my turn I created a small directive that utilizes this library.
In your template it will look something like this:
<div ps-src="http://100widgets.com/js_data.php?id=21"></div>
the directive:
function psScr($document) {
return {
restrict: 'A',
scope: {
psSrc: '#'
},
link: link
}
function link(scope, elm) {
if (typeof postscribe !== 'undefined') {
postscribe(elm[0], `<script src='${scope.psSrc}'></script>`);
} else {
// If postscibe isn't loaded, first load the needed libarary
var script = document.createElement('script');
script.src = 'https://gitcdn.xyz/repo/krux/postscribe/master/dist/postscribe.js';
script.onload = function () {
// once postscibe is in, kick it into action.
link(scope,elm);
};
document.head.appendChild(script);
}
}
}
angular.module('psSrcModule', [])
.directive('psSrc', psScr);
You can see it in action in this plunk
Not all of the widgets behave nicely in combination with postscribe though, some of them seem to display some artefacts in the html. I currently lack the time to find out who is to blame for this (100widgets or postscribe), but if you really need this, this is something that can be worked out.

ngAnimate - both source and target template are visible while animation

I try to create a simple fade-in fade-out animation using ngAnimate. The animation looks good except that the container of the view contains both the source and the target template while animation.
How can I show the active template only?
<html ng-app="testApp" lang="en">
<head>
<script src="https://code.angularjs.org/1.3.15/angular.min.js"></script>
<script src="https://code.angularjs.org/1.3.15/angular-route.js"></script>
<script src="https://code.angularjs.org/1.3.15/angular-animate.min.js"></script>
<script type="text/ng-template" id="/page1.html">page1 content</script>
<script type="text/ng-template" id="/page2.html">page2 content</script>
<script>
var app = angular.module('testApp', ['ngRoute','ngAnimate']);
app.config(function($routeProvider) {
$routeProvider.when('/page1', {templateUrl: '/page1.html'});
$routeProvider.when('/page2', {templateUrl: '/page2.html'});
});
</script>
<style>
#container {border: 1px solid black;} /**the container has double height while animation*/
#content {-webkit-transition: opacity .2s linear;}
#content.ng-enter {opacity:0;}
#content.ng-enter.ng-enter-active {opacity:0;}
#content.ng-leave {opacity:1;}
#content.ng-leave.ng-leave-active {opacity:0;}
</style>
</head>
<body>
<div id="container">
<div id="content" ng-view></div>
</div>
</body>
</html>
There are multiple solutions:
Delay the enter animation (transition-delay) with the duration of the leave animation
Remove either the enter or leave animation
Use #content{position: absolute} or #content + #content {margin-top: -$height px} so that the height of the parent is not affected.

Resources