AngularJS with CSS3 Animations - Slideshow Issues - angularjs

I am attempting to create a simple content slider with AngularJS. I am following this tutorial:https://www.sitepoint.com/creating-slide-show-plugin-angularjs/
However, the content is "jumping". The next slide appears below the current one; it does not appear in its appropriate place while the other is fading out. Here is an image that hopefully clarifies the issue:
The HTML:
<div class="row" id='topSlider' ng-controller="SliderCtrl">
<h2 class="col-sm-12">Latest Stories</h2>
<div class="col-sm-12">
<div class="slider" ng-repeat="story in stories" ng-show="story.visible">
<a class="containerLink" href="View/ViewPost?postID={{story.StoryID}}">
<div class="slide">
<h3 class="cursive">{{story.Title}}</h3>
<blockquote>{{story.StoryPara}}</blockquote>
</div>
</a>
</div>
</div>
<div class="row" id="arrows">
<span class="glyphicon glyphicon-arrow-left"></span>
<span class="glyphicon glyphicon-arrow-right"></span>
</div>
The controller:
app.controller("SliderCtrl", ["$scope", "$http", "$timeout", function ($scope, $http, $timeout) {
$scope.currentIndex = 0;
$http.post("/StoryWall/Home/GetLatestStories").then(function (response) {
$scope.stories = response.data;
$scope.stories[0].visible = true;
},
function (response) {
$scope.stories = response.data;
});
$scope.next = function () {
$scope.currentIndex < $scope.stories.length - 1 ? $scope.currentIndex++ : $scope.currentIndex = 0;
};
$scope.prev = function () {
$scope.currentIndex > 0 ? $scope.currentIndex-- : $scope.currentIndex = $scope.stories.length - 1;
};
$scope.$watch("currentIndex", function () {
$scope.stories.forEach(function (story) {
story.visible = false;
});
$scope.stories[$scope.currentIndex].visible = true;
});
var timer;
var sliderFunction = function () {
timer = $timeout(function () {
$scope.next();
timer = $timeout(sliderFunction, 2000);
}, 5000);
};
sliderFunction();
}]);
and the CSS:
.slider.ng-hide-add, .slider.ng-hide-remove {
-webkit-transition:all linear 1s;
-moz-transition:all linear 1s;
-o-transition:all linear 1s;
transition:all linear 1s;
display:block!important;
clear: none;
float: none;
}
.slider.ng-hide-add.ng-hide-add-active,
.slider.ng-hide-remove {
opacity: 0;
}
.slider.ng-hide-add,
.slider.ng-hide-remove.ng-hide-remove-active {
opacity: 1;
}
Please note, if I remove the CSS it works just fine. The containers hide and show as they should. However, I would like to be able to apply some animations. I am new to both CSS animations and AngularJS; I apologize if this is a redudant or obvious issue.

check this css -> https://daneden.github.io/animate.css/
it's my script
var app=angular.module("app",[]);
app.controller("control",function($scope){
$scope.tab="1";
});
you focus on ng-show method
for example
all slider item in div like that
<div ng-show="tab==1" class="slider-item animated slideInRight">
...............
</div>
and your button must be
<button ng-mouseover="tab=1">1</button>
I hope I could help you

Related

toggling one directive effects other dircetive inside ngrepeat

