Deletion of elements in array with $timeout does not work properly - javascript

The directive notification should delete "itself" after 5 seconds. However some elements get missed and some get deleted more than once. Identifier property is unique for each notification. Thanks for help.
Factory
angular.module('AdS').factory('notificationFactory', function () {
var notificationFactory = {};
notificationFactory.notifications = [];
notificationFactory.identifier =0;
notificationFactory.add = function(note){
if(typeof note!=='undefined'){
notificationFactory.identifier++;
note.identifier = notificationFactory.identifier;
notificationFactory.notifications.push(note);
}
}
notificationFactory.delete = function (note) {
if(typeof note!=='undefined'){
for(var i =0;i<notificationFactory.notifications.length;i++){
if(notificationFactory.notifications[i].identifier==note.identifier){
notificationFactory.notifications.splice(i,1);
}
}
}
return "";
}
notificationFactory.getNotifications = function () {
return notificationFactory.notifications;
}
return notificationFactory;
});
Directive
angular.module('AdS').directive('siteNotification', [
'$timeout',
function ($timeout) {
return {
restric: "E",
templateUrl: "/Templates/htmlBits/notification.html",
scope: {
note:"=",
center:"="
},
link: function (scope, element, attrs) {
$timeout(function () {
scope.center.delete(scope.note);
}, 5000);
scope.delete=function(note){
scope.center.delete(note);
}
}
};
}
]);
html
<site-notification ng-repeat="not in notificationCenter.notifications track by $index" center=notificationCenter note=not ></site-notification>

