Browser not rendering updated values after callback function is called - javascript

I'm using a callback function after an animation, and I'm getting a result that I don't understand.
The reader's current_state should increase by 1, then the reader should move right, and then this should repeat 3 more times.
When I run the program using my web inspector, I see that the current_state does indeed increase before moving each step. However, in my browser window I only see the reader's text change values during the first step. So when the program ends, I see a 2 in the reader's text on my screen, but the value of the reader's current_state is actually 5.
To perplex me even more, I was tinkering around and added a random button with an empty click event attached. Hitting this button will make the current_state appear as the reader's text on the screen. So if I repeatedly click this button while the program runs, it looks perfect.
I'm not looking for an entirely different way to do this, as this isn't my actual code, just an example to illustrate the issue. In my code I'm trying to stick to using angular and a recursive callback function after an animation, if it's possible to fix this issue.
Thanks in advance, this has been driving me crazy!
https://jsfiddle.net/snookieordie/ns09cvqc/7/
var app = angular.module("turingApp", []);
app.controller("turingController", ["$scope", function ($scope) {
$scope.reader = {
current_state: 1,
}
$scope.run_program = function() {
if($scope.reader.current_state < 5) {
$scope.reader.current_state++;
$(".reader").animate({"left": "+=50px"}, 1000, function() {
$scope.run_program();
});
}
}
}]);
CSS:
.reader {
position: relative;
height: 50px;
width: 50px;
left: 0px;
font-size: 35px;
background-color: coral;
}
HTML:
<body ng-app="turingApp" ; ng-controller="turingController">
<div class="reader">{{reader.current_state}}</div>
<br/><br/>
<input type="button" class="code_submit" value="Run Code" ng-click="run_program()" />
<br/><br/>
<input type="button" value="Empty Click Event" ng-click="" />
</body>

Use $scope.apply() just after animate call back function is being called, Below I have edited js code
var app = angular.module("turingApp", []);
app.controller("turingController", ["$scope", function($scope) {
$scope.reader = {
current_state: 1,
}
$scope.run_program = function() {
if ($scope.reader.current_state < 5) {
$scope.reader.current_state++;
$(".reader").animate({"left": "+=50px"}, 1000, function() {
$scope.run_program();
$scope.$apply(); // Have added this line onlye
});
}
}
}]);
Why this is needed? actually angular has watchers concept and when you are doing this complex kind of functionality watchers need to keep deep eye on variable changes, which is not present auto to achieve speed, but can be added/enhanced manually.

Related

Is there a way to create a button and a click event on this js function?

