ng-if dependent element not disappearing - angularjs

I've got the following problem:
When clicking a button my searchResults window opens due to 'displayResults' is set on 'true'. But when clicking on the close button it's not closed even if $scope.displayResults is set to false again.
html
<section id="searchResults" data-ng-if="displayResults">
<h2>Suchergebnisse:</h2>
<div id="closeMenuS">❌</div>
<ul data-ng-repeat="x in sItems">
....
</ul>
</section>
AngularJS
$http({
method : "POST",
url : "/searchFor",
data: {item: sI, dID: searchID}
}).then(function(response){
if(response.data.length > 0 || response.data != 'undefined'){
$("#sidebar").css({pointerEvents: "none"});
$("#default").css({pointerEvents: "none"});
$scope.displayResults = true;
$scope.sItems = [];
for(let i = 0; i < response.data.length; i++){
$scope.sItems[i] = response.data[i];
}
window.setTimeout(function(){
$("#closeMenuS").click(function(){
$scope.displayResults = false;
$("#sidebar").css({pointerEvents: "auto"});
$("#default").css({pointerEvents: "auto"});
});
}, 30);
}
}, function myError(response) {
$scope.myWelcome = response.statusText;
});

If you're in a callback in a jQuery listener, you need to explicitly apply the change. Try this:
window.setTimeout(function(){
$("#closeMenuS").click(function(){
$scope.displayResults = false;
$scope.$apply();
$("#sidebar").css({pointerEvents: "auto"});
$("#default").css({pointerEvents: "auto"});
});
}, 30);

AngularJs has its own implementation for timeouts. By using the native setTimeout() you are hiding what you are trying to do from angular.
The result is that AngularJs has no idea that your $scope changed.
The correct way would be to inject the $timeout service and replace your setTimeout() call with $timeout (I stripped down the rest of the code for simplification):
angular.module("YourModule")
.controller("YourController", ["$scope", "$timeout", YourController]);
function YourController($scope, $timeout)
{
...
$scope.doSomeMagic = function()
{
$timeout(function()
{
$("#closeMenuS").click(function()
{
$scope.displayResults = false;
$("#sidebar").css({pointerEvents: "auto"});
$("#default").css({pointerEvents: "auto"});
});
}, 30);
}
...
}
This is why the $apply() function suggested by Itamar works. You're basically forcing AngularJs to detect changes.
However, the better practice is to use the tools provided by AngularJs and avoid native javascript when possible.

Related

Adding auto refresh after a particular interval (5 seconds) in angular js

I have a page in which which has multiple tabs. I want to add the feature that the tab is reloaded automatically after a fixed duration. I have the following:
<uib-tab index="$index + 1" ng-repeat="environmentRegionTab in ctrl.environmentRegionTabs"
heading="{{environmentRegionTab.environmentRegionName}}"
select="ctrl.onEnvironmentRegionSelect(environmentRegionTab.id);">
<div class="panel-body tab-content">
<div class="alert alert-success" ng-show="ctrl.deployStatus[ctrl.environmentRegion.name].show">
<strong>Launched deployment with execution id
{{ctrl.deployStatus[ctrl.environmentRegion.name].id}}</strong>
</div>
...................
And the following controller:
export function ServiceDetailController(ecsServiceResponse, teamListResponse, productListResponse, businessSubOwnerListResponse, serviceInstancesResponse, businessOwnerListResponse, EcsService, SecretsService, $location, $stateParams, $uibModal, $scope, $state, $window) {
'ngInject';
var self = this;
var serviceInstanceId;
self.ecsAuthControl = {};
self.initialize = _initialize;
self.clearMessages = _clearMessages();
self.onEnvironmentRegionSelect = _onEnvironmentRegionSelect;
$scope.reloadRoute = function() {
$state.reload();
};
function _onEnvironmentRegionSelect(serviceInstanceId) {
self.selectedserviceInstanceId = serviceInstanceId;
if (serviceInstanceId) {
$location.search('serviceInstanceId', serviceInstanceId);
_loadEnvironmentRegion();
} else {
$location.search('serviceInstanceId', null);
_loadSummary();
}
}
}
I am not able to understand how to add the fixed time duration? I also would like to show a counter ticking down from 5 to 0 after which the page is reloaded. How can I do it? I declared the reload function but I am not able to figure out how to add a fixed timer? Thanks!
Make use of $interval service in angularjs:
$interval(function () {
$scope.reloadRoute();
}, 5000);
(make sure to pass $interval as a dependency to controller)
Example Plunker
Here is one of the safest way through which you can achieve the functionality.
Function which does the refresh:-
var poll = function() {
console.log("polling");
$scope.doRefresh(); // Your refresh logic
};
Call the poll from StartPollar:
var startPoller = function() {
if (angular.isDefined(stop)) {
stopPoller();
}
stop = $interval(poll, $scope.intervalTime); //$scope.intervalTime : refresh interval time
};
If you want to Stop it:
var stopPoller = function() {
if (angular.isDefined(stop)) {
$interval.cancel(stop);
stop = undefined;
console.log("cancelled poller operation");
} else {
console.log("do nothing");
}
};