Rather than using an array for notificationFactory.notifications, you could use an object, with the unique identifier pointing to your note like so:
notificationFactory.notifications = {};
notificationFactory.add = function(note) {
if (typeof note!=='undefined') {
notificationFactory.identifier++;
notificationFactory.notifications[notificationFactory.identifier] = note;
}
}
notificationFactory.delete = function (note) {
if(typeof note!=='undefined'){
notificationFactory.notifications[notificationFactory.identifier] = null;
}
return "";
}
Also, in your directive, you seem to be injecting notificationFactory via the html. This is very unusual, the typical way to inject your factory is as follows:
angular.module('AdS').directive('siteNotification', [
'$timeout',
'notificationFactory',
function ($timeout, notificationFactory) {
...
}
The only reason I can see to do it differently way is if you want to be able to inject a different type of factory into your directive.

Related

Checking whether a function is called when another function is triggered

I am new to AngularJS and Jasmine. Given the following controller, how do I test whether the allPanelsRetrieved() function is called when the $scope.getPanels is triggered?
angular.
module('panelList').
controller('PanelListController', ['Panel', 'PanelSelection', '$scope', '$location', '$uibModal', '$rootScope',
function PanelListController(PanelSelection, $scope, $location, $uibModal, $rootScope) {
$scope.maxAbv = 2;
$scope.minAbv = 12;
this.allPanelsRetrieved = (index, before, filterParams) => {
.....
};
$scope.getPanels = () => {
const filterParams = {};
filterParams.abv_lt = $scope.minAbv;
filterParams.abv_gt = $scope.maxAbv;
$scope.currentPagePanels = this.allPanelsRetrieved (1,[], filterParams);
};
}]).
component('panelList', {
templateUrl: '/components/panel-list/panel-list.template.html',
controller:'PanelListController',
});
Assuming you want allPanelsRetrived to be called, then simply use a boolean.
var bool = false
this.allPanelsRetrieved = (index, before, filterParams) => {
.....
bool=true;
};
$scope.getPanels = () => {
if (bool) {
const filterParams = {};
filterParams.abv_lt = $scope.minAbv;
filterParams.abv_gt = $scope.maxAbv;
$scope.currentPagePanels = this.allPanelsRetrieved (1,[], filterParams);
} else {
// allPanelsRetrieved was not called
}
};
I can see that allPanelsRetrieved seems to be a private(local) method and used inside that controller.
You need not test private(local) methods execution.
If you still want to check if the method is triggered or not you can use jasmine's toHaveBeenCalled() method
execept(myMethod).toHaveBeenCalled();
passes when method is called.

How to convert website into multi language using AngularJs

My approach is this it is translating one time when i applied on element on angular template but when i applied multiple directive as template making multiple time api call so how to avoid it.
good solution please comment
var directive = {
restrict: 'AE',
scope: false,
multiElement: true,
link: link
};
$rootScope.cityName = cookieService.getCookie('lang');
$rootScope.translateThese = [];
$rootScope.translationData = '';
$rootScope.translationCounter = 0;
function link(scope, element, attr) {
scope.$evalAsync(function() {
$rootScope.translateThese.push(element[0]);
scope.$on('translateAPISuccess', function(e, data) {
angular.forEach($rootScope.translateThese, function(elem) {
var translatedString = $rootScope.translationData[elem.innerHTML.trim().toLowerCase()];
elem.innerHTML = translatedString;
// $compile(element.contents())(scope);
})
})
});
// This is a 0 second timeout to push the execution of this
// directive to the next digest cycle where all the dynamic values
// are present. I can also use scope.$evalSync for this purpose.
//
// #example
// $timeout(function() {
//
$timeout(function() { // We could also use scope.$watch here watching $viewContentLoaded // scope.$watch('$viewContentLoaded', function(e, u) {
$rootScope.translationCounter++;
var translateThese = [];
// if (scope.$last === true && scope.translationCounter === $rootScope.translateThese.length) { //we can use this but scope.$last isn't working
if ($rootScope.translationCounter === $rootScope.translateThese.length) {
$rootScope.translateThese.forEach(function(sentence) {
// trim.apply(sentence).split(" ").forEach(function(v) {
translateThese.push(sentence.innerHTML.replace(/[^a-zA-Z ]/g, ""));
// })
})
dataService.staticTranslation('guj', translateThese).then(function (response) {
$rootScope.translationData = response.response;
scope.$emit('translateAPISuccess', response.response);
// When using the translateAPI from horizontal
// $rootScope.translationData = JSON.parse(response.response).StaticLangTranslationApplicationResponse.StaticLangTranslationApplication.translations;
// scope.$emit('translateAPISuccess', JSON.parse(response.response).StaticLangTranslationApplicationResponse.StaticLangTranslationApplication.translations);
});
}
});
}
return directive;
}
You could look at using the Angular Translate service: https://github.com/angular-translate/angular-translate
It offers a filter, directive and service for translating your content.

How to filter data based on two custom filters in Angular js

I have two custom filters, I want to filter my data using both these custom filters. But i faced the problem, If I used one by one then they work good, But when I try to use both filters at the same time then no output. My code is as follow:
<script>
var myApp = angular.module('myApp', []);
myApp
.filter('selectedTags', function() {
return function(postdata, tags) {
return postdata.filter(function(task) {
for (var i in task.tarn_list) {
if (tags.indexOf(task.tarn_list[i]) != -1) {
return true;
}
}
return false;
});
};
})
.filter('selectedDep', function() {
return function(postdata, tags) {
return postdata.filter(function(task) {
for (var i in task.deployment) {
if (tags.indexOf(task.deployment[i]) != -1) {
return true;
}
}
return false;
});
};
})
.controller('PostList', ['$scope', '$http', function($scope, $http) {
var jsonFile='../../json.php';
$http.get(jsonFile).success(function(data) {
$scope.postdata = data;
});
$scope.useMakes=[]
$scope.checkBoxModel={
search:[],
ddsearch:[]
};
$scope.totalFeatures=features;
$scope.deployment=all_deployment;
}]);
</script>
My div is as follow on which I want to apply filter:
<div ng-repeat="record in postdata | selectedDep:checkBoxModel.ddsearch | selectedTags:checkBoxModel.search" >
Not having seen the actual dataset, this here should float the boat I reckon - given the properties you've exposed in your question and the nature of the loops;
https://jsfiddle.net/op7m14m1/1/
Instead of for in loops, I've opted for nested filters (which is in essence what you're doing).
var predicate = [];
dataset.filter(function (a) {
var inner = a.inner.filter(function (b) {
return predicate.indexOf(b) > -1;
});
return inner.length > 0;
});
Looking at the two filters you have, you could break it down into a single function with a bound (or passed in) parameter dictating which property to use as a matcher for the filter(s).
Something like this;
function generic () {
return function (prop, dataset, predicate) {
return dataset.filter(function (element) {
var innards = element[prop].filter(function (iEl) {
return predicate.indexOf(iEl) > -1;
});
return innards.length > 0;
});
}
}
And then to use it you could do the following;
module.filter('genericFilter', generic);
module.filter('selectedDep', generic.bind(null, 'deployment');
module.filter('selectedTags', generic.bind(null, 'tarn_list');
// $filter('genericFilter')('deployment', [], ['a']);
// $filter('selectedDep')([], ['b']);
// $filter('selectedTags')([], ['c']);
This setup allows for a single function, that you can reuse to your heart's content - simply pass in the property you would like to do a deep filter of, or bind it preemptively.

angular watch object not in scope

I have a service in which values can change from outside Angular:
angularApp.service('WebSocketService', function() {
var serviceAlarms = [];
var iteration = 0;
this.renderMessages = function(alarms, socket) {
if (! angular.equals(serviceAlarms, alarms)) {
serviceAlarms = alarms;
iteration++;
}
};
this.getAlarms = function () {
return serviceAlarms;
};
this.iteration = function () {
return iteration;
};
this.socket = initSocketIO(this);
});
The initSocketIO function makes callbacks to this services renderMessages() function and serviceAlarms variable gets changed on a steady basis.
Now i am trying to watch for changes in this service like so:
controllers.controller('overviewController', ['$scope', 'WebSocketService', function ($scope, WebSocketService) {
$scope.$watch(
function () {
return WebSocketService.iteration();
},
function(newValue, oldValue) {
$scope.alarms = WebSocketService.getAlarms();
},
true
);
}]);
to no avail. The second function provided to $watch never gets executed except on controller initialization.
I have tried with and without true as third parameter.
You should use $rootScope.$watch (not $scope.$watch)
I ended up using the solution below since $watch didn't work as excpected.
I refactored the solution to use $rootScope in combination with:
angularApp.run(['$rootScope', function($rootScope){
$rootScope.socket = {};
$rootScope.socket.alarms = [];
$rootScope.socket.faults = [];
$rootScope.socket.renderErrors = function(faults, socket) {
var faultArray = [];
angular.forEach(faults, function(error) {
error.value ? faultArray.push(error) : null;
});
if (! angular.equals($rootScope.socket.faults, faultArray)) {
$rootScope.socket.faults = faultArray;
$rootScope.apply();
}
};
$rootScope.socket.renderMessages = function(alarms, socket) {
if (! angular.equals($rootScope.socket.alarms, alarms)) {
$rootScope.socket.alarms = alarms;
$rootScope.$apply();
}
};
$rootScope.socket.socket = initSocketIO($rootScope.socket);
}]);
Now i have my socket-updated-model in all scopes to use freely in controllers and views.
Controller example:
$scope.acknowledgeAlarm = function(alarm) {
$scope.socket.socket.emit('acknowledgeAlarm', {
hash:alarm.icon.hash,
id:alarm.id
});
};
View example:
<div ng-repeat="alarm in socket.alarms">
{{alarm.name}} {{alarm.icon.progress}}
</div>

Angular service not passing between controllers

I have two controllers on a parallel scope level I need to pass data between:
function TableRowCtrl($scope, $http, sharedProperties) {
console.log(sharedProperties.getProperty());
$scope.items = sharedProperties.getProperty();
}
and
function SideNavCtrl($scope, $http, sharedProperties) {
$scope.customers = undefined;
var temp = "cats";
$http.get('data/customers.json').success(function(data) {
$scope.customers = data;
temp = "dogs";
sharedProperties.setProperty(temp)
});
sharedProperties.setProperty(temp);
console.log(sharedProperties.getProperty());
}
I am trying to use a service to do this (via examples I have seen) :
angular.module('myApp', []).service('sharedProperties', function() {
var property = "Cats";
return {
getProperty: function() {
return property;
},
setProperty: function(value) {
property = value;
}
};
});
However - when I try and set the data in the SideNavCtrl http success function, it does not bubble out - the service still returns 'cats' as its value. From what I have read, services are supposed to be global, and setting data in them should be permanent (as is its purpose). What am I doing wrong, and how can I get data between these two controllers on the same scope?
The problem is your TableRowCtrl saves the result of a function in its scope variable. When the service itself changes, the value in the scope does not because at that point, it's a simple property. You can either expose your service directly in the scope or wrap $scope.items in a function instead:
function TableRowCtrl($scope, $http, sharedProperties) {
$scope.items = function() { return sharedProperties.getProperty(); };
}
// And in your view
{{ items() }}
Or
function TableRowCtrl($scope, $http, sharedProperties) {
$scope.shared = sharedProperties;
}
// And in your view
{{ shared.getProperties() }}
Edit: Simple plunkr here
Edit #2:
If the problem is a binding that isn't updated because of an asynchronous process, you can use $scope.$apply:
$http.get('data/customers.json').success(function(data) {
$scope.customers = data;
temp = "dogs";
sharedProperties.setProperty(temp)
if(!$scope.$$phase)
$scope.$apply();
});
Edit 3:
I've recreated your $http.get and updated the plunkr and it works. Based on what you are showing in your questions, it should work using function instead of regular properties.
#SimomBelanger already identified the problem. I suggest using objects rather than primitives, then you don't need to call functions in your view:
<div ng-controller="TableRowCtrl">items={{items.property}}</div>
<div ng-controller="SideNavCtrl">customers={{customers}}</div>
app.service('sharedProperties', function () {
var obj = {
property: "Cats"
};
return {
getObj: function () {
return obj;
},
setObjProperty: function (value) {
obj.property = value;
}
};
});
function SideNavCtrl($scope, $timeout, sharedProperties) {
$scope.customers = undefined;
var temp = "cats";
$timeout(function () {
$scope.customers = 'some data';
temp = "dogs";
sharedProperties.setObjProperty(temp);
}, 2000);
sharedProperties.setObjProperty(temp);
}
function TableRowCtrl($scope, $http, sharedProperties) {
$scope.items = sharedProperties.getObj();
}
fiddle
In the fiddle I use $timeout to simulate an $http response.
Because getObj() returns a (reference to an) object, updates to that object are automatically picked up by the view.

Categories

Resources