How to trigger a controller function after an Animation in Angular JS - javascript

I have come to a standstill in terms on threading together a sequence of Animations and then a controller action.
What I basically want to do is basically
1. click on a button/div, 2.Trigger an Animation, 3. Once animation is complete run a function in a controller that resets the button/div
I have completed steps 1 & 2 and just need to get the last bit done.
Here is the Button
<button ng-class="{'clicked':clicked, 'correct' : answer.answer == 'correct' }"
ng-click="clicked = true"
ng-repeat='answer in answers'
type="button"
class="btn btn-answers answer-animation">
{{ answer.es }}
</button>
Here is the animation
app.animation('.answer-animation', function(){
return {
beforeAddClass: function(element, className, done){
if (className === 'clicked') {
if( $(element).hasClass('correct') ){
$(element).addClass('animated bounce');
} else {
$(element).addClass('animated wobble');
}
}
else {
done();
}
}
};
});
And here is the last step the controller, I want the trigger the submitAnswer function inside this controller, after the animation has finished. The main bit is submitAnswer
app.controller('Game', function($scope, $http, $location, QA, Rounds ) {
//Reset all QA buckets
QA.reset();
$scope.round = 1;
$scope.playing = true;
QA.setUpGameData();
$scope.answers = QA.answers();
$scope.question = QA.question();
$scope.submitAnswer = function(question, answer){
if($scope.round <= Rounds) {
if(question.en === answer.en){
$scope.round++;
QA.setUpGameData();
$scope.answers = QA.answers();
$scope.question = QA.question();
if($scope.round === Rounds + 1){
$scope.playing = false;
$scope.message = 'Amazing well done!';
$scope.score = ($scope.round-1) * 1000;
}
}
else {
$scope.playing = false;
$scope.message = 'Sorry Wrong Answer :(';
$scope.score = ($scope.round-1) * 1000;
}
}
};
})
I have tried writing the ng-click in the HTML like so
ng-click="clicked = true;submitAnswer(question, answer)"
and then setting a $timeout on the submintAnswer function, but does really get the UX the app deserves.
Again ultimately I want a way to trigger the submitAnswer function in the controller after the animation is completed.

You can get the $scope of an element using,
var $scope = angular.element(element).scope();
Though there are some problems with syncing the scope if this happens.

Related

Refresh checkbox in ng-repeat when data changes

html
<li ng-repeat="col in columns">
<span class="inputH">
<input type="checkbox" value="col.name" ng-if="col.default === true" checked
ng-click="onColSelect(col.name,$event)" id="column_{{$index}}">
<input type="checkbox" value="col.name" ng-if="col.default === false"
ng-click="onColSelect(col.name,$event)" id="column_{{$index}}">
</span>
<span class="textH">{{ 'leadOpportunityHeader.' + col.name | translate }}</span>
</li>
JS
$scope.onColumnSelectCancel = function () {
setTimeout(function () {
var cookieData = $cookieStore.get('selectedColumn');
$scope.unSelectedColoumns = cookieData;
angular.forEach($scope.columns, function (value, key) {
var flag = false;
for (var k = 0; k < cookieData.length; k++) {
if (value.name == cookieData[k]) {
flag = true;
}
}
if (flag == false) {
value.default = false;
flag = true;
}
});
console.log("new column", $scope.columns);
}, 100);
};
What I am really trying to do, is whenever onColumnSelectCancel() is called, I need to refresh all the checkboxes with their check and uncheck properties.My data is changing, but checkboxes state is still not changing. If I checked a checkbox and then I call onColumnSelectCancel() , now the checkboxes should change according to the source $scope.columns
I have applied $apply also, but it didn't worked.
setTimeout is not a compnent of angularjs . So, you have to manually push update using $scope.$apply
Like this
setTimeout(function() {
var cookieData = $cookieStore.get('selectedColumn');
$scope.unSelectedColoumns = cookieData;
$scope.$apply();
}, 100);
Otherwise you can use angular $timeout, here angular manages $apply internally.
Like this
$timeout(function() {
var cookieData = $cookieStore.get('selectedColumn');
$scope.unSelectedColoumns = cookieData;
}, 100);
You have to inject $timeout in your scope.
If your controller code is working fine then just inject $timeout in your controller and change the setTimeout(function () { }) to $timeout(function() {}) and that should work.
So your code will be now:
$scope.onColumnSelectCancel = function () {
$timeout(function () {
var cookieData = $cookieStore.get('selectedColumn');
$scope.unSelectedColoumns = cookieData;
angular.forEach($scope.columns, function (value, key) {
var flag = false;
for (var k = 0; k < cookieData.length; k++) {
if (value.name == cookieData[k]) {
flag = true;
}
}
if (flag == false) {
value.default = false;
flag = true;
}
});
console.log("new column", $scope.columns);
}, 100);
};
This is because setTimeout is an asynchronous execution and Angular is unaware of those changes being done inside that block so we have to tell Angular that something has changed.
$timeout is an Angular wrapper of setTimeout.
Like Anik mentioned, you can also use $scope.$apply which forces Angular to run the digest cycle which may fail at a point when the digest cycle is already in progress. So it is always safe to use $timieout instead.

