I am using UI-Router in a project. I am using the state resolve functionality provided by the router. When I want to display a loader in between two states and I change states very often the spinner pops up but don't disappear anymore.
The same effect can be found here.
http://rp.js.org/angular-ui-view-spinner/example/index.html
If one clicks fast between the two states the loader won't hide.
I am waiting for the stateChangeStart broadcast and display the loader and on statChangeSuccess, stateChangeError and viewContentLoaded I want't to hide the loader. The stateChangeStart gets fired but stateChangeSuccess doesn't. Any idea why this behaviour appears?
I am using angular-ui-router in version 0.2.18
Here the Code for show and hide:
$rootScope.$on('$stateChangeStart', showLoading);
$rootScope.$on('$stateChangeSuccess', hideLoading);
$rootScope.$on('$stateChangeError', hideLoading);
$rootScope.$on('$viewContentLoaded', hideLoading);
function showLoading() {
$timeout(function () {
angular.element('.loading-indicator').show();
}, 50);
}
function hideLoading() {
$timeout(function () {
angular.element('.loading-indicator').hide();
}, 50);
}
You need to cancel previous timer. See documentation here: https://docs.angularjs.org/api/ng/service/$timeout.
var yourTimer;
function showLoading() {
if (yourTimer) {$timeout.cancel(yourTimer);}
yourTimer = $timeout(function () {
angular.element('.loading-indicator').show();
}, 50);
}
function hideLoading() {
if (yourTimer) {$timeout.cancel(yourTimer);}
yourTimer = $timeout(function () {
angular.element('.loading-indicator').hide();
}, 50);
}
I can't understand for which reason you use a timer for loading spinner, i recommend use only css if you use for the animation.
Here a little of my code:
HTML
<div ui-view class="fade container-fluid"></div>
<div class="spinner modal-viewer" hidden>
<div class="transparente"></div>
<div class="contenedor">
<div class="center-middle">
<div class="sk-circle">
<div class="sk-circle1 sk-child"></div>
<div class="sk-circle2 sk-child"></div>
...
<div class="sk-circle11 sk-child"></div>
<div class="sk-circle12 sk-child"></div>
</div>
</div>
</div>
</div>
You need manage them out of the uiView, because when you let out of a state, you will lose control of the state controller, then you can't disappear the spinner.
For that, put the spinner out of the uiView element, as above example.
JS
document.querySelector('.spinner')['style'].display = 'none';
My apologies for my poor previous answer.
Related
I am trying to prevent scrolling only when the lightbox component is open, but cannot seem to do so. I hope to not use any outside libraries or plug-ins to do this.
My App.vue contains the "LightBox" component, so I am assuming the prevent scrolling function should live in the App.vue as well.
App.vue snippet:
<template>
<div class="SocialAlbumWidget">
<div v-if="isModalVisible && media[activeIndex]">
<LightBox
...
/>
I currently have a "showModal ()" function in the "methods" section, so was thinking of passing that through another function.
Methods:
mothods: {
...
showModal () {
this.isModalVisible = true
},
closeModal () {
this.isModalVisible = false
}
I expect the body to have scroll when the"Lightbox" component is closed and disabled when the "Lightbox" component is open. Thanks! Let me know what other code would be useful.
Prevent scrolling events on LightBox modal itself -
<LightBox
#wheel.prevent
#touchmove.prevent
#scroll.prevent
/>
style overflow: hidden might create some concerns.
such as;
Visibility of scrollbar
UI bounce w.e.f overflow toggle
You could use a watcher to react to changes in isModalVisible and disable the scrolling function by using style="overflow: hidden".
Something along these lines:
// HTML
<btn #click="dialog = !dialog" >Click Me </btn>
// JS
new Vue({
el: '#app',
data () {
return {
dialog: false,
}
},
watch: {
isModalVisible: function() {
if(this.isModalVisible){
document.documentElement.style.overflow = 'hidden'
return
}
document.documentElement.style.overflow = 'auto'
}
}
})
I have a navigation bar which has a drop-down menu item Log Out which calls the ngPostLogOut() function.
In app.js
.when("/logout", {
controller: 'AuthController',
templateUrl: "client/html/auth/logout.html"
});
AuthController
$scope.ngPOSTLogOut = function() {
if ($rootScope.user) {
$rootScope.user = null;
}
if ($window.sessionStorage) {
$window.sessionStorage.clear();
alert('Entered..');
}
alert('Before HTTP');
$http.post('server/auth/logout.php')
.then(function(result) {
$scope.logout = result.data;
});
alert('After HTTP');
/*
$timeout(function() {
$location.path('/');
}, 10000);
*/
};
logout.html
<div ng-controller="AuthController as auth">
<p ng-show='user == null' class="text-center">{{logout}}</p>
<br>
<button ng-click='ngPOSTLogOut()' class="btn btn-default btn-block">Angular To PHP</button>
Now, if a person clicks the Log Out item from the drop-down in the navbar then the function is called. I know this because I have set up alerts and they do pop up. But, the 'echo' from the login.php doesn't get featured. But, the odd thing is is that if I press the Angular to PHP button which also calls the ngPostLogOut() function, the function completes perfectly and as intended.
My guess
My guess is that ngRoute forces Angular to prioritize the HTML template switch making it so that the ngPOSTLogOut() function's parametres get ignored or dismissed.
The entire project on GitHub
https://github.com/AquaSolid/RAMA_Angular_PHP
Change this:
<li ng-click='ngPOSTLogOut()' ng-show='user != null'>Log Out</li>
to this:
<li ng-show="user != null"><a ng-click="ngPOSTLogOut()">Log Out</a></li>
And consider using ng-if rather than ng-show.
I have an AngularJs directive like this:
app.directive("showSuccess", function () {
return {
restrict: "A",
link: function (_scope, _element) {
_scope.$watch("successMessage", function (newVal) {
if (newVal) {
$(_element).find("#successMessage").html(newVal);
$(_element).slideDown().delay(3000).slideUp();
}
});
// Below code does not work
$(_element).find(".hide-message").on("click", function () {
$(_element).slideUp();
_scope.successMessage = "";
});
}
};
});
The related HTML is:
<div class="ui-state-success" show-success>
<i class="icon-ok-sign small"></i>
<span id="successMessage"></span>
<i class="icon-remove hide-message"></i>
</div>
When the panel is triggered to slide down, the screen shot is:
The problem is, when I click the "×", the panel won't slide up (although that it will slide up anyway after 3s delay).
I know I can do this using ng-click. But anyone knows why it does not work in this case? Thanks.
It's because jQuery animations are queued. You're calling .slideUp() on it and expecting it to slide instantly; however, it is currently waiting out it's 3 second delay.
One solution is to use .stop(true, false) to cancel the previous queued animation:
$(_element).find(".hide-message").on("click", function () {
$(_element).stop(true, false).slideUp();
_scope.successMessage = "";
});
on didInsertElement i have initialised bootstrap popover and it works fine until i run an action i.e submit a form, after i save the form data on db i make a request to get the current saved data from api and then i use this.set() to update the model in realtime for the user... however after i use this.set() the popover breaks... to explain it a little better i'm gonna use an example below:
<form {{action 'saveForm' on='submit'}}>
{{input type="text" value=firstName class="form-control" placeholder="Firstname"}}
<button type="submit" class="btn btn-success" >Submit</button>
</form>
{{#each firstname in model.firstNames}}
<span data-toggle="popover" data-title="Firstname" data-content="{{unbound firstname}}" data-placement="top">Firstname</span>
{{/each}}
after using this.set() the popover inside #each doesn't work anymore..
UPDATE: this is the action where i call this.set()
App.firstNamesController = App.AppController.extend({
actions: {
updateFirstnames: function () {
$.getJSON('/api/firstnames/get/', function (jsonResponse) {
this.set('firstNames', jsonResponse.data.firstNames);
}.bind(this));
}
}
});
UPDATE #2:
App.firstNamesView = Ember.View.extend({
templateName: 'firstNamesTemplate',
didInsertElement: function() {
$('span[data-toggle="tooltip"]').tooltip({
trigger: 'click'
});
}
});
You'll need to (as Jeff mentioned) re-initialize your listeners after you set new names. didInsertElement isn't going to run every time new elements enter the DOM, it's just going to run when the view has been inserted for the first time once it's elements are ready.
Re-setting firstNames is adding new elements to the DOM after you set your listeners, so they aren't going to have the listeners set on them.
Untested solution:
App.firstNamesController = App.AppController.extend({
actions: {
updateFirstnames: function() {
$.getJSON('/api/firstnames/get/', function(jsonResponse) {
this.set('firstNames', jsonResponse.data.firstNames);
// add this after setting names:
Ember.run.next(this, function () {
$('span[data-toggle="tooltip"]').tooltip();
});
}.bind(this));
}
}
});
Also, your selector in your jQuery is targeting span[data-toggle="tooltip"] but your template has span[data-toggle="popover"] - is that a typo?
EDIT 1
Another option could be to observe the model.firstNames property and have the listeners get set any time model.firstNames changes. But observers can be a pain to manage.
// in your controller:
toolTipObserver: function () {
Ember.run.next(this, function () {
$('span[data-toggle="tooltip"]').tooltip();
});
}.observes('model.firstNames')
I have a single-page html application. There is only one thml file that has multiple DIVs, that are displayed/hidden when the relevant button is clicked.
I use some animation on one of this DIVs (not on the "first page DIV"). The problem is that the animation starts directly when the html document is loaded and vhen i go to that div with the animation, the animation is already ended.
My question is: How to make the animation start just at the moment when it's DIV is displayed?
here is the code:
<!DOCTYPE html>
<html>
<head>
<script>
function show(shown, hidden) {
document.getElementById(shown).style.display='block';
document.getElementById(hidden).style.display='none';
return false;
}
</script>
<script>
function myFunction()
{
var o, qt=[
["a","b","c"],
["d","e","f"],
["g","h","i"]];
o=document.getElementById("quote1");
o.innerHTML="";
for(var i=1;i<4;i++)
o.innerHTML+="<p id=\"quote1_"+i+"\" style=\"font-size: 28pt;\"> </p>";
var q=qt[Math.floor(Math.random() * qt.length)];
document.getElementById("quote1_1").innerHTML=q[0];
setTimeout(function() { document.getElementById("quote1_2").innerHTML=q[1]; }, 2000);
setTimeout(function() { document.getElementById("quote1_3").innerHTML=q[2]; }, 3000);
}
window.onload = function(){
myFunction();
}
</script>
</head>
<body>
<div id="Page1">
<div data-role="page">
<div data-role="content">
<center>
SHOW ANIMATION
</center>
</div>
</div>
</div>
<div id="Page2" style="display:none">
<div data-role="page" id="main">
<div data-role="content">
<center>
<div id="quote1"></div>
<center>
Next
</center>
</center>
</div>
</div>
</div>
</body>
</html>
Adding to Kcent's post, there is also a plugin for jQuery called "ScrollMagic":
http://janpaepke.github.io/ScrollMagic/
It activates animations depending on scroll speed/placement on the page.
You can find the documentation here:
http://janpaepke.github.io/ScrollMagic/docs/index.html
Using pure javascript, you can use the MutationObserver. Yet using libraries like angularjs or knockoutjs should be much simpler. The MutationObserver is quite complicated in my opinion. One way to do that when the MutationObserver didn't exist yet was to track object creation. When you create an object in the dom, you can trigger the callback. The only big problem with this method is that it wouldn't work for changes to the dom that aren't explicitly tracked. The MutationObserver will catch any change to the DOM by user interaction or not.
In your case, a simple solution would be to execute something right after you added it to the dom.
Something like that:
function changeElement(target, source, callback) {
target.innerHTML = source;
setTimeout(function() {
callback(target);
}, 0);
}
setTimeout(function() {
changeElement(document.getElementById("quote1_2"), q[1], function (parent) {
// code here
}
}, 2000);
You could also change the function above to already include the setTimeout to make it cleaner.
function changeElement(target, source, callback, wait) {
setTimeout(function() {
target.innerHTML = source;
setTimeout(function() {
callback(target);
}, 0);
}, wait);
}
changeElement(document.getElementById("quote1_2"), q[1], function (parent) {
// code here
}, 2000);
The setTimeout with 0 is to make sure the code is executed after the innerHTML gets updated. It's possible that if you execute directly the callback, the dom won't be already available in the callback.
As for animation, you should have a look to css3 transitions. https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Using_CSS_transitions The animations can be done by making opacity modified itself slowly for example. It really depends on what you want to do.
MutationObserver
This is a way to do what you're trying to do. MutationObserver isn't that easy to setup. I did a small example:
var target = window.container;
function changeOpacity(target) {
setTimeout(function(){
target.style.opacity = 1;
}, 100);
}
// create an observer instance
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log(mutation);
if (mutation.type == "childList") {
for(var i=0; i<mutation.addedNodes.length; i++) {
changeOpacity(mutation.addedNodes[i]);
}
}
});
});
// configuration of the observer:
var config = {
attributes: true,
childList: true,
characterData: true
};
// pass in the target node, as well as the observer options
observer.observe(target, config);
setTimeout(function () {
target.innerHTML = "<div class='elem'>Some text</div>";
}, 2000);
And a fiddle: http://jsfiddle.net/8zmc8/
If you are able to use jQuery, there's a library called waypoints that handles this very problem: http://imakewebthings.com/jquery-waypoints/
The core of what you are looking for is:
- adding an event listener to the window scroll
- listening for the offsetTop of the target div to come into view
- and then trigger the animation at the desired offsetTop value