I wrote an angularjs directive to show and hide ajax spinners. The visibility of the spinner is toggled by show and hide buttons whose functionality is written inside the MainController. There is a variable inside the controller which is set to true and false based on the button click. This variable is passed to the directive using isolate scope. When I try to toggle one spinner, all the other spinners are also visible. How can I change my code to only toggle the particular spinner.
https://plnkr.co/edit/AFmBVbHaBPk66T7UjPC5?p=preview
// Code goes here
angular.module('app',[])
.controller('MainController',[MainController])
.directive('loadingDirective',[loadingDirective]);
function MainController(){
var mc = this;
mc.showMe = showMe;
mc.hideMe = hideMe;
mc.loading = false;
function showMe(){
mc.loading = true;
}
function hideMe(){
mc.loading = false;
}
}
function loadingDirective() {
return {
restrict: 'E',
replace:true,
scope:{
loading:"=loading"
},
template: '<span class="spinner">Loading…</span>',
link: function (scope, element, attr) {
scope.$watch('loading', function (val) {
if (val)
$(element).show();
else
$(element).hide();
});
}
};
}
/* Styles go here */
.spinner {
position: relative;
/* [1] */
display: inline-block;
width: 1em;
/* [2] */
height: 1em;
/* [2] */
font-size: 32px;
/* [3] */
border-bottom: 1px solid;
/* [4] */
vertical-align: middle;
overflow: hidden;
/* [5] */
text-indent: 100%;
/* [5] */
-webkit-animation: 0.5s spinner linear infinite;
animation: 0.5s spinner linear infinite;
/**
* 1. Make the spinner a circle.
*/
/**
* The (optically) non-spinning part of the spinner.
*
* 1. Border around entire element fills in the rest of the ring.
* 2. Paler than the part that appears to spin.
*/
}
.spinner, .spinner:after {
border-radius: 100%;
/* [1] */
}
.spinner:after {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border: 1px solid;
/* [1] */
opacity: 0.5;
/* [2] */
}
/**
* Size variants (built by adjusting `font-size`).
*/
.spinner--small {
font-size: 16px;
}
.spinner--large {
font-size: 64px;
}
/**
* Color overrides.
*/
.spinner--light {
color: #fff;
}
.spinner--dark {
color: #333;
}
#-webkit-keyframes spinner {
to {
-webkit-transform: rotate(360deg);
}
}
#keyframes spinner {
to {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body>
<h1>Hello Plunker!</h1>
<div ng-controller="MainController as mc">
<div ng-repeat="i in [1,2,3,4,5]">
<loading-directive loading="mc.loading"></loading-directive>
<button ng-click="mc.showMe()">show</button>
<button ng-click="mc.hideMe()">hide</button>
</div>
</div>
</body>
</html>
If you want the spinners to have their own states, then they should be controlled by different variables.
In your example it is achievable by using an array to hold the variables
<div ng-repeat="i in [1,2,3,4,5]">
<loading-directive loading="mc.loading[i]"></loading-directive>
<button ng-click="mc.show(i)">show</button>
<button ng-click="mc.hide(i)">hide</button>
</div>
mc.loading = {};
function show(i){
mc.loading[i] = true;
}
function hide(i){
mc.loading[i] = false;
}
In a more real case example where you have some data and you use ng-repeat over them, you should assign the loading states inside the elements themselves.
This is a common technique to assign state to each items in ng-repeat
mc.fruits = [
{name:"apple"},
{name:"orange"},
{name:"starfruit"}
]
function load(fruit) { fruit.loading = true; }
function noLoad(fruit) { fruit.loading = false; }
<div ng-repeat="fruit in mc.fruits">
<loading-directive loading="fruit.loading"></loading-directive>
{{fruit.name}}
<button ng-click="mc.load(fruit)">show</button>
<button ng-click="mc.noLoad(fruit)">hide</button>
</div>
Working Plunkr: https://plnkr.co/edit/peGDxYJzKJgiHuPp4zmQ
You needed to define the isolated scope in the directive correctly. Essentially, your directive was still dependent on the controller as you were using the same variable mc.loading to determine the state of all directive instances.
By moving the deterministic variable $scope.loading as well as the buttons inside the directive, we are completely isolating each directive instance and making them all completely independent units.
HTML:
<div ng-controller="MainController as mc">
<div ng-repeat="i in [1,2,3,4,5]">
<loading-directive></loading-directive>
</div>
</div>
JS:
angular.module('app',[])
.controller('MainController',[MainController])
.directive('loadingDirective',[loadingDirective]);
function MainController(){
}
function loadingDirective() {
return {
restrict: 'E',
replace:true,
scope:{},
template: '<div><span ng-if="loading" class="spinner">Loading…</span>'
+ '<button ng-click="showMe()">show</button>'
+ '<button ng-click="hideMe()">hide</button></div>',
controller: function($scope) {
$scope.showMe = showMe;
$scope.hideMe = hideMe;
function showMe(){
$scope.loading = true;
}
function hideMe(){
$scope.loading = false;
}
}
};
}
The loading variable watched is common for all the directives used, hence when the model is changed the watch condition runs 5 times in your case, removing all the spinners.
I used the index to see what is being hidden or shown,
Updated fiddle: https://plnkr.co/edit/Jjfk6v7TJZHlQicM45ln?p=preview
HTML
<div ng-repeat="i in [1,2,3,4,5]">
<loading-directive data-index="{{$index}}" loading="mc.loading" offset="mc.offset"></loading-directive>
<button ng-click="mc.showMe($index)">show</button>
<button ng-click="mc.hideMe($index)">hide</button>
</div>
Angular
angular.module('app',[])
.controller('MainController',[MainController])
.directive('loadingDirective',[loadingDirective]);
function MainController(){
var mc = this;
mc.showMe = showMe;
mc.hideMe = hideMe;
mc.loading = false;
mc.offset =-1;
function showMe(offset){
mc.loading = true;
mc.offset = offset;
}
function hideMe(offset){
mc.loading = false;
mc.offset = offset;
console.log(offset);
}
}
function loadingDirective() {
return {
restrict: 'E',
replace:true,
scope:{
loading:"=loading",
offset:"=offset"
},
template: '<span class="spinner">Loading…</span>',
link: function (scope, element, attr) {
scope.$watch('[loading, offset]' , function (val) {
if(attr.index == scope.offset || scope.offset == -1){
if (val[0])
element.show();
else
element.hide();
}
});
}
};
}
SCRIPT:
function showMe(i){
mc.loading = true;
i=true;
}
IN HTML ADD THIS
<div ng-repeat="i in [1,2,3,4,5]">
<span><loading-directive loading="mc.loading" ng-show="i==true"></loading-directive>
<button ng-click="mc.showMe(i)">show</button></span>
<button ng-click="mc.hideMe()">hide</button>
</div>

cordova ionic app seems to load controller twice

I'm having some issues with an app I'm creating in Cordova (5.3.3) using ionic (1.6.5) and angular js and building for iOS and Android.
I have a menu item that when I click should load the next state with other menu items. However it seems like it loads the next state once without the animation, then loads the state again with the animation resulting in a "jerky" effect. It's best shown in a video I have uploaded here: https://www.youtube.com/watch?v=YCEQeqFyNl4&feature=youtu.be
I was wondering if anybody has an idea of what could be the issue here and where I should start looking, or if there are known bugs about this?
Any help would be appreciated!
// the controller
.controller('ProgramCtrl', ['$scope', 'FacProgram',
function($scope, FacProgram) {
$scope.refresh = function() {
FacProgram.refresh()
.finally(function() {
$scope.$broadcast('scroll.refreshComplete');
});
};
$scope.temp_articles = function() {
return FacProgram.all();
};
}
])
.controller('ProgramDetailCtrl', ['$scope', '$stateParams', 'FacProgram',
function($scope, $stateParams, FacProgram) {
$scope.art = FacProgram.get($stateParams.programId);
}
])
// The factory in a different js file:
.factory('FacProgram', ['$http', '$q', 'FacJson',
function($http, $q, FacJson) {
var temp_articles = [];
function findHeader(src, headTag, index) {
var head = $(src).find(headTag).get(0);
temp_articles[index].headlinethumb = head.textContent;
temp_articles[index].headline = head.textContent;
}
function refresh() {
var q = $q.defer();
... // A fucntion that get's URL's from .json file
////////////////////////////////////////////////////////////
angular.forEach(urls, function(URL, index) {
//add the articles to the dictionary
temp_articles.push({
inx: index,
img: img_logos[index]
});
$http.get(URL)
.then(function(sc) {
// The will parse the html looking for thumbnail text, main body text etc
var src = sc.data.substring(sc.data.indexOf('<html>'));
... // code that parses website for content etc
////////////////////////////////////////////////////////
q.resolve();
},
function() {
q.reject();
});
});
});
return q.promise
}
return {
all: function() {
return temp_articles;
},
get: function(programId) {
return temp_articles[programId];
},
refresh: function() {
console.log('DPG programs refresh triggered');
temp_articles = []; // a catch to prevent duplicates and to allow other templates to use without
//contamination
return refresh()
}
}
}]);
<!-- the start state for progams of the DPG -->
<ion-view>
<ion-content>
<div class="list">
<a class="item item-thumbnail-left item-icon-right item-text-wrap" ng-repeat="art in temp_articles()" href="#/program/{{$index}}">
<img ng-src="{{art.img}}">
<h2 class="padding-top" style="font-weight: 300;">{{art.headlinethumb}}</h2>
<i class="icon ion-chevron-right" style="font-size: 18px"></i>
</a>
</div>
</ion-content>
</ion-view>
<!-- the datailed view of the application -->
<ion-view>
<ion-content>
<div style="text-align: left; padding-top: 30px; padding-left: 20px; padding-right: 20px">
<h2 style="font-weight: 500; font-size: 17px">{{art.headline}}</h2>
</div>
<!--<p style="padding-left: 10px; font-size: 13px; font-weight: 300">Datum</p>-->
<div style="text-align: center">
<img style="max-height: 300px; max-width: 300px; width: auto;" ng-src="{{art.img}}">
</div>
<div class="padding" style="font-weight: 300">
<div ng-bind-html="art.text | hrefToJS"></div>
</div>
</ion-content>
</ion-view>
the same happened to me if you put your controller in app.js try removing it from there and only applying it in controller or viceversa.
your state in app.js should look like this:
.state('state-name', {
url: '/state-name',
templateUrl: "templates/walkthrough/state-name.html"
//Notice no controller here
})