I've been trying for more than a day to make three implementations in this code, but still without success.
1° I need to create a button inside this page.
I've already tried to create a very normal button through html and css, but the fireworks page covers and hides the button element.
All elements displayed on the page are purely created within the JS file.
Here are the html and css
HTML
<canvas id="fireworksidButton" href="#" class="myButton">Click here</canvas>
<canvas id="firework"></canvas>
<canvas id="city"></canvas>
CSS
html {
height: 100%;
}
body {
margin: 0;
height: 100%;
}
canvas {
position: absolute;
top: 0;
left: 0;
}
#app {
position: absolute;
top: 0;
left: 0;
}
.myButton {
background-color:#44c767;
border-radius:28px;
border:1px solid #18ab29;
display:inline-block;
cursor:pointer;
color:#ffffff;
font-family:Arial;
font-size:28px;
padding:16px 31px;
text-decoration:none;
text-shadow:0px 1px 0px #2f6627;
}
.myButton:hover {
background-color:#5cbf2a;
}
.myButton:active {
position:relative;
top:1px;
}
2° I need to remove the Loop generated by the function.
I've already tried disabling this render action, but unfortunately it didn't work.
Here is the part of the script that renders the loop action.
let _pre = OrbitCalculator.pre_render();
(function loop(count) {
if (render_end) {
render_end = false;
_pre = OrbitCalculator.pre_render();
loop(0);
return;
}
if (count === MAX_AMOUNT_OF_FIREWORKS) {
count--;
} else {
fireworks.push(_pre[count]);
}
setTimeout(() => loop(++count), INTERVAL);
})(0);
3° I need to create a click event so that when the button is clicked, the function will be executed.
I´d like wrappe the function that triggers the Loop with the code below if it make sense.
function fireworksTrigger(){
document.querySelector('#fireworksidButton').addEventListener("click", async () => {
});
To make the function run, there is a dependence of the following library:
We can find the library inserted in the compiler´s settings.
[Library]https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.3.3/pixi.min.js
Here is the whole code running on the compiler. 599 Lines of code. That´s why i didn`t post the whole script here.
[Fireworks]https://codepen.io/paulodoporto/pen/Jjverod
What should I do please? Any Light?
Thank you so much in advance!
First i dont think that putting the button inside the canvas element is the best way to do it, you might just want to do <button class='myButton'>Click here<button>. To make the button not be covered by the canvas, i would suggest that you change the z-index like freedomn-m said. A higher z-index means that its placed higher when rendering, so make sure that the button has a higher z-index than the canvas. Alternatively, you can just put the button html after the canvas and that should automatically make the button on thop, since its rendered after.
I'm not 100% sure what your trying do do here but it looks like your using recursion so i would just use async and then sleep like this
const sleep = ms => new Promise(res => setTimeout(res, ms));
(async () => {
/*whatever u wanna do*/
await sleep(2);
})();
You should just be able to put whatever function u wanna call inside the addeventlistener('click',//whatever') but this might not work unless you call the function fireworksTrigger in a main/setup run before the main loop starts
Hope this helps

Scroll horizontal scroll bar automatically from left to right [duplicate]

I thought this would be fairly easy but I'm stuck.
My code is executing and ignoring the setTimeout.
I am getting the scroll width of my element, then saying while i is less than the width (flavoursScrollWidth), move the horizontal scroll 1px along every second.
That isn't what is happening though, it just executes each pixel movement almost instantly.
I also tried taking the code out of the load event and taking the setTimeout out of the while loop. Then creating a function containing the while loop, and calling the function in a setInterval. Didn't help.
const flavoursContainer = document.getElementById("flavoursContainer")
const flavoursScrollWidth = flavoursContainer.scrollWidth
window.addEventListener("load", () => {
let i = 0
while (i < flavoursScrollWidth) {
setTimeout(flavoursContainer.scrollTo(i, 0), 1000)
console.log(i)
i++;
}
})
.container {
width:300px;
overflow-x:scroll;
white-space: nowrap;
}
<div class="container" id="flavoursContainer">
This is a really long sentence to demo my code, it's just going on and on. Still going. I should have used some default placeholder text but I've started now so I'll keep going.
</div>
I would suggest using setInterval rather than setTimeout and just checking if the container is scrolled to the end. I also found that if you scroll faster, like every 15ms, you get a smoother user experience.
const flavoursContainer = document.getElementById('flavoursContainer');
const flavoursScrollWidth = flavoursContainer.scrollWidth;
window.addEventListener('load', () => {
self.setInterval(() => {
if (flavoursContainer.scrollLeft !== flavoursScrollWidth) {
flavoursContainer.scrollTo(flavoursContainer.scrollLeft + 1, 0);
}
}, 15);
});
.container {
width: 300px;
overflow-x: scroll;
white-space: nowrap;
background-color: #fff;
}
<div class="container" id="flavoursContainer">
This is a really long sentence to demo my code, it's just going on and on. Still going. I should have used some default placeholder text but I've started now so I'll keep going.
</div>

angular-loading-overlay doesn't show anything

I have an AngularJS frontent app, where I am using angular-loading-overlay library. The problem is that it doesn't show me my spinner.
I have a template with an assigned controller:
<div ng-controller="FooCtrl" data-ng-init="initFoo()">
<div class="bs-loading-container" bs-loading-overlay="FOO" bs-loading-overlay-template-url="/static/spinner.html">
... here some ng-repeat, data for it is loading inside initFoo();
</div>
</div>
The controller itself:
app.controller("FooCtrl", ['$scope', '$http', 'bsLoadingOverlayService',
function ($scope, $http, bsLoadingOverlayService) {
$scope.initFoo = function () {
bsLoadingOverlayService.start({referenceId: 'FOO'});
$http.get("/model").then(function (response) {
// some long-loading data
});
bsLoadingOverlayService.stop({referenceId: 'FOO'});
};
}]);
CSS fragment:
.bs-loading-container {
position: relative;
}
spinner.html itself is some div with text inside (I have simplified it for testing purposes):
<div style="position: absolute; width: 100%; height: 100%; top: 0; left: 0; background-color: rgba(255,255,255,0.7);">
<h1>Please, wait...</h1>
</div>
There is no errors in console. The method initFoo() works fine, it loads a data and the data is drawn in the page after it is loaded fine. But during loading no spinner appears.
Furthermore, if I deliberately change bs-loading-overlay-template-url to improper path, then I receive errors in console. That means, I guess, that library angular-loading-overlay itself was loaded well.
I know that it is always very specific, but may be any ideas what could I miss and what should I check?
Your problem is that you are showing the overlay, and hiding it instantly
$http.get is a asynchronous. so you should hide the overlay inside the handler methods. Try this:
$scope.initFoo = function () {
bsLoadingOverlayService.start({referenceId: 'FOO'});
$http.get("/model").then(function (response) {
// some long-loading data
bsLoadingOverlayService.stop({referenceId: 'FOO'});
});
};

Spinner in Angular.js

I want to use a spinner which I can show during some of the rest api calls for a better UX. I have come across many existing github projects which does exactly similar things.
https://github.com/cgross/angular-busy
https://github.com/urish/angular-spinner
But I'm not able to use any of the existing projects. I think before I start writing something of my own, I want to know if things which I'm looking for can be done using these projects or any other existing project.
Requirement:
During some of the rest api calls like uploading images, fetching some data, deleting images, etc, I want to show a spinner with background faded. Once I have the result, I can show the background again and remove the spinner.
I want to use this spinner with start/stop from my controller not from my html.
I don't want this spinner for all the xhr requests by default.
I think angular-busy demo does solves most of the above requirements except that it needs a promise param in html. Is there anyway by which I can control the start/stop dynamically from my controller rather than giving a promise.
Angular-spinner demo is good but it doesn't fade out background. Is there any way to fade out background ?
Can anyone give me some pointers how exactly can I solve my problem ?
I always create my own spinner with this logic:
js:
app.directive('ngSpinnerBar', ['$rootScope',
function ($rootScope) {
return {
link: function (scope, element, attrs) {
// by defult hide the spinner bar
element.addClass('hide');
// count how many time requests were sent to the server
// so when they all done the spinner will be removed
scope.counter = 0;
$rootScope.$on('$stateNetworkRequestStarted', function () {
scope.counter++;
element.removeClass('hide'); // show spinner bar
// $('body').addClass('page-on-load');
});
$rootScope.$on('$stateNetworkRequestEnded', function () {
scope.counter--;
if (scope.counter <= 0) {
scope.counter = 0;
element.addClass('hide'); // show spinner bar
// $('body').removeClass('page-on-load'); // remove page loading indicator
}
});
}
};
}
])
html:
<div ng-spinner-bar></div>
As you can see every time i send a request to the api i show the spinner (css create the spinning - link) and when result come back i send event to hide the spinner.
if you want to make things easier for you, you should create a service which send all the api requests (wrap $http). that way you can ensure every request will show the spinner.
EDIT
the first result in google gave me this - fade background in angular
It seems like http://bsalex.github.io/angular-loading-overlay/_site/ fits the requirements.
For example:
var app = angular.module('app-http-integration-with-reference-id-and-matchers', [
'bsLoadingOverlay',
'bsLoadingOverlayHttpInterceptor'
])
.factory('randomTextInterceptor', function(bsLoadingOverlayHttpInterceptorFactoryFactory) {
return bsLoadingOverlayHttpInterceptorFactoryFactory({
referenceId: 'random-text-spinner',
requestsMatcher: function(requestConfig) {
return requestConfig.url.indexOf('hipsterjesus') !== -1;
}
});
})
.factory('randomUserInterceptor', function(bsLoadingOverlayHttpInterceptorFactoryFactory) {
return bsLoadingOverlayHttpInterceptorFactoryFactory({
referenceId: 'random-user-spinner',
requestsMatcher: function(requestConfig) {
return requestConfig.url.indexOf('randomuser') !== -1;
}
});
})
.config(function($httpProvider) {
$httpProvider.interceptors.push('randomTextInterceptor');
$httpProvider.interceptors.push('randomUserInterceptor');
}).run(function($sce, bsLoadingOverlayService) {
bsLoadingOverlayService.setGlobalConfig({
/*
It is only an example, don't use this url in production.
Copy this template to your code base or use integration with Spin.js (see Docs & Examples)
*/
templateUrl: $sce.trustAsResourceUrl('https://raw.githubusercontent.com/bsalex/angular-loading-overlay/gh-pages/_site/loading-overlay-template.html')
});
});
app.controller('HttpIntegrationWithReferenceIdAndMatchersController', function($scope, $http, $sce, bsLoadingOverlayService) {
$scope.randomText = $sce.trustAsHtml('Fetch result here');
$scope.randomUser = undefined;
$scope.fetchRandomText = function() {
$http.get('http://hipsterjesus.com/api/')
.success(function(data) {
$scope.randomText = $sce.trustAsHtml(data.text);
})
.error(function() {
$scope.randomText = $sce.trustAsHtml('Can not get the article');
});
};
$scope.fetchRandomUser = function() {
$http.get('https://randomuser.me/api/')
.success(function(data) {
$scope.randomUser = data.results[0];
});
};
});
.random-result {
display: flex;
height: 170px;
margin-top: 1em;
}
.random-result__text,
.random-result__user {
position: relative;
overflow: auto;
border: 2px dashed #C00;
flex: 1;
margin: 0.5em;
padding: 0.5em;
text-align: center;
}
.user__photo {
width: 100px;
height: 100px;
border-radius: 50%;
margin: 0;
}
.user__name {
font-size: 1.5em;
margin: 0.5em;
padding: 0;
text-align: center;
}
<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.5.0/angular.min.js"></script>
<script src="https://rawgit.com/bsalex/angular-loading-overlay/master/dist/angular-loading-overlay.js"></script>
<script src="https://rawgit.com/bsalex/angular-loading-overlay-http-interceptor/master/dist/angular-loading-overlay-http-interceptor.js"></script>
<div ng-app="app-http-integration-with-reference-id-and-matchers">
<div ng-controller="HttpIntegrationWithReferenceIdAndMatchersController">
<div class="well well-lg bs-loading-container">
<button ng-click="fetchRandomText()">Fetch random text</button>
<button ng-click="fetchRandomUser()">Fetch random user</button>
<div class="random-result">
<div class="random-result__text" bs-loading-overlay bs-loading-overlay-reference-id="random-text-spinner" bs-loading-overlay-delay="3000">
<p ng-bind-html="randomText"></p>
</div>
<div class="random-result__user user" bs-loading-overlay bs-loading-overlay-reference-id="random-user-spinner" bs-loading-overlay-delay="3000">
<div ng-if="randomUser">
<img ng-src="{{randomUser.picture.large}}" alt="" class="user__photo" />
<p class="user__name">
{{randomUser.name.first}} {{randomUser.name.last}}
</p>
</div>
</div>
</div>
</div>
</div>
</div>
In the snippet above I've used integration with $http service. It matches requests and shows spinner with specified referenceId.
Also, the following features are available:
You can show and hide spinners from controllers injecting bsLoadingOverlayService and calling bsLoadingOverlayService.start(); and bsLoadingOverlayService.stop();;
You can wrap Promises to show and hide spinners with bsLoadingOverlayService.wrap();
You can create preconfigured handlers (bsLoadingOverlayService.createHandler({referenceId: 'handler-overlay'});), to keep options in one place and then just call preconfiguredHandler.start(); and preconfiguredHandler.stop();

How to click a button until the button isn't visible?

I'm trying to write a step that clicks a more button until that button changes to display: none;.
CLARIFICATION: The page I'm working with is a Google+ business page. There is a more button that loads about 10 reviews at a time until they are all loaded. Once they are, Google sets the button to display: none;
I've played around with a ton of different ways to accomplish it. Right now I'm working with something like:
This one is not working, and I'm sure there is an elegant way to accomplish this.
casper.then(function() {
while (this.visible('.d-s.L5.r0')){
console.log("Click");
this.click('.d-s.L5.r0');
}
});
This new code is working, but I have to set how many times it repeats which is pretty hacky:
casper.repeat(15, function() {
console.log("Click");
this.click('.d-s.L5.r0');
this.wait(400);
});
You need proper status handling of the button. By clicking the more button it will be invisible, but another loading span will be visible until the next items are loaded. Then it is exchanged again. You need to reflect the change in your code.
if (this.visible(moreButton)) { // synchronous
// asynchronous steps...
this.thenClick(moreButton);
this.waitUntilVisible(loadingButton);
this.waitUntilVisible(moreButton);
this.then(function(){
this.capture("business.png"); // overwrite the current screenshot
// recursion here
});
}
Additionally, it is good practice to write the thing recursive, because you don't know the number of steps until you try the steps. So the complete code would be:
var casper = require('casper').create({
waitTimeout: 10000,
viewportSize: {
width: 900,
height: 720
}
});
var moreButton = '[id*="about-page"] span.d-s.L5.r0',
loadingButton = moreButton + ' + span.dP.PA';
function clickMore(max, i){
i = i || 0;
if ((max == null || i < max) && this.visible(moreButton)) { // synchronous
// asynchronous steps...
this.thenClick(moreButton); // sometimes the click is not properly dispatched
this.waitUntilVisible(loadingButton);
this.waitUntilVisible(moreButton, null, function onTimeout(){
// only placeholder so that the script doesn't "die" here if the end is reached
});
this.then(function(){
this.capture("business_"+i+".png");
clickMore.call(this, max, i+1); // recursion
});
}
}
casper.start("https://plus.google.com/101200069268247335090/about?hl=en").then(function(){
clickMore.call(casper);
}).then(function(){
this.capture("business_end.png");
}).run(function(){
this.echo("DONE");
this.exit();
});
You can also call clickMore with an argument setting the maximum number of clicks like this:
clickMore.call(casper, 10);
A problem remains. The click on the more button is sometimes not dispatched. You should explore other approaches clicking that button.

Categories

Resources