I'm trying to check if something exists and show a button based on that. What am I doing wrong and how can I fix it?
My code looks like this:
$scope.isURL = function(url) {
$http.get(url).success(function (data) {
if (data === 302) {
return true
}
});
};
my HTML:
<ul ng-repeat="foo in foos">
<button ng-hide="isURL(foo.url)">visit site</button>
<button ng-show="isURL(foo.url)">remove bookmark?</button>
</ul>
Error loop I'm getting:
Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []
The biggest problem is that you are setting up a binding on a function that is returning a new promise each time.
The $digest cycle is going to fire, and evaluate the result of your function and determine that the result has changed, making it dirty. This will trigger another $digest loop that will continue ad infinitum.
A better option would be to check each of the urls, and set a property on the foo objects if they are good.
app.controller('displayCtrl', ['$scope','$http', function(scope,$http){
var foos = [
{name: 'foo-1', url:'assets/foo.html'},
{name: 'foo-2', url:'assets/bad.html'}
];
angular.forEach(foos, function(foo){
$http.get(foo.url).success(function (data) {
if(data === 302){
foo.isUrl = true;
}
});
});
scope.foos = foos;
}]);
Which you can then bind to in your markup like this:
<ul ng-repeat="foo in foos">
<button ng-hide="foo.isUrl">visit site</button>
<button ng-show="foo.isUrl">remove bookmark?</button>
</ul>
You may want to try something like this instead:
$scope.urls = {};
$scope.isURL = function(url) {
if (angular.isUndefined($scope.urls[url])) {
$scope.urls[url] = false;
$http.get(url).success(function(data) {
if (data === 302) {
$scope.urls[url] = true;
}
return data;
});
}
return $scope.urls[url];
};
That will also prevent you from checking each URL more than once. I am not sure why you are checking for data === 302, but I assume you know what you are doing there. If you are trying to check the status code, that will not work, you would need to use this instead:
$http.get(url).then(function(response) {
if (response.status === 302) {
$scope.urls[url] = true;
}
return response;
});
Edit
Documentation on how to use ngShow (and other directives) can be found on the AngularJS web site. The if the expression evaluated in the ngShow directives is truthy the element will be shown. In your code sample, $scope.isURL(...) does not return a value.
Related
Based on changing the value of a variable I wish to display an error message in my html. I call an api from my angular code, and if it returns an error, I have set up a setInterval function that should update bookingData.tracking_id to false and then show an error message in the html. This should be very easy but combining this with setInterval is proving slightly difficult.
Here is the angular/javascript
this.confirmTrackingTest = function () {
TestService.finalPackageCheck({tracking_id: controller.bookingData.tracking_id}).$promise.then(function (data) {
return data;
}).catch(function (err) {
var i = 0;
var interval = setInterval(function () {
i += 1;
if (i === 3) {
if (err.statusText === "Not Found") {
controller.bookingData.showErrorMessage = true;
}
clearInterval(interval)
}
}, 2000);
console.log(controller.bookingData.showErrorMessage)
});
}
this.bookingData = {
showErrorMessage: false,
tracking_id: 1
};
Here is the html:
{{Packs.bookingData.showErrorMessage}}
<div class="alert alert-danger" role="alert" ng-if="Test.bookingData.showErrorMessage">
<p>Please show this message</p>
</div>
The {{Packs.bookingData.showErrorMessage}} shows false so that is recongised in the html.
Please let me know if any further information is required.
This is exactly the reason why the Angular gods invented $interval in order to remove the need to manually use $apply.
So either you call $scope.$apply inside the callback or you use $interval (don't forget to include it in the parameter list for depedency injection)
This is the function which load listings from server. Initially listings are displayed but when gets null response on applying filter, it still shows previous result and not clearing previous listings.
$scope.browseListing = function (strURL) {
$scope.CurrentTab = strURL;
$scope.getURL(strURL);
$http.post($scope.URL)
.then(function (response) {
if (response.data != 'null') {
$scope.Data = response.data;
$scope.TotalListingCount = $scope.Data.length;
$window.alert('Result is not null');
}
else {
$scope.TotalListingCount = '0';
$window.alert('Result is null');
$scope.Data = [];
}
}, function (response) {
$log.info(response);
});
};
Edited
How do I solve this so that on empty response previous listings gets cleared and shows no listings?
May be your scope does not update. Please try this below ( it's not 100% good approach, But at this time you can solve your issue)
if(!$scope.$$phase) {
$scope.$apply(
$scope.Data = [];
);
}
$scope.TotalListingCount = '0';
$window.alert('Result is null');
and please check your console having any error.
Update :
try another way like this (declare empty object globally)
.then(function (response) {
$scope.TotalListingCount = '0';
$scope.Data = [];
if (response.data != 'null') {
$scope.Data = response.data;
$scope.TotalListingCount = $scope.Data.length;
$window.alert('Result is not null');
}
else {
$window.alert('Result is null');
}
}
It's does not works well, then please share your filter code. Bcs the problem should be there.
The following create new scopes, and inherit prototypically: ng-repeat, ng-include, ng-switch, ng-view, ng-controller, directive with scope: true, directive with transclude: true.
So, use $parent with 'ng-repeate' to reference to parent scope instead of using newly created scope byng-repeat` as-
<tr ng-repeat="listing in $parent.Data | orderBy : sortColumn : SortDirection">
After adding $parent in ng-repeat to scope property in UI it updates UI as per changes.
Here is full description of scope
I have a Poller that I have setup that has 2 files which are being queried. When new data has been found I am trying to set the color of my text background in the view but its just not happening.
If someone can solve this issue that would be great I am also welcome to suggestions to improving the structure of the code.
Service:
function Poller($http, $timeout) {
var projectcache = { response: [], calls: 0 };
var msgcache = { response: [], calls: 0 };
var newdata = false;
var msgdata = false;
var msgcolor = {};
var projectcolor = {};
var poller = function () {
$timeout(poller, 10000);
console.log("Begin Poller!");
$http.get('http://localhost/app/controllers/php/getProjects.php')
.then(function(r) {
if (r.data.projects.length > projectcache.response.length) {
newdata = true;
projectcolor = 'green';
} else {
newdata = false;
projectcolor = 'green';
};
angular.copy(r.data.projects, projectcache.response);
console.log("New Data Found: " + newdata);
});
$http.get('http://localhost/app/controllers/php/getMessages.php')
.then(function(m) {
if (m.data.messages.length > msgcache.response.length) {
msgdata = true;
msgcolor = 'green';
} else {
msgdata = false;
msgcolor = 'green';
};
angular.copy(m.data.messages, msgcache.response);
console.log("New Msg Found: " + msgdata);
});
};
poller();
return {
projects: projectcache.response,
messages: msgcache.response,
newdata: newdata,
msgdata: msgdata,
msgcolor: msgcolor,
projectcolor: projectcolor
};
};
View:
<li ng-class="{active: selectTab=='inbox'}" style="background-color:{{msgcolor}};" ng-click="selectTab='inbox'">Inbox</li>
<li ng-class="{active: selectTab=='projects'}" style="background-color:{{projectcolor}};" ng-click="selectTab='projects'">Projects</li>
Controller:
app.controller("taskbarController", ['$scope', 'authData', '$location', 'projectsModal', 'sendMessageModal', 'Poller',
function ($scope, authData, $location, projectsModal, sendMessageModal, Poller) {
$scope.msgcolor = Poller.msgcolor;
$scope.projectcolor = Poller.projectcolor;
}]);
My first thought is to use ng-class for this. I see you already have ng-class handling the display of your 'active' class.
If you'd like to try this approach out, I would:
1. Create css clases for each state/color you want to change to. (Can do this in external css file or between tags you create at the beginning of your page.
.successBackground {
background-color:green;
}
.errorBackground {
background-color:red;
}
Modify your ng-class attributes. Here I am assuming that success means that msgdata=true and error means that msgdata=false
Current html:
<li ng-class="{active: selectTab=='inbox'}" style="background-color:{{msgcolor}};" ng-click="selectTab='inbox'">Inbox</li>
<li ng-class="{active: selectTab=='projects'}" style="background-color:{{projectcolor}};" ng-click="selectTab='projects'">Projects</li>
Updated html:
<li ng-class="{active: selectTab=='inbox', successBackground:msgdata===true, errorBackground:msgdata===false}" ng-click="selectTab='inbox'">Inbox</li>
<li ng-class="{active: selectTab=='projects',successBackground:msgdata===true, errorBackground:msgdata===false}" ng-click="selectTab='projects'">Projects</li>
Now when your msgdata is updated, the successBackground and errorBackground are automatically updated based on the latest msgdata value.
Hope this helps!
#Elevant, the comment option didn't allow me to format my code snippets, so I am replying to your latest comment in this answer post.
I'm not sure if the watcher can listen to just the Poller object or if it'll need to listen to each attribute separately (msgColor, projectColor). In my code snippet here, I'll assume we cannot and we'll need to listen to each individually.
Current code:
$scope.msgcolor = Poller.msgcolor;
$scope.projectcolor = Poller.projectcolor;
Updated with watchers:
$scope.$watch('Poller.msgcolor', function(newValue,oldValue) {
$scope.msgcolor = Poller.msgcolor;
});
$scope.$watch('Poller.projectcolor', function(newValue,oldValue) {
$scope.projectcolor = Poller.projectcolor;
);
Though if you still wanted to look into the option to move $timeout, I would make the following changes (not sure if this matches what you had tried).
In the Poller service definition remove $timeout. Updated snippet:
function Poller($http)
Still in Poller, remove this line:
$timeout(poller, 10000);
In the Controller add $timeout - updated snippet:
app.controller("taskbarController", ['$scope', 'authData', '$location', 'projectsModal', 'sendMessageModal', 'Poller','$timeout'
function ($scope, authData, $location, projectsModal, sendMessageModal, Poller,$timeout)
Then in the controller, you would add:
$timeout(function(Poller) {
Poller.poller();
$scope.msgcolor = Poller.msgcolor;
$scope.projectcolor = Poller.projectcolor;
}, 10000);
I hope this helps, I haven't had a chance to test the code, so you may have to tinker around with it a bit. Let me know how it goes!
I fetch a collection from the server and I would like to get detail for each item. All requests are received correctly, but the paragraph Loading... doesn't hide.
<h2 ng-repeat-start="server in hypervisors track by server.ip | orderBy:server.ip">
{{server.ip}}
</h2>
<div ng-repeat-end>
<p ng-hide="{{server.loaded}}" class="ng-hide">Loading...</p>
When I uncomment the line in controller before post everything works fine.
vmwareStatusApp.controller('Home', function ($scope, $http) {
$http.post('Home/ListHypervisors').success(function (data) {
$scope.hypervisors = data;
$scope.listLoaded = true;
$scope.hypervisors.forEach(function (item) {
//item.loaded = true; // this line works
$http.post('Home/HostInfo', { 'ip': item.ip }).success(function (data) {
$scope.hypervisors[0].loaded = true;
item.loaded = true;
item.detail = data;
})
.error(function(data) {
item.loaded = true;
item.error = data;
item.displayError = true;
});
});
});
});
There are many posts about refreshing view, but I haven't found any working for me. Neither anti-patter with calling $digest() didn't work, because of multiple callback. Which part of AngularJS tutorial have I skipped?
Just remove the braces from your ng-hide like this
ng-hide="server.loaded"
ng-hide and angular directives should be read like this :
ng-directive = "somethingAngularWillInterpret"
The opposite exemple is in your HTML angular will not know what he should interpret instead of just showing some text
<b>server.loaded</b> will show a bold "server.loaded"
To notice angular that he need to interpret we will use the braces
<b>{{somethingAngularWillInterpret}}</b> will show a bold result of the interpretation
EDIT :
So doing this ng-hide="{{server.loaded}}" is probably saying to angular to interpret the result of the server.loaded interpretation like a var named true or a var named false (just speculation, i need to try it).
Just tested it, this just lead to a syntax error.
I'm trying to build a simple infinite scroll. It loads the data fine but after loading, new added elements' directives don't work.
This is relevant part of the scroll checking and data loading directive.
.directive("scrollCheck", function ($window, $http) {
return function(scope, element, attrs) {
angular.element($window).bind("scroll", function() {
// calculating windowBottom and docHeight here then
if (windowBottom >= (docHeight - 100)) {
// doing some work here then
$http.get('service page').then(function (result) {
if (result.data.trim() != "") {
var newDiv = angular.element(result.data);
element.append(newDiv);
}
// doing some other work
},function () {
// error handling here
});
}
scope.$apply();
});
};
})
Service page returns some repeats of this structure as result.data
<div ...>
<div ... ng-click="test($event)"></div>
<div ...>...</div>
</div>
As i said data loads just fine but those test() functions in ng-clickdirectives don't work. How to get em work?
I believe you are going to need to compile the html element returned. Something like this
$compile(newDiv)(scope); // Corrected. Thanks
You'll need to be sure and pass in $compile into your function