Programatically changing ngShow doesn't make use of ngAnimate classes

I'm trying to trigger ngShow via my controller, it works, but it doesn't make use of the ngAnimate classes that I need to get a fade transition.
It works like it should if I use a button to toggle ngShow, but not if I toggle it programmatically. Is this expected behavior? Can I get around it?
Plunk:
http://plnkr.co/edit/swJDP1KBBxcRfK9auYPs?p=preview
<body ng-controller="MainCtrl">
<input type="checkbox" ng-model="visible">
<div ng-show="visible" class="wrap" role="document">
Hello
</div>
</body>
var app = angular.module( "app", ['ngAnimate']);
app.run(function($rootScope) {
$rootScope.visible = false;
});
app.controller('MainCtrl', function($rootScope, $scope) {
$rootScope.visible = true;
});
.wrap.ng-hide-add-active {
display: block!important;
transition: 0.5s ease-in-out all;
}
.wrap.ng-hide-remove-active {
display: block!important;
transition: 0.5s ease-in-out all;
transition-delay: 0.5s;
}
.wrap.ng-hide {
opacity: 0;
}
You're run block and controller code likely get executed in the same digest cycle so Angular doesn't see the visible variable changing. This would work if you put your controller code in a timeout. e.g.
app.controller('MainCtrl', function($rootScope, $scope, $timeout) {
$timeout(function() {
$rootScope.visible = true;
});
});
http://plnkr.co/edit/5IhGE3ol5kI64OlT1e8v?p=preview