Infinite Scrolling reloads page, Ionic

What is happening is that when I reach the bottom of the page, it refreshes and loads the new data, however it doesn't show the data for the previous and current page.
For example it looks like this:
1
2
3
4
* end of page, refreshes page*
5
6
7
8
My function in my controller:
var i = 0;
$scope.result = [];
$scope.noMoreItemsAvailable = false;
$scope.loadMore = function() {
if (i < 4) {
$http.get(url.recommended + i).success(function(response) {
i++;
$scope.result = $scope.result.push(response);
console.log(response);
$timeout(function() {
$scope.result = response
});
$scope.$broadcast('scroll.infiniteScrollComplete');
});
} else {
$scope.noMoreItemsAvailable = true;
}
}
HTML:
<div class="item item-text-wrap" ng-click="post($event,res)" ng-repeat="res in result" ng-controller="recommendedJobsCtrl" ui-sref="tabs.jobDetails">
<ul>
<li id="jobTitle">{{res.title }}</li>
</ul>
</div>
<ion-infinite-scroll ng-if="!noMoreItemsAvailable" on-infinite="loadMore()" distance="1%"></ion-infinite-scroll>
Well, there are 2 main problems:
You're attributing the value of the push for your array. You shouldn't do this, you just have to do this:
$scope.result.push(response);
You should remove this timeout because it's overriding what you already have:
$timeout(function() {
$scope.result = response
});
By the way, I'd recommend you to create a factory to prevent problems with async data.
You could do something like this:
angular
.module('app', [])
.controller("MainCtrl", MainCtrl)
.factory("ItemsFactory", ItemsFactory);
ItemsFactory.$inject = ['$http'];
function ItemsFactory($http) {
var factory = {
getPages: getPages
};
return factory;
function getPages(url) {
return $http.get(url);
}
}
Then, in your controller:
MainCtrl.$inject = ['$scope', 'ItemsFactory'];
function MainCtrl($scope, ItemsFactory) {
var url = 'https://www.google.com';
function getResponse(response) {
$scope.result.push(response.data);
}
function getError(response) {
console.log(response);
}
ItemsFactory.getPages(url)
.then(getResponse);
.catch(getError);
}
Please, note: I also recommend you to change the way that you're retrieving your items from your back-end. It isn't a good way to retrieve the elements 1 by 1. The correct in your case is to retrieve all the four items at once and treat them in controller.
Your timeout is causing the $scope.result to be overwritten by the response.
Just remove this and it should append the response to the result
REMOVE THIS
$timeout(function ()
{
$scope.result=response
});

AngularJS hide div after delay

While creating my app in AngularJS (awesome framework!) I stuck in one task: how to show and hide hidden div (ng-show) after some action.
Detailed description: using AngularUI $modal service I'm asking if user wants to perform action, if yes, I run service to POST request via $http to send which item I want to delete. After it finished I want to show hidden div with information, that process has accomplished successfully. I created a simple service with $timeout to set div's ng-show and hide after some time but it doesn't update variable assigned to ng-show directive. Here is some code:
Controller for listing and deleting items
$scope.deleteSuccessInfo = false; //variable attached to div ng-show
$scope.deleteCluster = function(modalType, clusterToDelete) {
modalDialogSrvc.displayDialog(modalType)
.then(function(confirmed) {
if(!confirmed) {
return;
}
clusterDeleteSrvc.performDelete(itemToDelete)
.then(function(value) {
//my attempt to show and hide div with ng-show
$scope.deleteSuccessInfo = showAlertSrvc(4000);
updateView(itemToDelete.itemIndex);
}, function(reason) {
console.log('Error 2', reason);
});
}, function() {
console.info('Modal dismissed at: ' + new Date());
});
};
function updateView(item) {
return $scope.listItems.items.splice(item, 1);
}
Part of service for deleting item
function performDelete(itemToDelete) {
var deferred = $q.defer(),
path = globals.itemsListAPI + '/' + itemToDelete.itemId;
getDataSrvc.makeDeleteRequest(path)
.then(function() {
console.log('Item removed successfully');
deferred.resolve({finishedDeleting: true});
}, function(reason) {
console.log('error ', reason);
deferred.reject(reason);
});
return deferred.promise;
}
return {
performDelete: performDelete
};
Simple service with $timeout to change Boolean value after some time
angular.module('myApp')
.service('showAlertSrvc', ['$timeout', function($timeout) {
return function(delay) {
$timeout(function() {
return false;
}, delay);
return true;
};
}]);
I tried $scope.$watch('deleteSuccessInfo', function(a, b) {...}) with no effect. How to apply false after some delay? Or maybe You would achieve this in other way?
Thank You in advance for any help!
Change the implementation of the showAlertSrvc service, like this:
angular.module('myApp')
.service('showAlertSrvc', ['$timeout', function($timeout) {
return function(delay) {
var result = {hidden:true};
$timeout(function() {
result.hidden=false;
}, delay);
return result;
};
}]);
And then change thes 2 lines:
The declaration of deleteSuccessInfo should be like this:
$scope.deleteSuccessInfo = {};
And then do this:
$scope.deleteSuccessInfo = showAlertSrvc(4000);
And finally in your view do this "ng-show=!deleteSuccessInfo.hidden"
Example

