Prevent ng-repeat until request data is fully loaded - javascript

Im trying to prevent ng-repeat runs before the request data is fully loaded into an array, I've done it using a timeout but it only works when the request takes less than I say
Here is my HTML:
<div class="blocks-container" ng-init="loadProjects()" ng-controller="buildMonitorController">
<div class="row">
<!-- <div> -->
<div class="col-xs-12 col-sm-6 col-md-3 col-lg-2 block animate"
ng-if="!errorDialogActive && finishedRequest"
ng-repeat="build in builds.builds.build track by build._id | orderBy:'lastBuildDetails.startDate' : true"
ng-class="{'running': project.running ,'block-green': build._status ==='SUCCESS','block-red': build._status==='FAILURE'}"
id="{{build._id}}">
<div class="title-container"><p>{{build._buildTypeId}}</p></div>
<div class="update-container col-xs-12">
<time>{{ build.lastBuildDetails.startDate | date : 'dd.MM.yyyy H:mm:s'}}</time>
</div>
</div>
<!--</div>-->
</div>
<!-- Start error state dialog -->
<div ng-include src="'views/main/error-dialog.html'"></div>
And Here is my AngularJS:
$scope.refreshBuilds = function () {
$scope.errorList.length = 0
//#TODO remove this part right after the API is working
//Init
var suffix = '';
var randomnumber = Math.floor(Math.random() * 3);
//simulate mock by random number
switch (randomnumber) {
case 1:
suffix = '-success';
break;
case 2:
suffix = '-less';
break;
default:
break;
}
var url = 'mock/builds'+suffix+'.xml';
console.log('url: ' + url)
$http({
method: 'GET',
url: url,
headers: {
Authorization: 'Basic AAA'
}
}).success(function (data, status) {
//Recive builds from xml and reset scope
var buildsToFilter = new X2JS().xml_str2json(data);
$scope.errorDialogActive = false;
//filter builds which have a no build API detail status
if (buildsToFilter.builds.build !== undefined) {
angular.forEach(buildsToFilter.builds.build, function (build, index) {
$http({
method: 'GET',
url: 'mock/build-'+build._id+'.xml',
headers: {
Authorization: 'Basic AAA'
}
}).success(function (buildDetailData) {
$scope.errorDialogActive = false;
//push detail data into build array
buildsToFilter.builds.build[index].lastBuildDetails = new X2JS().xml_str2json(buildDetailData).build;
console.log(buildsToFilter.builds.build[index]);
}).error(function (data, status) {
$scope.errorDialogActive = true;
//remove build from index if no build detail was found
buildsToFilter.builds.build.splice(index, 1);
$scope.setError(status, '', '');
}).then(function () {
//after filtering builds to display, setup builds $scope for FrontEnd
$scope.builds = buildsToFilter;
});
});
} else {
}
}).error(function (data, status) {
//active dialog if error happens & set error
$scope.errorDialogActive = true;
$scope.setError(status, 'builds', '');
}).then(function () {
$timeout(function () {
$scope.finishedRequest = true;
}, 5000);
//refresh right after proceed and a little delay
$timeout(function () {
console.log('Starting refresh');
$scope.refreshBuilds();
}, 21000);
})
};

You can put condition
ng-show=builds.builds.build.length > 0
So when the builds.builds.build have data then only it will be displayed

