angularjs make serial actions by condition - ajax

i'd like to do user friendly action at my app.
The issue is:
I have one button. When user 'mousedown' on it, i send ajax query to server to get some info. What i want is to show that info only after user 'mouseup' on this button. In other words, when he releases mouse button.
The problem is that user can release mouse button before or after server answers to my ajax call, by i still want to show info only after release button.
I think i can do this with deferred stuff, by i don't have a lot experience in this. Can you give me some info, how to do this?
Example code:
.html
<input type='button' ng-start='start()' ng-end='end()' >
.js
$scope.start = function(){
$http.get(url, data).success(function(result){})
}
$scope.end = function(){
//show result from first function
}

Tried to do it with use of $q, but it appears that $q is not necessary.
.html
<input type='button' ng-mousedown='start()' ng-mouseup='end()' >
<div ng-show="loaded && resolved" ng-bind="processedResult"></div>
.js
$scope.start = function(){
$scope.resolved = false;
$scope.loaded = false;
$http.get(url, data).success(function(result){
$scope.loaded = true;
$scope.processedResult = processResult(result);
});
};
$scope.end = function(){
$scope.resolved = true;
};

I think, i've solved my problem with pretty promise feature.
.html
<input type='button' ng-start='start()' ng-end='end()' >
.js
$scope.start = function() {
promise = $q.when($scope.ApiCall())
.then(function(result){
return result.data;
}, function(error){
console.log(error);
})
;
};
$scope.end = function() {
promise.then(function(result){
processResult(result.datat);
});
};
$scope.ApiCall = function(){
var data = {};
return $http.get(url, {params: data});
}

Related

Ajax call not returning a response, just reloads page