Angularjs how to stop infinity scroll when server has no more data to send

Based on THIS example which I have modified with an Ajax request to get the data Im struggling to find a way to stop it when the server has no more data to send.
I have tried to add a boolean variable in a service, and a $watch method in the directive but it is not working.
Is there a simple way to to achieve that ?
This is not my code but if there is no easy answer I can post my code with the changes I have done.
thanks for your help.
<div id="fixed" when-scrolled="loadMore()">
<ul>
<li ng-repeat="i in items">{{i.id}}</li>
</ul>
</div>
function Main($scope) {
$scope.data = { comments : [] }
$scope.loadMore = function(){
$http({
url: '/comment/next',
method: "POST"
})
.success(function(data){
for(var i=0; i<data.length; i++){
$scope.data.comments.push(data[i]);
}
});
}
}
angular.module('scroll', []).directive('whenScrolled', function() {
return function(scope, elm, attr) {
var raw = elm[0];
elm.bind('scroll', function() {
if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight) {
scope.$apply(attr.whenScrolled);
}
});
};
});
I can only guess as to a solution without seeing more of your code (specifically what is returned by $http results), but I believe this sort of thing could work, depending on the structure of a comment object.
function Main($scope) {
$scope.data = { comments : [] }
$scope.scrollComplete = false;
$scope.loadMore = function(){
if($scope.scrollComplete || $scope.loading) { return; }
$scope.loading = true;
$http({
url: '/comment/next',
method: "POST"
})
.success(function(data){
for(var i=0; i<data.length; i++){
var totalComments = $scope.data.comments.length;
if($scope.data.comments[totalComments - 1].someID === data[i].someID){
$scope.scrollComplete = true;
}else{
$scope.data.comments.push(data[i]);
}
}
$scope.loading = false;
}).error(function(){ $scope.loading = false });
}
}
Just bear in mind that a solution like this isn't really elegant. What I like to do is allow an item ID to be passed to the API (i.e. your /comment/next), and treat that as the last grabbed item. So the API will only give me back everything after that. Using that method, you would simply have to pass the last comment ID to the API.

Angular call service on asynchronous data

I have a service that make some calls to retrieve data to use in my app. After I've loaded data, I need to call another service to make some operations on my data. The problem is that second service will not have access to the data of the first service.
I've made a plunker: plunkr
First service
app.factory('Report', ['$http', function($http,$q){
var Authors = {
reports : [],
requests :[{'url':'data.json','response':'first'},
{'url':'data2.json','response':'second'},
{'url':'data3.json','response':'third'}]
};
Authors.getReport = function(target, source, response, callback) {
return $http({ url:source,
method:"GET",
//params:{url : target}
}).success(function(result) {
angular.extend(Authors.reports, result)
callback(result)
}
).error(function(error){
})
}
Authors.startQueue = function (target,callback) {
var promises = [];
this.requests.forEach(function (obj, i) {
console.log(obj.url)
promises.push(Authors.getReport(target, obj.url, obj.response, function(response,reports){
callback(obj.response,Authors.reports)
}));
});
}
return Authors;
}])
Second service
app.service('keyService', function(){
this.analyze = function(value) {
console.log(value)
return value.length
}
});
Conroller
In the controller I try something like:
$scope.result = Report.startQueue('http://www.prestitiinpdap.it', function (response,reports,keyService) {
$scope.progressBar +=33;
$scope.progress = response;
$scope.report = reports;
});
$scope.test = function(value){
keyService.analyze($scope.report.about);
}
I think this is what you are going for? Essentially, you want to call the second service after the first succeeds. There are other ways of doing this, but based on your example this is the simplest.
http://plnkr.co/edit/J2fGXR?p=preview
$scope.result = Report.startQueue('http://www.prestitiinpdap.it', function (response,reports) {
$scope.progressBar +=33;
$scope.progress = response;
$scope.report = reports;
$scope.test($scope.report.about); //added this line
});
$scope.test = function(value){
$scope.example = keyService.analyze(value); //changed this line to assign property "example"
}
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<p>Progress notification : {{progress}}!</p>
<div ng-show="show">
<progress percent="progressBar" class="progress-striped active"></progress>
</div>
<pre>{{report}}</pre>
<pre>{{report.about}}</pre>
{{example}} <!-- changed this binding -->
</body>

Resources