How to ensure that a scope variable updates and binds before going to view angularjs

In my code, there is this scope variable called $scope.detailsPresent which basically just checks if there is data and displays a different page based on the result.
What happens is that when i do console.log($scope.detailsPresent) the value is correct based on if there is data which the value should be false or if there is no data the value should be true.
But the view doesnt bind the value yet so on the view the value is not yet updated hence it doesnt show the correct page. How do i ensure that the value is updated in the view ? I have tries $scope.$apply() but i get an error so is there anyway to do it?
my_controller.js
angular.module('my.controllers')
.controller('ReferralCtrl', function($scope, $rootScope, $window, $state, $q, $timeout, referralCasesGroupByCaseStatus, AuthenticationService, $ionicLoading) {
$scope.detailsPresent = {myVar: false};
$scope.showCaseStatusFromDashboard = function(number) {
$timeout(function() {
$scope.$applyAsync(function() {
$rootScope.fromDashboard = true;
})
}, 1000, true);
$scope.showCaseStatus(number);
}
$scope.showCaseStatus = function(number) {
if(changedNumber !== 0 && changedNumber !== number) {
changedNumber = number;
}
else {
if(changedNumber > 0) {
$scope.$applyAsync($scope.detailsPresent.myVar = true);
}
}
$timeout(function() {
$scope.$applyAsync(function() {
referralCasesGroupByCaseStatus.showCaseStatus(number, $rootScope.listingDetails).then(function(listingCaseStatus) {
$rootScope.listingByCaseStatus = angular.copy(listingCaseStatus);
if(listingCaseStatus == 0 || listingCaseStatus == undefined || listingCaseStatus == null) {
$scope.detailsPresent.myVar = true;
$scope.changeNumber = true;
$state.go('app.case_status')
}
else {
$scope.detailsPresent.myVar = false;
$scope.noMoreItemsAvailable = false;
$scope.changeNumber = true;
$state.go('app.case_status')
}
})
})
}, 1000);
}
my.html
<ion-view view-title="Case Status">
<ion-content>
<div ng-if="detailsPresent.myVar">
<ng-include src="template='no-listings'"></ng-include>
</div>
<div ng-if="!detailsPresent.myVar">
<ng-include src="'templates/case-listings.html'"></ng-include>
</div>
</ion-content>
</ion-view>
I have been on this for about 6days but no success in sight. Any help is deeply appreciated.
This isn't a full answer, but I have some suggestions. This would work better as a comment, but I don't have enough reputation yet.
Try switching your ng-if attributes to ng-show attributes. I realize this leaves the included elements in the DOM, but it might work for you if the performance is not seriously affected.
Also, the reason you get an error when calling $scope.$apply() is because you're already within a call to $apply(), or rather $applyAsync().
See this answer for more details on $apply()

AngularJs Counter to count up to a specific target number

I am trying to create a counter using Angularjs which should count up to a number which is already present in that division. Here is my html snippet.
<div class="circle-home">
<span class="circle-home-score " id="counterofreviews" data-count="{{noReviews}}">{{noReviews}}</span> REVIEWS
</div>
Now when I am trying to get the value inside the span I get it as {{noReviews}} instead of its value.
Here is my AngularJs code.
var demoApp = angular.module(['demoApp','ngRoute','ui.bootstrap']);
demoApp.controller('SearchController',function ($scope, $http, $facebook, $interval){
$scope.noReviews=100;
$scope.childOnLoad = function() {
$scope.uppercount=$("#counterofreviews").text();
$scope.no_Reviews=0;
console.log($scope.uppercount);
var stop;
stop = $interval(function() {
if ($scope.uppercount >$scope.no_Reviews) {
$scope.noReviews=$scope.no_Reviews;
$scope.no_Reviews++;
console.log('Inside if statement');
} else {
$scope.stopFight();
}
}, 100);
};
$scope.stopFight = function() {
if (angular.isDefined(stop)) {
$interval.cancel(stop);
stop = undefined;
}
};
$scope.childOnLoad();
};
Output of console.log($scope.uppercount) is {{noReviews}}. I am unable to figure out a proper way to do it. Please suggest the correction or any other better method for the same perpose.
Not sure why do you use jQuery to get the #counterofreviews value. Is the value there because it's added from a server side script?
As mentioned in the comments, your code is probably not working because jQuery.text() is returning a string. Using parseInt(text) could work.
Please have a look at the demo below and here at jsfiddle.
It's more Angular and should help you getting started with your counter.
var demoApp = angular.module('demoApp', []); //'ngRoute','ui.bootstrap']);
demoApp.controller('SearchController', function ($scope, $http, $interval) { //$facebook,
$scope.noReviews = 100;
//$scope.childOnLoad = function () {
this.upperCount = 10; //$("#counterofreviews").text();
console.log(this.upperCount);
var stop;
this.startCounter = function () { // needed for re-run on change
//console.log(stop, this);
this.no_Reviews = 0;
if ( angular.isUndefined(stop) )
stop = $interval(checkCount.bind(this), 100);
};
this.startCounter();
//};
function checkCount() {
if (this.upperCount >= this.no_Reviews) {
this.noReviews = this.no_Reviews;
this.no_Reviews++;
//console.log('Inside if statement');
} else {
stopFight();
}
}
function stopFight() {
if (angular.isDefined(stop)) {
$interval.cancel(stop);
stop = undefined;
}
};
//$scope.childOnLoad();
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="demoApp" class="circle-home" ng-controller="SearchController as ctrl">Review max.:
<input ng-model="ctrl.upperCount" ng-change="ctrl.startCounter()"/> <span class="circle-home-score " id="counterofreviews" data-count="{{ctrl.upperCount}}">{{ctrl.noReviews}}</span> REVIEWS</div>

Function inside setTimeout doesn't modify $scope variables / CSS properties

I'm battling with setTimeout(), unable to even grasp what the problem could be. I first thought that it was a scope problem but can't fix it.
I'd like to delay the hiding/unhiding of a DIV to let my CSS transition on opacity kick in but when I click the alert_button to fade then hide the alert, it only fades and I'm left with an invisible div. Delayed $scope.alert_token doesn't switch to 'true' and the opacity of my alert stuck on 1.
app.js :
angular.module('myApp', [])
.controller('myCtrl', function($scope) {
$scope.alert_token = true // hide if true
$scope.alert_message = ""
$scope.p_name = ""
$scope.isInArray = function(arr, item) {
// IF ITEM IS ALREADY IN ARRAY
if (arr.indexOf(item) > -1) {
$scope.alert_token = !$scope.alert_token
$scope.alert_message = "Entry already exists"
setTimeout(function() {
document.getElementById("alert").style.opacity = "1"
}, 305)
}
else ...
}
$scope.submit = function(listType) {
if (listType == "player") {
$scope.isInArray(p_list, $scope.p_name)
$scope.p_name = ""
}
else ...
}
$scope.closeAlert = function() {
document.getElementById("alert").style.opacity = "0"
setTimeout(function() {
$scope.alert_token = !$scope.alert_token
}, 305)
}
Anything happening outside angular's knowledge is not updated to the DOM. In you case its setTimeout. Instead use $timeout.
......
.controller('myCtrl', function($scope, $timeout) {...
^^^^^^^^
//Other code
....
$scope.closeAlert = function() {
document.getElementById("alert").style.opacity = "0"
$timeout(function() {//$timeout
$scope.alert_token = !$scope.alert_token
}, 305)
}
Also since you are using angularJS, to update CSS properties I would recommend you to use ngClass and ngStyle

Trying to bind variables that are changed in event

I have an AngularJS page which contains the following buttons:
<button class="ui button" ng-click="startTimer()" ng-show="!timerRunning">START</button>
<button class="ui button" ng-click="stopTimer()" ng-show="timerRunning">STOP</button>
I also have this piece of code defined in my controller:
$scope.timerRunning = false;
$scope.timerDone = false;
$scope.startTimer = function () {
$scope.$broadcast('timer-start');
$scope.timerRunning = true;
$scope.timerDone = false;
};
$scope.stopTimer = function () {
$scope.$broadcast('timer-stop');
$scope.timerRunning = false;
$scope.timerDone = true;
};
$scope.$on('timer-stopped', function (event, data) {
console.log('Timer Stopped - data = ', data);
});
$scope.$on('timer-tick', function (event, args) {
if (args.millis == 0) {
$scope.stopTimer();
}
});
I would expect that when timer hits 0 (it's a countdown timer taken from here) the flag timerDone and timerRunning would be changed and so would the buttons display. But in fact it doesn't happen. I debugged it and saw that I get into the stopTimer() method and that the variables are changed but the buttons stay hidden/shown respectively as they were before the timer hit 0.
I guess I'm missing something with the scope here as I'm pretty new to angular but I just couldn't figure out how to overcome this.
Looks like it was a prototypal inheritance issue like Rob J was right. What I did was to fix the even listener as follows (added $scope.$apply()):
$scope.$on('timer-tick', function (event, args) {
if (args.millis == 0) {
$scope.stopTimer();
$scope.$apply();
}
});

Categories

Resources