I have a Symfony 3 CRM and I use ajax calls to action the removal of items throughout the system. It uses a single call and then uses a switch statement to determine what it is the user is attempting to delete and handles it accordingly.
However, for some strange reason one particular type of item doesn't seem to work, it just reloads the page.
Here is the trigger button (I am implementing bootstrap confirmation):
<a href="" data-type="unit" id="{{ unit.id }}"
data-toggle="confirmation-singleton"
data-btn-ok-class="btn btn-xs btn-success"
data-btn-cancel-class="btn btn-xs btn-danger"
class="btn btn-xs btn-danger remove-item">
<i class="fa fa-remove no-override"> </i>
</a>
My ajax call for removal of items:
$('.remove-item').confirmation({
rootSelector: '[data-toggle=confirmation-singleton]',
container: 'body',
onConfirm: function() {
var type = $(this).attr('data-type');
var id = $(this).attr('id');
var data = type + '|' + id;
$.ajax( '/app_dev.php/ajax-call/remove-item/' + data )
.done( function(response) {
if(response != 'success') {
if(response == 'units_exist') {
alert("You cannot delete this item as there are units already linked to it.");
} else if(response == 'no_property') {
alert("Sorry! Property could not be found.");
} else if(response == 'bookings_exist') {
alert("Sorry! This unit has bookings. Please delete the bookings first.");
}
}
});
return false;
},
onCancel: function() {
return false;
}
});
And on the PHP side, for this particular example:
$data = $request->get('data');
$parts = explode("|",$data);
$type = $parts[0];
$id = $parts[1];
// using switch on $type
case 'unit':
$em = $this->getDoctrine()->getManager();
$repo = $em->getRepository('AppBundle:Unit');
$booking_repo = $em->getRepository('AppBundle:Booking');
$bookings = $booking_repo->findBy(array('unitId' => $id)); // check to see if any bookings exist
if(!empty($bookings)) {
return new Response('bookings_exist');
} else {
$item = $repo->findOneBy(array('id' => $id));
if(!empty($item)) {
$em->remove($item);
$em->flush();
}
}
break;
In this example, it SHOULD return 'bookings_exist' and if I directly go to the URL in the browser, it does display this message - however, all it does it reload the page instead of throwing the alert as stipulated in the ajax call. I know this call works as it does successfully delete other items in the CRM, it just seems to be when it cannot delete it due to a condition such as this.
I may be missing something really obvious here, so any help is appreciated.
For jQuery Ajax, use success and error handlers
Other handlers in jQuery's Ajax object are unreliable at best, and vary in their behavior and support between versions and browsers.
Prevent Default is generally a good idea with ajax handled events
Should jQuery fail, and NOT return false, the element will do it's default behavior, which in your case is which reloads the page.
onConfirm: function(e) {
e.preventDefault();
var type = $(this).attr('data-type');
var id = $(this).attr('id');
var data = type + '|' + id;
$.ajax( '/app_dev.php/ajax-call/remove-item/' + data )
.success( function(response) {
if (response.errorMessage) {
alert(response.errorMessage);
}
})
.error( function(xhr, status, error) {
console.log(status + '\n' + error);
})
;
return false;
}
PHP Side, build a JSONResponse
if(!empty($bookings)) {
return new JsonResponse([
'errorMessage' => 'Sorry! Property could not be found.'
);
}
instead of just adding .done() you should also use
.fail(function( jqXHR, textStatus, errorThrown ) {});
to catch any errors.
If the bookings is not empty then the function will return the new response 'booking_exist' and stop ... it will not proceed to next statments .
So if you need to delete the item use this code instead :
if(empty($bookings)) {
return new Response('bookings_not_exist');
} else {
$item = $repo->findOneBy(array('id' => $id));
if(!empty($item)) {
$em->remove($item);
$em->flush();
}

CasperJs Google login

I having been working on some code to access my google CSE
For that I need to sign in with my google account.
I have the following code:
var casper = require('casper').create({
verbose: true,
logLevel: 'debug',
waitTimeout: 5000,
clientScripts: ["libs/jquery.min.js"],
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5)
AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4'
});
const google_email = "MY_EMAIL";
const google_passwd = "MY_PASSWORD";
const loginUrl = 'https://accounts.google.com';
casper.start(loginUrl, function() {
this.waitForSelector('#view_container > form');
});
casper.then(function() {
this.fillSelectors('#view_container > form', {
'input[name="identifier"]': google_email
}, false);
});
casper.then(function() {
this.click('#identifierNext');
});
casper.wait(500, function() { //Wait for next page to load
this.capture('images/step01.png');
});
casper.then(function() {
this.evaluate(function () {
var identifierNext = $('#identifierNext');
identifierNext.click();
});
});
casper.then(function() {
this.capture('images/step02.png');
});
casper.run(function() {
this.echo("Done");
});
The part of entering the email seems to work.
But the click part isn't working.
I found this but it seems outdated.
Thanks
I haven't fixed the issue related to the new form, but we found a way to access the old form, so the "old" scripts should still work, and that solution is disabling JS. For that, change the loginUrl to
https://accounts.google.com/ServiceLogin?passive=1209600&continue=https%3A%2F%2Faccounts.google.com%2FManageAccount&followup=https%3A%2F%2Faccounts.google.com%2FManageAccount&flowName=GlifWebSignIn&flowEntry=ServiceLogin&nojavascript=1#identifier
Important thing: nojavascript=1
We are using the script posted in Casperjs Google Login Not working
Just changing the login URL.
Try this - a lot more delays are needed to wait for the dynamically loaded pages.
casper.options.verbose = true; // verbose reporting
casper.options.logLevel = 'debug'; // full log reporting
casper.options.exitOnError = false; // Keep going on error
const google_email = "EMAIL";
const google_passwd = "PASSWORD";
const loginUrl = 'https://accounts.google.com';
// Load the login page
casper.start(loginUrl, function() {
this.waitForSelector('#view_container'); // '> form' doesn't seem to work
});
// Fill in the 'username' form
casper.then(function() {
this.fill('form', {
identifier: google_email,
});
this.sendKeys('#view_container', casper.page.event.key.Enter , {keepFocus: true});
});
// First 'Enter' is too quick for Google, send another one after a pause
casper.wait(2500, function() {
this.sendKeys('#identifierId', casper.page.event.key.Enter , {keepFocus: true});
});
// Wait for the 'password' form
casper.waitForSelector("#passwordNext", function() {
this.echo("password form is apparently available");
});
// Password form seems to load slowly, even if the selector is found/visible, this delay ensures next form fill works
casper.wait(2500, function() {
this.echo("password form is really available");
});
// Fill in the 'password' form
casper.then(function() {
this.fill('form', {
password: google_passwd,
});
this.sendKeys('#view_container', casper.page.event.key.Enter , {keepFocus: true});
});
// First 'Enter' is too quick for Google, send another one after a pause
casper.wait(500, function() {
this.sendKeys('input.whsOnd.zHQkBf', casper.page.event.key.Enter , {keepFocus: true});
});
// Extend timeout to allow for slow dynamic page rendering
casper.options.waitTimeout = 25000;
casper.waitForSelector("#gb", function() {
this.echo("login complete");
});
casper.thenOpen('https://(google app you want)', function() {
// Check it opened okay
});

Having trouble updating a scope more than once

I'm using angular with the ionic framework beta 1.
Here's my ng-repeat html:
<a href="{{item.url}}" class="item item-avatar" ng-repeat="item in restocks | reverse" ng-if="!$first">
<img src="https://server/sup-images/mobile/{{item.id}}.jpg">
<h2>{{item.name}}</h2>
<p>{{item.colors}}</p>
</a>
</div>
And here's my controllers.js, which fetches the data for the ng-repeat from a XHR.
angular.module('restocks', ['ionic'])
.service('APIservice', function($http) {
var kAPI = {};
API.Restocks = function() {
return $http({
method: 'GET',
url: 'https://myurl/api/restocks.php'
});
}
return restockAPI;
})
.filter('reverse', function() {
//converts json to JS array and reverses it
return function(input) {
var out = [];
for(i in input){
out.push(input[i]);
}
return out.reverse();
}
})
.controller('itemController', function($scope, APIservice) {
$scope.restocks = [];
$scope.sortorder = 'time';
$scope.doRefresh = function() {
$('#refresh').removeClass('ion-refresh');
$('#refresh').addClass('ion-refreshing');
restockAPIservice.Restocks().success(function (response) {
//Dig into the responde to get the relevant data
$scope.restocks = response;
$('#refresh').removeClass('ion-refreshing');
$('#refresh').addClass('ion-refresh');
});
}
$scope.doRefresh();
});
The data loads fine but I wish to implement a refresh button in my app that reloads the external json and updates the ng-repeat. When I call $scope.doRefresh(); more than once, I get this error in my JS console:
TypeError: Cannot call method 'querySelectorAll' of undefined
at cancelChildAnimations (http://localhost:8000/js/ionic.bundle.js:29151:22)
at Object.leave (http://localhost:8000/js/ionic.bundle.js:28716:11)
at ngRepeatAction (http://localhost:8000/js/ionic.bundle.js:26873:24)
at Object.$watchCollectionAction [as fn] (http://localhost:8000/js/ionic.bundle.js:19197:11)
at Scope.$digest (http://localhost:8000/js/ionic.bundle.js:19300:29)
at Scope.$apply (http://localhost:8000/js/ionic.bundle.js:19553:24)
at done (http://localhost:8000/js/ionic.bundle.js:15311:45)
at completeRequest (http://localhost:8000/js/ionic.bundle.js:15512:7)
at XMLHttpRequest.xhr.onreadystatechange (http://localhost:8000/js/ionic.bundle.js:15455:11) ionic.bundle.js:16905
It looks like it's related to a bug, as per:
https://github.com/driftyco/ionic/issues/727
Which was referenced from:
http://forum.ionicframework.com/t/show-hide-ionic-tab-based-on-angular-variable-cause-error-in-background/1563/9
I'm guessing it's pretty much the same issue.
Maybe try instead using angular.element(document.getElementById('refresh')) for a possible workaround (guessing).

AngularJS update View after Model loaded from Ajax

I'm newbie of angularjs developing and i wrote this simple app, but don't understand how i can update view, after the model il loaded from ajax request on startup!
This code don't work when I add delay into photos.php, using:
sleep(3);
for simulate remote server delay! instead if search.php is speedy it work!!
<!doctype html>
<html ng-app="photoApp">
<head>
<title>Photo Gallery</title>
</head>
<body>
<div ng-view></div>
<script src="../angular.min.js"></script>
<script>
'use strict';
var photos = []; //model
var photoAppModule = angular.module('photoApp', []);
photoAppModule.config(function($routeProvider) {
$routeProvider.when('/photos', {
templateUrl: 'photo-list.html',
controller: 'listCtrl' });
$routeProvider.otherwise({redirectTo: '/photos'});
})
.run(function($http) {
$http.get('photos.php')//load model with delay
.success(function(json) {
photos = json; ///THE PROBLEM HERE!! if photos.php is slow DON'T update the view!
});
})
.controller('listCtrl', function($scope) {
$scope.photos = photos;
});
</script>
</body>
</html>
output of photos.php
[{"file": "cat.jpg", "description": "my cat in my house"},
{"file": "house.jpg", "description": "my house"},
{"file": "sky.jpg", "description": "sky over my house"}]
photo-list.html
<ul>
<li ng-repeat="photo in photos ">
<a href="#/photos/{{ $index }}">
<img ng-src="images/thumb/{{photo.file}}" alt="{{photo.description}}" />
</a>
</li>
</ul>
EDIT 1, Defer solution:
.run(function($http, $q) {
var deferred = $q.defer();
$http.get('photos.php')//load model with delay
.success(function(json) {
console.log(json);
photos = json; ///THE PROBLEM!! if photos.php is slow DON'T update the view!
deferred.resolve(json);//THE SOLUTION!
});
photos = deferred.promise;
})
EDIT 2, Service solution:
...
//require angular-resource.min.js
angular.module('photoApp.service', ['ngResource']).factory('photoList', function($resource) {
var Res = $resource('photos.php', {},
{
query: {method:'GET', params:{}, isArray:true}
});
return Res;
});
var photoAppModule = angular.module('photoApp', ['photoApp.service']);
...
.run(function($http, photoList) {
photos = photoList.query();
})
...
The short answer is this:
.controller('listCtrl', ['$scope', '$timeout', function($scope, $timeout) {
$timeout(function () {
$scope.photos = photos;
}, 0);
}]);
The long answer is: Please don't mix regular javascript and angular like this. Re-write your code so that angular knows what's going on at all times.
var photoAppModule = angular.module('photoApp', []);
photoAppModule.config(function($routeProvider) {
$routeProvider.when('/photos', {
templateUrl: 'photo-list.html',
controller: 'listCtrl'
});
$routeProvider.otherwise({redirectTo: '/photos'});
});
photoAppModule.controller('listCtrl', ['$scope', function($scope) {
$scope.photos = {};
$http.get('photos.php') // load model with delay
.success(function(json) {
$scope.photos = json; // No more problems
});
}]);
use broadcast
//service
var mydata = [];
this.update = function(){
$http.get(url).success(function(data){
mydata = data;
broadcastMe();
});
};
this.broadcastMe = function(){
$rootScope.$broadcast('mybroadcast');
};
//controller
$scope.$on('mybroadcast', function(){
$scope.mydata = service.mydata;
};
http://bresleveloper.blogspot.co.il/
EDIT:couple of days ago i've learned the best practice
http://bresleveloper.blogspot.co.il/2013/08/breslevelopers-angularjs-tutorial.html
I think you're better off using high level angular services for data transfer, also look into promises and services:
http://docs.angularjs.org/api/ng.$q
You need to bind an element in your view to a property (simple or object) of your $scope object. Once the $scope object is updated the view should be updated on its own. That is the beauty of AngularJS.
EDIT:
Please register your controller as
photoAppModule.controller('listCtrl', function($scope){
$scope.photos = photos;
});
If photos variable is not available, then you might have to create a service with the variable and inject in the controller.

Jquery Address Plugin Post issue

Hi I am learning to use the Jquery Address plugin, and am using the tutorial over
here
So here is the html
Test 1<br />
Test 2
Load Area: <br />
<div id="area"></div>
And here is the Jquery code
function loadURL(url) {
$("#area").load(url);
}
// Event handlers
$.address.init(function(event))
.change(function(event) {
$("#area").load($('[rel=address:' + event.value + ']').attr('href'));
})
$('a').click(function(){
loadURL($(this).attr('href'));
});
Now this works well. However I want to do a POST call on the back button. So I replace
$("#area").load($('[rel=address:' + event.value + ']').attr('href'));
with
var myhref = $('[rel=address:' + event.value + ']').attr('href');
$.post(myhref, function(data) {
$('#area').html(data);
});
This throws the console error "this.value is not a function".
Considering my very superficial knowledge of Jquery (& programming in general), what am I doing wrong here?
Aw man I wish someone could answer this. I've been trying to get this thing to POST for a day now.. It just wants to get everything it seems, but I am at a loss of how I would tweak it to post.
function loadURL(url) {
$.post(url, function(data){
$("#area").html(data);
})
}
// Event handlers
$(document).ready(function(){
$("a").click(function(e){
e.preventDefault();
$.address.value($(this).attr("href"));
$.post($(this).attr("href"), function(data){
$("#area").html(data);
})
return false;
})
})
$(document).ready(function(){
$.address.init(function(event) { // Initates the address plugin
}).externalChange(function(event) { //externalChange is browser back/fwd button/address bar
if(event.path != "/"){
loadURL(event.path);
}
})
})

Resources