AngularJS : service - controller - view data binding

I am trying to bind data between a service, controller and a view. Below, I have provided plunker link for the app.
Basically 'dataService' has a function to find geolocation. The function sets the variable dataService.locationDone.
The dataService is assigned to $scope.data in the 'HomeController'.
In home.html, I refer the value of data.locationDone to show different labels.
The issue is, after the locationDone variable is modified in the dataService, the labels in the home.html does not change.
plunker link: http://embed.plnkr.co/BomSztgC7PzGMzJyDKoF/preview
Service:
storelocator.factory('dataService', [function() {
var data = {
locationDone: 0,
position: null,
option: null
}
var getLocation = function() {
data.locationDone = 0;
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (pos) {
data.locationDone = 1;
data.position = pos;
console.log('got location ', data.locationDone);
}, function () {
data.locationDone = 2;
});
} else {
data.locationDone = 3;
}
};
getLocation();
return data;
}]);
Controller:
storelocator.controller('HomeController', ['$scope', '$location', 'dataService', function ($scope, $location, dataService) {
$scope.data = dataService;
console.log('Home ctrl', $scope.data.locationDone);
$scope.fn = {};
$scope.fn.showOne = function () {
$scope.data.option = 3;
$location.path('/map');
};
$scope.fn.showAll = function () {
$scope.data.option = 99;
$location.path('/map');
};
}]);
view:
<div layout="column" layout-align="start center">
<div layout="column" layout-margin layout-padding>
<div flex layout layout-margin layout-padding>
<md-whiteframe flex class="md-whiteframe-z1" layout layout-align="center center" ng-show="data.locationDone==0">
<span flex>Awaiting location information.</span>
</md-whiteframe>
<md-whiteframe flex class="md-whiteframe-z1" layout layout-align="center center" ng-show="data.locationDone==2"><span flex>Error in getting location.</span>
</md-whiteframe>
<md-whiteframe flex class="md-whiteframe-z1" layout layout-align="center center" ng-show="data.locationDone==3"><span flex>The browser does not support location.</span>
</md-whiteframe>
</div>
<md-button flex ng-show="data.locationDone==1" class="md-accent md-default-theme md-raised" ng-click="fn.showOne()">Find Nearest Three Stores</md-button>
<div flex></div>
<md-button ng-show="data.locationDone==1" flex class="md-accent md-default-theme md-raised" ng-click="fn.showAll()">Find All Stores</md-button>
</div>
</div>
You are updating the variable locationDone defined in the scope of the service. It will not have any impact on the object that you have returned during in the service (when updated later). Instead predefine an object in your service and update the property on the reference rather than a variable.
Also note that since you are using native async api, which runs out of the angular context you would need to manually invoke digest cycle by doing $rootScope.$apply() in your case or just use $timeout wrap your updates in $timeout. However better approach would be to property create an api in dataService with method returning $q promise.
With timeout
.factory('dataService', ['$timeout', function($timeout) {
var service = { //Define here;
locationDone: 0,
position: null,
option: null
}
var getLocation = function() {
service.locationDone = 0;
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (pos) {
$timeout(function(){
service.locationDone = 1;
service.position = pos;
});
}, function () {
$timeout(function(){
service.locationDone = 2;
});
});
} else {
service.locationDone = 3;
}
};
getLocation();
return service; //return it
}]);
With qpromise implementation:
.factory('dataService', ['$q', function($q) {
return {
getLocation:getLocation
};
function getLocation() {
if (navigator.geolocation) {
var defer = $q.defer();
navigator.geolocation.getCurrentPosition(function (pos) {
defer.resolve({locationDone : 1,position : pos})
}, function () {
defer.resolve({ locationDone : 2});
});
return defer.promise;
}
return $q.when({locationDone : 3});
};
}]);
And in your controller:
$scope.data = {};
dataService.getLocation().then(function(result){
$scope.data = result;
});
Demo
angular.module('app', []).controller('HomeController', ['$scope', '$location', 'dataService', function ($scope, $location, dataService) {
$scope.data = {};
dataService.getLocation().then(function(result){
$scope.data = result;
})
console.log('Home ctrl', $scope.data.locationDone);
$scope.fn = {};
$scope.fn.showOne = function () {
$scope.data.option = 3;
$location.path('/map');
};
$scope.fn.showAll = function () {
$scope.data.option = 99;
$location.path('/map');
};
}]).factory('dataService', ['$q', function($q) {
return {
getLocation:getLocation
};
function getLocation() {
if (navigator.geolocation) {
var defer = $q.defer();
navigator.geolocation.getCurrentPosition(function (pos) {
defer.resolve({locationDone : 1,position : pos})
}, function () {
defer.resolve({ locationDone : 2});
});
return defer.promise;
}
return $q.when({locationDone : 3});
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="HomeController">
<div layout="column" layout-align="start center">
<div layout="column" layout-margin layout-padding>
<div flex layout layout-margin layout-padding>
<md-whiteframe flex class="md-whiteframe-z1" layout layout-align="center center" ng-show="data.locationDone==0">
<span flex>Awaiting location information.</span>
</md-whiteframe>
<md-whiteframe flex class="md-whiteframe-z1" layout layout-align="center center" ng-show="data.locationDone==2"><span flex>Error in getting location.</span>
</md-whiteframe>
<md-whiteframe flex class="md-whiteframe-z1" layout layout-align="center center" ng-show="data.locationDone==3"><span flex>The browser does not support location.</span>
</md-whiteframe>
</div>
<md-button flex ng-show="data.locationDone==1" class="md-accent md-default-theme md-raised" ng-click="fn.showOne()">Find Nearest Three Stores</md-button>
<div flex></div>
<md-button ng-show="data.locationDone==1" flex class="md-accent md-default-theme md-raised" ng-click="fn.showAll()">Find All Stores</md-button>
</div>
</div>
</div>

How to use a keypress event in AngularJS?

I want to catch the enter key press event on the textbox below. To make it more clear I am using a ng-repeat to populate the tbody. Here is the HTML:
<td><input type="number" id="closeqty{{$index}}" class="pagination-right closefield"
data-ng-model="closeqtymodel" data-ng-change="change($index)" required placeholder="{{item.closeMeasure}}" /></td>
This is my module:
angular.module('components', ['ngResource']);
I am using a resource to populate the table and my controller code is:
function Ajaxy($scope, $resource) {
//controller which has resource to populate the table
}
You need to add a directive, like this:
Javascript:
app.directive('myEnter', function () {
return function (scope, element, attrs) {
element.bind("keydown keypress", function (event) {
if(event.which === 13) {
scope.$apply(function (){
scope.$eval(attrs.myEnter);
});
event.preventDefault();
}
});
};
});
HTML:
<div ng-app="" ng-controller="MainCtrl">
<input type="text" my-enter="doSomething()">
</div>
An alternative is to use standard directive ng-keypress="myFunct($event)"
Then in your controller you can have:
...
$scope.myFunct = function(keyEvent) {
if (keyEvent.which === 13)
alert('I am an alert');
}
...
My simplest approach using just angular build-in directive:
ng-keypress, ng-keydown or ng-keyup.
Usually, we want add keyboard support for something that already handled by ng-click.
for instance:
<a ng-click="action()">action</a>
Now, let's add keyboard support.
trigger by enter key:
<a ng-click="action()"
ng-keydown="$event.keyCode === 13 && action()">action</a>
by space key:
<a ng-click="action()"
ng-keydown="$event.keyCode === 32 && action()">action</a>
by space or enter key:
<a ng-click="action()"
ng-keydown="($event.keyCode === 13 || $event.keyCode === 32) && action()">action</a>
if you are in modern browser
<a ng-click="action()"
ng-keydown="[13, 32].includes($event.keyCode) && action()">action</a>
More about keyCode:
keyCode is deprecated but well supported API, you could use $evevt.key in supported browser instead.
See more in https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
Another simple alternative:
<input ng-model="edItem" type="text"
ng-keypress="($event.which === 13)?foo(edItem):0"/>
And the ng-ui alternative:
<input ng-model="edItem" type="text" ui-keypress="{'enter':'foo(edItem)'}"/>
Here is what I figured out when I was building an app with a similar requirement,
it doesn't require writing a directive and it's relatively simple to tell what it does:
<input type="text" ng-keypress="($event.charCode==13)?myFunction():return" placeholder="Will Submit on Enter">
You can use ng-keydown
="myFunction($event)" as attribute.
<input ng-keydown="myFunction($event)" type="number">
myFunction(event) {
if(event.keyCode == 13) { // '13' is the key code for enter
// do what you want to do when 'enter' is pressed :)
}
}
html
<textarea id="messageTxt"
rows="5"
placeholder="Escriba su mensaje"
ng-keypress="keyPressed($event)"
ng-model="smsData.mensaje">
</textarea>
controller.js
$scope.keyPressed = function (keyEvent) {
if (keyEvent.keyCode == 13) {
alert('presiono enter');
console.log('presiono enter');
}
};
You can also apply it to a controller on a parent element. This example can be used to highlight a row in a table by pressing up/down arrow keys.
app.controller('tableCtrl', [ '$scope', '$element', function($scope, $element) {
$scope.index = 0; // row index
$scope.data = []; // array of items
$scope.keypress = function(offset) {
console.log('keypress', offset);
var i = $scope.index + offset;
if (i < 0) { i = $scope.data.length - 1; }
if (i >= $scope.data.length) { i = 0; }
};
$element.bind("keydown keypress", function (event) {
console.log('keypress', event, event.which);
if(event.which === 38) { // up
$scope.keypress(-1);
} else if (event.which === 40) { // down
$scope.keypress(1);
} else {
return;
}
event.preventDefault();
});
}]);
<table class="table table-striped" ng-controller="tableCtrl">
<thead>
<tr>
<th ng-repeat="(key, value) in data[0]">{{key}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in data track by $index" ng-click="draw($index)" ng-class="$index == index ? 'info' : ''">
<td ng-repeat="(key, value) in row">{{value}}</td>
</tr>
</tbody>
</table>
Trying
ng-keypress="console.log($event)"
ng-keypress="alert(123)"
did nothing for me.
Strangley the sample at https://docs.angularjs.org/api/ng/directive/ngKeypress, which does ng-keypress="count = count + 1", works.
I found an alternate solution, which has pressing Enter invoke the button's ng-click.
<input ng-model="..." onkeypress="if (event.which==13) document.getElementById('button').click()"/>
<button id="button" ng-click="doSomething()">Done</button>
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
Informe your name:<input type="text" ng-model="pergunta" ng-keypress="pressionou_enter($event)" ></input>
<button ng-click="chamar()">submit</button>
<h1>{{resposta}}</h1>
</div>
<script>
var app = angular.module('myApp', []);
//create a service mitsuplik
app.service('mitsuplik', function() {
this.myFunc = function (parametro) {
var tmp = "";
for (var x=0;x<parametro.length;x++)
{
tmp = parametro.substring(x,x+1) + tmp;
}
return tmp;
}
});
//Calling our service
app.controller('myCtrl', function($scope, mitsuplik) {
$scope.chamar = function() {
$scope.resposta = mitsuplik.myFunc($scope.pergunta);
};
//if mitsuplik press [ENTER], execute too
$scope.pressionou_enter = function(keyEvent) {
if (keyEvent.which === 13)
{
$scope.chamar();
}
}
});
</script>
</body>
</html>
This is an extension on the answer from EpokK.
I had the same problem of having to call a scope function when enter is pushed on an input field. However I also wanted to pass the value of the input field to the function specified. This is my solution:
app.directive('ltaEnter', function () {
return function (scope, element, attrs) {
element.bind("keydown keypress", function (event) {
if(event.which === 13) {
// Create closure with proper command
var fn = function(command) {
var cmd = command;
return function() {
scope.$eval(cmd);
};
}(attrs.ltaEnter.replace('()', '("'+ event.target.value +'")' ));
// Apply function
scope.$apply(fn);
event.preventDefault();
}
});
};
});
The use in HTML is as follows:
<input type="text" name="itemname" lta-enter="add()" placeholder="Add item"/>
Kudos to EpokK for his answer.
What about this?:
<form ng-submit="chat.sendMessage()">
<input type="text" />
<button type="submit">
</form>
Now when you push enter key after write something in your input, the form know how to handle it.
Some example of code that I did for my project.
Basically you add tags to your entity.
Imagine you have input text, on entering Tag name you get drop-down menu with preloaded tags to choose from, you navigate with arrows and select with Enter:
HTML + AngularJS v1.2.0-rc.3
<div>
<form ng-submit="addTag(newTag)">
<input id="newTag" ng-model="newTag" type="text" class="form-control" placeholder="Enter new tag"
style="padding-left: 10px; width: 700px; height: 33px; margin-top: 10px; margin-bottom: 3px;" autofocus
data-toggle="dropdown"
ng-change="preloadTags()"
ng-keydown="navigateTags($event)">
<div ng-show="preloadedTags.length > 0">
<nav class="dropdown">
<div class="dropdown-menu preloadedTagPanel">
<div ng-repeat="preloadedTag in preloadedTags"
class="preloadedTagItemPanel"
ng-class="preloadedTag.activeTag ? 'preloadedTagItemPanelActive' : '' "
ng-click="selectTag(preloadedTag)"
tabindex="{{ $index }}">
<a class="preloadedTagItem"
ng-class="preloadedTag.activeTag ? 'preloadedTagItemActive' : '' "
ng-click="selectTag(preloadedTag)">{{ preloadedTag.label }}</a>
</div>
</div>
</nav>
</div>
</form>
</div>
Controller.js
$scope.preloadTags = function () {
var newTag = $scope.newTag;
if (newTag && newTag.trim()) {
newTag = newTag.trim().toLowerCase();
$http(
{
method: 'GET',
url: 'api/tag/gettags',
dataType: 'json',
contentType: 'application/json',
mimeType: 'application/json',
params: {'term': newTag}
}
)
.success(function (result) {
$scope.preloadedTags = result;
$scope.preloadedTagsIndex = -1;
}
)
.error(function (data, status, headers, config) {
}
);
} else {
$scope.preloadedTags = {};
$scope.preloadedTagsIndex = -1;
}
};
function checkIndex(index) {
if (index > $scope.preloadedTags.length - 1) {
return 0;
}
if (index < 0) {
return $scope.preloadedTags.length - 1;
}
return index;
}
function removeAllActiveTags() {
for (var x = 0; x < $scope.preloadedTags.length; x++) {
if ($scope.preloadedTags[x].activeTag) {
$scope.preloadedTags[x].activeTag = false;
}
}
}
$scope.navigateTags = function ($event) {
if (!$scope.newTag || $scope.preloadedTags.length == 0) {
return;
}
if ($event.keyCode == 40) { // down
removeAllActiveTags();
$scope.preloadedTagsIndex = checkIndex($scope.preloadedTagsIndex + 1);
$scope.preloadedTags[$scope.preloadedTagsIndex].activeTag = true;
} else if ($event.keyCode == 38) { // up
removeAllActiveTags();
$scope.preloadedTagsIndex = checkIndex($scope.preloadedTagsIndex - 1);
$scope.preloadedTags[$scope.preloadedTagsIndex].activeTag = true;
} else if ($event.keyCode == 13) { // enter
removeAllActiveTags();
$scope.selectTag($scope.preloadedTags[$scope.preloadedTagsIndex]);
}
};
$scope.selectTag = function (preloadedTag) {
$scope.addTag(preloadedTag.label);
};
CSS + Bootstrap v2.3.2
.preloadedTagPanel {
background-color: #FFFFFF;
display: block;
min-width: 250px;
max-width: 700px;
border: 1px solid #666666;
padding-top: 0;
border-radius: 0;
}
.preloadedTagItemPanel {
background-color: #FFFFFF;
border-bottom: 1px solid #666666;
cursor: pointer;
}
.preloadedTagItemPanel:hover {
background-color: #666666;
}
.preloadedTagItemPanelActive {
background-color: #666666;
}
.preloadedTagItem {
display: inline-block;
text-decoration: none;
margin-left: 5px;
margin-right: 5px;
padding-top: 5px;
padding-bottom: 5px;
padding-left: 20px;
padding-right: 10px;
color: #666666 !important;
font-size: 11px;
}
.preloadedTagItem:hover {
background-color: #666666;
}
.preloadedTagItemActive {
background-color: #666666;
color: #FFFFFF !important;
}
.dropdown .preloadedTagItemPanel:last-child {
border-bottom: 0;
}
I'm a bit late .. but i found a simpler solution using auto-focus .. This could be useful for buttons or other when popping a dialog :
<button auto-focus ng-click="func()">ok</button>
That should be fine if you want to press the button onSpace or Enter clicks .
here's my directive:
mainApp.directive('number', function () {
return {
link: function (scope, el, attr) {
el.bind("keydown keypress", function (event) {
//ignore all characters that are not numbers, except backspace, delete, left arrow and right arrow
if ((event.keyCode < 48 || event.keyCode > 57) && event.keyCode != 8 && event.keyCode != 46 && event.keyCode != 37 && event.keyCode != 39) {
event.preventDefault();
}
});
}
};
});
usage:
<input number />
you can use ng-keydown , ng-keyup , ng-press such as this .
to triger a function :
<input type="text" ng-keypress="function()"/>
or if you have one condion such as when he press escape (27 is the key
code for escape)
<form ng-keydown=" event.which=== 27?cancelSplit():0">
....
</form>
I think using document.bind is a bit more elegant
constructor($scope, $document) {
var that = this;
$document.bind("keydown", function(event) {
$scope.$apply(function(){
that.handleKeyDown(event);
});
});
}
To get document to the controller constructor:
controller: ['$scope', '$document', MyCtrl]
(function(angular) {
'use strict';
angular.module('dragModule', [])
.directive('myDraggable', ['$document', function($document) {
return {
link: function(scope, element, attr) {
element.bind("keydown keypress", function (event) {
console.log('keydown keypress', event.which);
if(event.which === 13) {
event.preventDefault();
}
});
}
};
}]);
})(window.angular);
All you need to do to get the event is the following:
console.log(angular.element(event.which));
A directive can do it, but that is not how you do it.

Resources