You already have that
ng-if="!errorDialogActive && finishedRequest"
in place, but you never set $scope.finishedRequest = false. What if you simply modify your refreshBuilds function like this:
$scope.refreshBuilds = function () {
$scope.finishedRequest = false;
$scope.errorList.length = 0;
// etc
Does it work then in the way you wanted?

Related

Why are the functions being called in my angular html bindings receiving null values?

I am creating a simple web app that represents something like a trip planner. Just offers some simple itinerary options from a list of locations in a DB I made. My feed page is supposed to display pictures of each attraction, and each attraction is a clickable card that will lead you to another page with some more info on that attraction.
//
// init app
var tripperApp = angular.module('tripperApp', ["ui.router", 'ionic', 'LocalStorageModule']);
//
// setup routing
tripperApp.config(function($stateProvider, $urlRouterProvider) {
// For any unmatched url, redirect to /state1
$urlRouterProvider.otherwise("/splash");
// Now set up the states
$stateProvider
.state('splash', {
url: "/splash",
templateUrl: "partials/splash.html",
controller: "splashCtrl"
})
.state("login", {
url: "/login",
templateUrl: "partials/login.html",
controller: "loginCtrl"
})
.state("signup", {
url: "/signup",
templateUrl: "partials/signup.html",
controller: "signupCtrl"
})
.state("reset-password", {
url: "/reset-password",
templateUrl: "partials/reset-password.html",
controller: "resetPasswordCtrl"
})
.state("set-password", {
url: "/set-password/:user_id/:password_reset_hash",
templateUrl: "partials/set-password.html",
controller: "resetPasswordCtrl"
})
.state("feed", {
url: "/feed",
templateUrl: "partials/feed.html",
controller: "feedCtrl"
})
.state("wishlist", {
url: "/wishlist",
templateUrl: "partials/feed.html",
controller: "wishlistCtrl"
})
.state("specificwishlist", {
url: "/wishlist/:user_id",
templateUrl: "partials/feed.html",
controller: "wishlistCtrl"
})
.state("share", {
url: "/share",
templateUrl: "partials/share.html",
controller: "shareCtrl"
})
.state("attraction", {
url: "/attraction/:attraction_id",
templateUrl: "partials/attraction.html",
controller: "attractionCtrl"
});
});
This is my app.js file where I initialize all my routings and controllers. I think I am literally having problems with everything (I have a backend and everything in Nodejs, but that isn't working right because all of the values it receives is null - more on that later...).
The main problem I am seeing is that nearly all the data I am receiving is null. Nothing is really loading right - but I know that I have a proper connection to my DB. Because there are some places where I can see the correct results. Let me explain:
This is my services/feed.js file (I don't think it's very important):
//
// feed service: used to get setup filters and then query for a feed using those filters
tripperApp.factory("feedService", ["$rootScope", "api", function($rootScope, api) {
var feedService = {};
api.get("filters").success(function(data, status, headers, config) {
$rootScope.filters = data;
})
.error(function(data, status, headers, config) {
// #TODO - gracefully handle error
});
feedService.runSearch = function($rootScope, session, callback) {
// add on more results
api.post("search", {
"start": $rootScope.currentSpot
})
.success(function(data, status, headers, config) {
// copy results into array
for (var i = 0; i < data.length; i++) {
$rootScope.results.push(data[i]);
}
$rootScope.currentSpot += data.length;
// call callback
callback(true); // success!
})
.error(function(data, status, headers, config) {
callback(false); // error!
});
};
feedService.getWishlist = function($rootScope, session, callback) {
// get all results
api.post("wishlist", {
"attraction_ids": session.picks
})
.success(function(data, status, headers, config) {
// copy results into array
$rootScope.results = data;
callback(true); // success!
})
.error(function(data, status, headers, config) {
callback(false); // error!
});
};
// column-related functions
$rootScope.columns = function() {
var columns = 1;
var windowWidth = window.innerWidth;
if (windowWidth > 1300) {
columns = 5;
} else if (windowWidth > 1100) {
columns = 4;
} else if (windowWidth > 640) {
columns = 3;
} else if (windowWidth > 300) {
columns = 2;
}
var rv = [];
for (var i = 0; i < columns; i++) {
rv.push(i);
}
return rv;
}
$rootScope.getColumn = function(data, columnNumber) {
var columns = $rootScope.columns().length;
var rv = [];
for (var i = columnNumber; i < data.length; i += columns) {
rv.push(data[i]);
}
return rv;
}
$rootScope.columnClasses = function() {
var columns = $rootScope.columns().length;
return {
"col-33": (columns == 3),
"col-25": (columns == 4),
"col-20": (columns == 5),
"col-50": (columns == 2)
};
}
// generate thumbnail of a url
$rootScope.thumb = function(filename) {
return filename + ".thumb.jpeg";
}
return feedService;
}]);
This is my controller (controller/feed.js) file:
//
// feed controller
tripperApp.controller("feedCtrl", function($scope, $rootScope, session, feedService, $ionicScrollDelegate) {
// setup feed page (only once)
if (typeof($rootScope.feed) == "undefined") {
// first time loading - setup scope
$rootScope.feed = {
results: [],
currentSpot: 0
}
}
// auto scroll down (if appropriate)
window.setTimeout(function() {
if (typeof($rootScope.feed.lastScrollPosition) != "undefined") {
$ionicScrollDelegate.$getByHandle('feedScroll').scrollTo(0, $rootScope.feed.lastScrollPosition, false);
}
}, 0);
// tell feed template that this is NOT for a wish
$scope.wishlist = false;
// don't display share button on feed
$scope.displayShare = function() {
return false;
}
// query and display results
$scope.loadResults = function() {
feedService.runSearch($rootScope.feed, session, function(success) {
if (!success) {
// #TODO - handle error
}
$scope.$broadcast('scroll.infiniteScrollComplete');
});
}
// whether an attraction is picked
$scope.attractionIsPicked = function(attraction_id) {
return session.picks.indexOf(attraction_id) != -1;
};
// when a result is picked/unpicked
$scope.resultPicked = function(result) {
var attraction_id = result.id;
console.log(result);
console.log(attraction_id);
if (session.picks.indexOf(attraction_id) == -1) {
// not yet picked, so lets pick it
session.pickPlace(attraction_id);
} else {
// its picked, so lets unpick it
session.unpickPlace(attraction_id);
}
}
$rootScope.clickedResult = undefined;
$scope.resultClicked = function(result) {
$rootScope.clickedResult = result;
console.log("result: \n");
console.log(result);
$rootScope.feed.lastScrollPosition =
$ionicScrollDelegate.$getByHandle('feedScroll').getScrollPosition().top;
}
});
There are a couple of functions I am specifically having problems with:
resultPicked() in controller/feed.js and thumb() in services/feed.js. I am getting null values for both of these.
My partial/feed.html file is as such:
<ion-view>
<ion-header-bar class="bar bar-positive" align-title="center">
<div class="buttons">
<button class="button ion-navicon-round button-light" ng-click="toggleLeftMenu(); " style="font-size: 22px"></button>
</div>
<h1 class="title" ng-show="wishlist==false">New York Attractions</h1>
<h1 class="title" ng-show="wishlist==true">
<span ng-show="feed.user.name == 'Your'">Your</span>
<span ng-show="feed.user.name != 'Your'">{{feed.user.name}}'s</span>
Wish List
</h1>
<div class="buttons" ng-show="displayShare()">
<button class="button ion-android-share button-light" style="font-size: 22px" ng-click="sharePage();"></button>
</div>
</ion-header-bar>
<ion-content class="padding feed-page" delegate-handle="feedScroll">
<ion-list>
<!-- display results in columns -->
<div class="row">
<div class="col" ng-class="columnClasses()" ng-repeat="column in columns()" >
<div class="card" ng-repeat="result in getColumn(feed.results, column)" ng-show="feed.results.length > 0">
<div class="item item-image">
<a>
<img ng-src="{{thumb(result.src)}}" ui-sref="attraction({attraction_id: result.id})" ng-click="resultClicked(result)">
</a>
</div>
<div class="item item-bottom-sect">
<div class="row">
<div class="col-75 col-wrap">
<a ui-sref="attraction({attraction_id: result.id})" ng-click="resultClicked(result)" class="item-title">{{result.title}}</a>
</div>
<div class="col-25 right col-wrap">
<span ng-click="resultPicked(result)">
<span class="ion-heart heart heart-selected" ng-show="attractionIsPicked(result.id)" class="heart-selected"></span>
<span class="ion-heart heart" ng-show="!attractionIsPicked(result.id)"></span>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</ion-list>
<ion-infinite-scroll
ng-if="!resultsLoaded"
on-infinite="loadResults()"
distance="10%">
</ion-infinite-scroll>
</ion-content>
</ion-view>
Now, in my feed, there are two called to resultClicked() and one to resultPicked(). resultClicked(result) will return results just fine - but resultPicked(results) will always gets a null value! Also, thumb(result.src) always gets a null value. I have absolutely ZERO idea what - this is just things I'm confirming from console.logs. Is there anything glaringly wrong with the code?
Edit:
Here is a picture displaying what I am talking about. The cards are all "broken links" because the value im getting back is null (undefined) as shown in console. The "results" are also null, as shown in the console, when called from resultPicked(), but not from resultClicked().

AngularJS insert data twice to database

I am trying to insert data to database through angularJS but the data inserted twice.. I have tried to use ngRoute but still I face the same problem.
app.js
var app = angular.module("addDepApp", []);
app.controller('insertDepCtl', function($scope, $http) {
var isSend = false;
$scope.$on('newuser', function(event, data){
load(true);
});
var load = function(isEvent){
if($scope.$parent.newuser != null){
isSend = true;
}
};
load();
$scope.insertDepartment = function () {
console.log("called insertDepartment");
if (isSend == true){
$scope.newuserSend = {'org_id': $scope.$parent.newuser.org_id, 'dep_name': $scope.department};
$http.post("http://192.168.1.12:8888/XXXX/XXX/insertDep.php/",$scope.newuserSend)
}
}
});
add.html
<body ng-app="addDepApp">
<div class="12u$" ng-controller="insertDepCtl">
<input type="button" value="تسجيل" class="special" id="signup" ng-click="insertDepartment()"/>
</div>
</body>
Remove ng-controller="insertDepCtl" from your html code, your router injects this for you. Right now, you're calling everything twice
I have changed the code to this and now it works fine!!
$scope.insertDepartment = function () {
if (isSend == true) {
var request = $http({
method: "post",
url: "http://192.168.1.106:8888/XXXX/XXX/insertDep.php/",
data: {'org_id': $scope.$parent.newuser.org_id, 'dep_name': $scope.department},
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
});
}
}

When submitting an ajax request, how can you "put the original request on hold" temporarily until a condition is met?

I am wanting to implement a recaptcha process that captures all ajax requests before they go through - the desired process would be as follows:
User completes an action which is going to cause an ajax request of some sort.
If the user has already completed the recaptcha process, the ajax request proceeds without further delay
If the user has not completed the recaptcha process, put the ajax request "on hold" temporarily until the recaptcha process is completed, then continue the ajax request.
I have got things to a state where I intercept the call, however I don't know how to put it on hold temporarily. Here's the relevant code:
<script>
var captchaValidated = null;
var currentRequests = [];
$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
if (options.url != "/ValidateCaptcha") {
if (captchaValidated == null || captchaValidated == false) {
if (captchaValidated == null){
openRecaptcha();
} else {
verifyCaptcha(); //see async question in method
}
if (!captchaValidated) {
jqXHR.abort();
} else {
//let the original request proceed now - but how?!
}
}
}
});
function verifyCaptcha() {
var grecaptcha = $("g-recaptcha-response");
var encodedResponse;
if (grecaptcha != null) {
encodedResponse = grecaptcha.val();
$.ajax({
async: false, //set to false so that the calling method completes rather than async - what do you think?
headers: headers,
cache: false,
url: "/ValidateCaptcha",
type: 'POST',
contentType: 'application/json',
success: function (data) {
//parse the data - did we get back true?
captchaValidated = data;
},
error: function (raw, textStatus, errorThrown) { captchaValidated = null; alert("Validate ReCaptcha Error: " + JSON.stringify(raw)); },
data: JSON.stringify({ "encodedResponse": encodedResponse })
});
}
}
function invalidateCaptcha(){
captchaValidated = null;
}
function openRecaptcha() {
grecaptcha.render('recaptcha', {
'sitekey': "thekey",
'callback': verifyCaptcha,
'expired-callback': invalidateCaptcha,
'type': 'audio image'
});
$("#recaptchaModal").modal('show');
}
</script>
Any suggestions of how to proceed would be appreciated, thanks in advance!
Thank you #Loading and #guest271314 for your help in pointing me in the right direction that helped me get things figured out. I've pasted how I accomplished it below - perhaps it will be of help to someone else. Of course if anyone would like to weigh in on my implementation please do.
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCaptcha&render=explicit&hl=en" async defer></script>
<script>
var captchaValidated = null;
var currentRequests = [];
var captchaPrompted = false;
var captchaReady = false;
var resetCaptcha = false;
function onloadCaptcha() {
captchaReady = true;
captcha = grecaptcha.render('recaptcha', {
'sitekey': '<yoursitekey>',
'callback': verifyCaptcha,
'expired-callback': invalidateCaptcha,
'type': 'audio image'
});
}
var deferredCaptcha = null;
var promiseCaptcha = null;
var captcha = null;
function openRecaptcha() {
if (!captchaReady) {
setTimeout(openRecaptcha, 50);
}
if (captchaPrompted) {
return;
}
captchaPrompted = true;
var captchaTimer = setInterval(function () {
if (captchaValidated != null) {
if (captchaValidated) {
deferredCaptcha.resolve();
} else {
deferredCaptcha.reject();
captchaValidated = null;
}
}
}, 100);
if (resetCaptcha) {
captcha.reset();
}
deferredCaptcha = $.Deferred();
promiseCaptcha = deferredCaptcha.promise();
promiseCaptcha.done(function () {
//captcha was successful
clearInterval(captchaTimer);
//process the queue if there's items to go through
if (currentRequests.length > 0) {
for (var i = 0; i < currentRequests.length; i++) {
//re-request the item
$.ajax(currentRequests[i]);
}
}
});
promiseCaptcha.fail(function () {
//captcha failed
clearInterval(captchaTimer);
currentRequests = []; //clear the queue
});
$("#recaptchaModal").modal('show');
}
function verifyCaptcha() {
resetCaptcha = true;
var response = $("#g-recaptcha-response").val();
var encodedResponse;
// confirm its validity at the server end
$.ajax({
headers: headers,
cache: false,
url: "/ValidateCaptcha",
type: 'POST',
contentType: 'application/json',
success: function (data) {
captchaValidated = data;
if (!data) {
captchaPrompted = false;
}
},
error: function (raw, textStatus, errorThrown) { captchaValidated = false; captchaPrompted = false; alert("WTF Validate ReCaptcha Error?!: " + JSON.stringify(raw)); },
data: JSON.stringify({ "encodedResponse": response })
});
}
function invalidateCaptcha(){
deferredCaptcha.reject();
captchaValidated = null;
resetCaptcha = true;
}
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (settings.url == '/ValidateCaptcha' || captchaValidated) {
// we're validating the captcha server side now or it's already been validated - let it through
} else {
if (typeof settings.nested === 'undefined'){
settings.nested = true; //this flag is to determine whether it's already in the queue
currentRequests.push(settings); //add the request to the queue to be resubmitted
//prompt them with the captcha
openRecaptcha();
}
return false; // cancel this request
}
}
});
</script>
At $.ajaxPrefilter() use .then() chained to openCaptcha to call verifyCaptcha
if (captchaValidated == null){
openRecaptcha().then(verifyCaptcha);
}
at verifyCaptcha use .is() with parameter "*" to check if an element exists in document
if (grecaptcha.is("*")) {
at openRecaptcha(), if grecaptcha.render does not return asynchronous result return jQuery promise object using .promise(); else chain to grecaptcha.render and $("#recaptchaModal").modal('show'); using $.when()
return $("#recaptchaModal").modal('show').promise()
or
return $.when(grecaptcha.render(/* parameters */)
, $("#recaptchaModal").modal('show').promise())
Something like this? (pseudo-code)
verified = false;
$('#myButton').click(function(){
if (!verified) verify_by_captcha();
if (verified){
$.ajax(function(){
type: 'post',
url: 'path/to/ajax.php',
data: your_data
})
.done(function(recd){
//ajax completed, do what you need to do next
alert(recd);
});
}
});//end myButton.click

Not able to obtain value in view (directive)

I have an array in the profile-form.html directive's controller.
But I am neither able to obtain the value of that array (all_languages) nor iterate over it using ng-options in the directive. It's simply printing as string. I am new to Angular and maybe doing everything terribly wrong.
Directive
app.directive("profileForm", function() {
return {
restrict: "E",
templateUrl: "/static/translatorNgApp/profile-form.html",
controller: ['$scope','$http',function($scope, $http) {
this.getCookie = function(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
console.log(cookieValue);
return cookieValue;
};
$scope.csrftoken = this.getCookie('csrftoken');
$scope.myForm={};
$scope.all_languages = ['English', 'Hindi'];
$scope.language_pairs = [];
$scope.getAllLanguages = function () {
$http.get('/getAllLanguages'
).success(function(response) {
// success
$scope.all_languages.concat(response);
}).error(function(response) {
// failed
});
};
$scope.submitForm = function() {
var postData = {
method: 'POST',
url: '/accounts/tprofile/',
// headers : {'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'},
headers: {'X-CSRF-Token' : $scope.csrftoken },
data: $scope.myForm
};
$http(postData)
.then(function(response) {
// success
},
function(response) { // optional
// failed
});
console.log("Form submitted");
// $scope.message = "Sent Successfully 2";
// console.log(postData);
console.log(postData);
console.log($scope.myForm);
};
$document.ready(function(){
console.log("document ready");
$scope.getAllLanguages(); //This can be commented out for the question's sake.
});
}],
controllerAs: "profileFormCtrl"
};
});
Directive Template (profile-form.html)
<div class="form-group" >
<label for="id_source_language">Source language: </label>
<ul>
<li>
<pre>all_languages = {{all_languages | json}}</pre>
<select data-ng-model="source" ng-options="language for language in all_languages" class="form-control" id="id_source_language" name="source_language" placeholder="Source Language" required>
</select>
<button ng-click="language_pairs.pop($index)" aria-label="Remove">Remove</button>
<button ng-click="language_pairs.push({})">Add more pair</button>
</li>
</ul>
</div>
Using document.ready event inside a angular directive doesn't make sense to call specific method of scope. Because document.ready event has already happened after that event only angular started process page.
Ideally to make your code working state you need to remove $document.ready(function(){ .... }); code which isn't required there. And as document.ready is already accomplished, so the one which you had registered from directive wouldn't get call.

How to avoid data mix between $scope variables in ng-repeat when it is broadcasted in other controller?

I have two controllers. In one controller I am storing the data in scope variable for different categories and for different weeks and days. Here is the function for the same:
$scope.fetchWeekList = function(type) {
$scope.loading = true;
var paramtype = (type == 'mo')?'Mobiles':((type == 'ta')?'Tablets':((type == 'la')?'Laptops':'TVs'));
var weekListUrl = url + "/" + paramtype;
var request = $http({
method: "GET",
url: weekListUrl,
headers: { 'Accept' :'application/json','Content-Type' :'application/json', 'Accept-Language': 'en'}
});
request.success(
function(data) {
$scope.weekList = data.object;
$scope.loading = false;
});
request.error(
function(data, status) {
console.log(status);
$scope.weekList = data || "Request failed";
$scope.loading = false;
});
};
Please pat attention that I am fetching the data for the week lists for all the categories with this single function.
Then I am using this:
$scope.$on('fetchSaleDetails', function(event,type) {
$scope.fetchWeekList(type);
}
Then I am broadcasting it in the other controller like this:
$rootScope.$broadcast('fecthSaleDetails','mo');
$rootScope.$broadcast('fecthSaleDetails','ta');
$rootScope.$broadcast('fecthSaleDetails','la');
But when I switch the company the weeks of one category appears in the other and when I click again on the company the data changes. This is the function to update company.
$scope.updateCom = function(corresCom) {
$("html, body").animate({scrollTop: 0 }, "slow");
$rootScope.$broadcast('updateComDetail',corresCom);
$rootScope.$emit('fetchSaleDetails','mo');
$rootScope.$broadcast('fecthSaleDetails','mo');
$rootScope.$broadcast('fecthSaleDetails','ta');
$rootScope.$broadcast('fecthSaleDetails','la');
$scope.selectedCom = corresCom;
};
I would be grateful if someone can tell me the issue here. I have tried my best but no luck.
Thanks.

Categories

Resources