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)
Related
I have a Chrome extension uses an ng-show expression that checks a variable of Chrome storage, and displays a big blue button if the value is at zero. When opening the extension, however, the button could show up on the first click, or you may have to close and reopen the extension several times before it shows up (simply showing a blank body). This has been incredibly frustrating and is obviously a UX problem that I would like to fix before I publish the extension to the public.
Here is the div code from within the main view of the extension:
<div id="no_vseeks" ng-show="vseeks.length === 0">
<div class="big-button-container"><h1>CREATE</h1></div>
</div>
The expression is referring to an array called 'vseeks' in the Chrome local storage.
And here is what the extension is supposed to output:
But this is what the extension will show (seemingly) at random.
Please inform me if I need to include more code or images.
EDIT: Here's the main controller where the vSeeks array is being retrieved from Chrome storage. The console logs show that after the chrome.storage.get function is called, the array is present, but yet I still get a blank view.
app.controller('mainController', function($scope, $window) {
$scope.toggleAcc = function($event) {
var currentAcc = $event.currentTarget;
currentAcc.classList.toggle("active");
var panel = currentAcc.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
}
$scope.sendID = function(id) {
currentVID = id;
$window.location.href = "#!delete";
}
var noVseeks;
var home_button;
var newV_button;
console.log('controller reached', $scope.vseeks);
chrome.storage.sync.get('userData', function(items){
$scope.vseeks = items.userData.vSeeks;
console.log('inside chrome storage get', $scope.vseeks);
home_button = document.getElementById('home_button');
newV_button = document.getElementById('newV_button');
console.log('variables: ', home_button, newV_button);
if ($scope.vseeks.length < 1) {
home_button.style.visibility = "hidden";
newV_button.style.visibility = "hidden";
}
else {
newV_button.style.visibility = "visible";
if (home_button.style.visibility == "visible") {
home_button.style.visibility = "hidden";
}
}
});
});
I am unfamiliar with Chrome.storage.sync but it must be returning a non-angularjs promise (i.e. not $q). In that scenario, the function you are running upon resolve is executing outside of the angular digest cycle -- angular doesn't know it should be doing anything. The way to force angular to run its cycle is to use $scope.$apply. This will synchronize the model to the view and vice versa.
Hy every one!
I'm using in this project Angular 1.6.5 because my workteam think is better.
But I found an error with angular elements. In all forms of my app They stop the behaviour after a time. That is, it doesn't save date when i click in button save, ore delete. But when i refresh the page the elements start responds again. Any one have idea why?
In the project we are using a library to control style css. the problem may be there?
here is one piece of my html code
<div class="col-4-1">
<p class="link"><a class="btn-pri" href ng-click="savePartner()" ng-if="enableSaveBtn">Save</a></p>
<p class="link"><a class="btn-pri remove-link btn-red-50" href ng-click="delPartner()" ng-if="enableDelBtn">Erase</a></p>
</div>
pice controller code:
$scope.addLabelToPartner = function() {
$scope.partner.LabelList = $scope.partner.mcnList || [];
$scope.partner.LabelList.push($scope.partner.code);
$scope.partner.code = "";
};
$scope.delLabelFromPartner = function(idx) {
$scope.partner.LabelList.splice(idx, 1);
};
$scope.savePartner = function() {
$scope.saveErr = false;
WebService.createPartner($scope.partner).then(
function(response) {
common.widget.overlay.hide("overlayPartnerForm");
$scope.fetchAllPartners();
},
function(error) {
$scope.saveErr = true;
}
);
};
$scope.delPartner = function() {
$scope.saveErr = false;
WebService.deletePartner($scope.partner._id).then(
function(response) {
common.widget.overlay.hide("overlayPartnerForm");
$scope.fetchAllPartners();
},
function(error) {
$scope.saveErr = true;
}
);
};
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 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.
I have a form in a controller.
If there are unsaved change I want to warn the user about loosing them when leaving.
First I tried:
$scope.$on('$locationChangeStart', function (event, next, current) {
if ($scope.settingsForm.$dirty) {
event.preventDefault();
$scope.theUserWantsToLeave(function (result) {
if (result === "LEAVE") {
$location.path($location.url(next).hash());
$scope.$apply();
}
});
}
The code above throws an error in the line $scope.$apply();:
Error: $digest already in progress
removing this line just don't execute the redirect.
What would be the right way to do it?
===
Edit:
Other option I tried is handling it by reacting only when I need to cancel the redirection:
$scope.$on('$locationChangeStart', function (event, next, current) {
if ($scope.settingsForm.$dirty) {
$scope.theUserWantsToLeave(function (result) {
if (result === "STAY_HERE") {
event.preventDefault();
}
});
}
});
when doing things this way, the UI is breaking (I see the dialog and then it gone).
Seems like I can't call another async method while handling event.
I've managed to interrupt by the route change by listening for $locationChangeSuccess and then assigning $route.current to the last route.
Also, if $scope.theUserWantsToLeave is async, then the callback passed to it may fire too late to stop the route change. You could get around an async call by using a blocking flag, such as okToDiscardChanges in my examples.
JS:
$scope.okToDiscardChanges = false;
var lastRoute = $route.current;
$scope.$on('$locationChangeSuccess', function () {
if ($scope.settingsForm.$dirty && !$scope.okToDiscardChanges) {
$route.current = lastRoute;
$scope.errorMessage = 'You have unsaved changes. Are you sure you want to leave?';
}
});
HTML:
<form name="settingsForm">
<!-- form stuff -->
</form>
<div ng-show="errorMessage">
{{ errorMessage }}
<label>
<input type="checkbox" ng-model="okToDiscardChanges"> Discard Changes
</label>
</div>
Hope this works!