How can I refresh a angularjs directive every x seconds? - javascript

How can I refresh a angularjs directive every x seconds ? I want to reload function attached to link ever x seconds, regards.

Use an interval in your directive. But remember to destroy the interval when the directive is destroyed:
angular.directive('myDirective', myDirective);
myDirective.$inject = ['$interval'];
function myDirective($interval) {
var i = undefined;
return {
restrict: 'A',
link: function(scope) {
i = $interval(function () {
// Do stuff here.
}, seconds * 1000);
scope.$on('$destroy', function() {
console.log("destroy");
if (angular.isDefined(i)) {
$interval.cancel(i);
i = undefined;
}
});
}
};
}]);

Related

AngularJS directive changing ability to modify scope variable

Here's a plunkr with my problem: http://plnkr.co/edit/Sx830ekQyP7YBqmRB4Nd?p=preview
Click "Open", then click on "5". Notice how it changes to "test"? Now, type something into Body. It'll either say "Say a little more..." or "Now for the title". Either way, click the button again, and notice how it doesn't change to "test"? Why not? If I remove the directive, the button changes to "test" with or without text in the body.
I know this has to do with the scope in the directive, but I don't understand what exactly is wrong. Can you explain? Thanks.
angular.module('plunker', ['ngDialog']).controller('MainCtrl', function($scope, ngDialog) {
//$scope.submitPostValue = "OK";
$scope.submitPost = function() {
$scope.submitPostValue = 'test';
};
$scope.open = function () {
console.log('open');
$scope.submitPostValue = '5';
ngDialog.openConfirm({
template: 'postModal',
showClose: true,
trapFocus: false,
scope: $scope,
}).then(function (success) {
}, function (error) {
});
};
}).directive('bodyValidator', function () {
return {
require: 'ngModel',
link: function (scope, element, attr, ctrl) {
function customValidator(ngModelValue) {
if(ngModelValue.length > 0){
if(ngModelValue.length < 10) {
scope.submitPostValue = "Say a little more...";
scope.bodyValid = false;
}
else {
scope.bodyValid = true;
if(scope.titleValid)
scope.submitPostValue = "Submit";
else
scope.submitPostValue = "Now for the title..."
}
}
else {
scope.submitPostValue = "Enter a body...";
scope.bodyValid = false;
}
return ngModelValue;
}
ctrl.$parsers.push(customValidator);
}
};
});
Try to wrap all your variables into an object.
Define $scope.obj = {}; first and change all your scope.submitPostValue to $scope.obj.submitPostValue. In your HTML, change ng-value='submitPostValue' to ng-value=obj.submitPostValue.

$scope.$watch only triggered once

I've set the following watcher in my controller:
var embeds = {twitter: false, facebook: false};
$scope.$watch(embeds, function(newVal, oldVal) {
if(embeds.twitter && embeds.facebook) $scope.loading = appLoader.off();
});
This should fire when embeds changes. I have the following functions that check if all my embedded Tweets and Facebook posts have loaded for the page. When all Tweets or Facebook posts are loaded, it updates embeds within a $timeout block in order to trigger a digest cycle.
checkFBInit();
twttr.ready(function(twttr) {
twttr.events.bind('loaded', function(event) {
$timeout(function() {
embeds.twitter = true;
});
});
});
function checkFBInit() {
// Ensure FB.init has been called before attempting to subscribe to event
var fbTrys = 0;
function init() {
fbTrys++;
if (fbTrys >= 60) {
return;
} else if (typeof(FB) !== 'undefined') {
fbTrys = 60;
FB.Event.subscribe('xfbml.render', function() {
$timeout(function() {
embeds.facebook = true;
});
});
return;
} else {
init();
};
};
init();
};
The problem I'm having is that my watcher only fires once when I set it. I've tried binding embeds to $scope and/or watching embeds.twitter and embeds.facebook but the watcher only ever fires once.
Use:
$scope.embeds = {twitter: false, facebook: false};
$scope.$watch('embeds', function(newVal, oldVal) {
if ($scope.embeds.twitter && $scope.embeds.facebook) {
$scope.loading = appLoader.off();
}
}, true);
See https://docs.angularjs.org/api/ng/type/$rootScope.Scope. First argument must be string or function which return the name of param.

Angular 2 way binding in video play event doesn't work

My 2 way bidning doesn't work, it works if i call the vm.Play() function directly but when it gets called from the video play event then it doesn't work. Does anyone know why?
function VideoEventStats() {
var directive = {
restrict: "A",
replace: false,
scope: {
videoEventStats: "="
},
controller: controllerFunction,
controllerAs: "vm",
bindToController: true
};
controllerFunction.$inject = ["$element"];
function controllerFunction($element) {
var vm = this;
vm.Play = Play;
if($element.context.tagName === "VIDEO") {
angular.element($element).on('play', vm.Play);
$element.context.onended = function() {
console.log('ended..');
};
}
else {
console.warn('This element is not a video element');
}
function Play() {
vm.videoEventStats.CurrentUserHasSeen = true;
}
}
return directive;
}
Add the vm.videoEventStats.CurrentUserHasSeen = true; inside of a $timeout made the trick...
function Play() { $timeout(function() { vm.videoEventStats.CurrentUserHasSeen = true; }, 0); }

angular not updating $scope variable in controller

I have a directive to upload a file to the browser
angular.module('angularPrototypeApp')
.directive('upload', ['$parse', function ($parse) {
return {
restrict: 'A',
scope: false,
link: function(scope, ele, attrs) {
var fn = $parse(attrs.upload);
ele.on('change', function(onChangeEvent){
var reader = new FileReader();
reader.onload = function(onLoadEvent) {
scope.$apply(function(){
fn(scope, {$fileContents: onLoadEvent.target.result} );
});
}
reader.readAsText((onChangeEvent.srcElement || onChangeEvent.target).files[0]);
})
}
};
}]);
The code is taken from here:
https://veamospues.wordpress.com/2014/01/27/reading-files-with-angularjs/
The view looks like this:
<progressbar class="progress-striped active" value="dynamic" </progressbar>
<input type="file" upload="parseInputFile($fileContents)">
In the controller I do the following:
angular.module('angularPrototypeApp')
.controller('Gc2xlsxCtrl', ['$scope', 'blockUI', function ($scope, $timeout, blockUI) {
$scope.dynamic = 0;
$scope.parseInputFile = function($fileContents){
$scope.$broadcast('fileUploaded', $fileContents);
}
$scope.$on('fileUploaded', function(event, fileContents) {
if(fileContents !== undefined){
blockUI.start();
//a lot of work is done herem takes between 2 and 20 Seconds
for(var i = 1; i <= 100; i++){
$scope.dynamic += 1;
}
blockUI.stop();
}
});
}]);
My problem is that the update to $scope.dynamic is shown in the view only after the whole method has finished. The same is true for blockUI. The logs say that it's called right at the beginning of the method, but the view is never upated.
Any help on this would be fantastic!
This part:
//a lot of work is done here, takes between 2 and 20 Seconds
for(var i = 1; i <= 100; i++){
$scope.dynamic += 1;
}
Is synchronous code and won't update the view until it's done. Basically, the loop updates $scope.dynamic from 1 to 100, but the view can't change until it's done with that; so you just see it at 1, then at 100.
You need to have the work done asynchronously to allow the view to be updated while the work is happening.
How best to go about doing that is another question. You could use $q in AngularJS to do it.

AngularJS Masonry for Dynamically changing heights

I have divs that expand and contract when clicked on. The Masonry library has worked great for initializing the page. The problem I am experiencing is that with the absolute positioning in place from Masonry and the directive below, when divs expand they overlap with the divs below. I need to have the divs below the expanding div move down to deal with the expansion.
My sources are:
http://masonry.desandro.com/
and
https://github.com/passy/angular-masonry/blob/master/src/angular-masonry.js
/*!
* angular-masonry <%= pkg.version %>
* Pascal Hartig, weluse GmbH, http://weluse.de/
* License: MIT
*/
(function () {
'use strict';
angular.module('wu.masonry', [])
.controller('MasonryCtrl', function controller($scope, $element, $timeout) {
var bricks = {};
var schedule = [];
var destroyed = false;
var self = this;
var timeout = null;
this.preserveOrder = false;
this.loadImages = true;
this.scheduleMasonryOnce = function scheduleMasonryOnce() {
var args = arguments;
var found = schedule.filter(function filterFn(item) {
return item[0] === args[0];
}).length > 0;
if (!found) {
this.scheduleMasonry.apply(null, arguments);
}
};
// Make sure it's only executed once within a reasonable time-frame in
// case multiple elements are removed or added at once.
this.scheduleMasonry = function scheduleMasonry() {
if (timeout) {
$timeout.cancel(timeout);
}
schedule.push([].slice.call(arguments));
timeout = $timeout(function runMasonry() {
if (destroyed) {
return;
}
schedule.forEach(function scheduleForEach(args) {
$element.masonry.apply($element, args);
});
schedule = [];
}, 30);
};
function defaultLoaded($element) {
$element.addClass('loaded');
}
this.appendBrick = function appendBrick(element, id) {
if (destroyed) {
return;
}
function _append() {
if (Object.keys(bricks).length === 0) {
$element.masonry('resize');
}
if (bricks[id] === undefined) {
// Keep track of added elements.
bricks[id] = true;
defaultLoaded(element);
$element.masonry('appended', element, true);
}
}
function _layout() {
// I wanted to make this dynamic but ran into huuuge memory leaks
// that I couldn't fix. If you know how to dynamically add a
// callback so one could say <masonry loaded="callback($element)">
// please submit a pull request!
self.scheduleMasonryOnce('layout');
}
if (!self.loadImages){
_append();
_layout();
} else if (self.preserveOrder) {
_append();
element.imagesLoaded(_layout);
} else {
element.imagesLoaded(function imagesLoaded() {
_append();
_layout();
});
}
};
this.removeBrick = function removeBrick(id, element) {
if (destroyed) {
return;
}
delete bricks[id];
$element.masonry('remove', element);
this.scheduleMasonryOnce('layout');
};
this.destroy = function destroy() {
destroyed = true;
if ($element.data('masonry')) {
// Gently uninitialize if still present
$element.masonry('destroy');
}
$scope.$emit('masonry.destroyed');
bricks = [];
};
this.reload = function reload() {
$element.masonry();
$scope.$emit('masonry.reloaded');
};
}).directive('masonry', function masonryDirective() {
return {
restrict: 'AE',
controller: 'MasonryCtrl',
link: {
pre: function preLink(scope, element, attrs, ctrl) {
var attrOptions = scope.$eval(attrs.masonry || attrs.masonryOptions);
var options = angular.extend({
itemSelector: attrs.itemSelector || '.masonry-brick',
columnWidth: parseInt(attrs.columnWidth, 10) || attrs.columnWidth
}, attrOptions || {});
element.masonry(options);
var loadImages = scope.$eval(attrs.loadImages);
ctrl.loadImages = loadImages !== false;
var preserveOrder = scope.$eval(attrs.preserveOrder);
ctrl.preserveOrder = (preserveOrder !== false && attrs.preserveOrder !== undefined);
scope.$emit('masonry.created', element);
scope.$on('$destroy', ctrl.destroy);
}
}
};
}).directive('masonryBrick', function masonryBrickDirective() {
return {
restrict: 'AC',
require: '^masonry',
scope: true,
link: {
pre: function preLink(scope, element, attrs, ctrl) {
var id = scope.$id, index;
ctrl.appendBrick(element, id);
element.on('$destroy', function () {
ctrl.removeBrick(id, element);
});
scope.$on('masonry.reload', function () {
ctrl.scheduleMasonryOnce('reloadItems');
ctrl.scheduleMasonryOnce('layout');
});
scope.$watch('$index', function () {
if (index !== undefined && index !== scope.$index) {
ctrl.scheduleMasonryOnce('reloadItems');
ctrl.scheduleMasonryOnce('layout');
}
index = scope.$index;
});
}
}
};
});
}());
Like with many non-Angular libraries, it appears the answer lies in wrapping the library in an Angular directive.
I haven't tried it out but it appears that is what this person did
You can use angular's $emit, $broadcast, and $on functionality.
Inside your masonry directive link function:
scope.$on('$resizeMasonry', ctrl.scheduleMasonryOnce('layout'));
Inside your masonryBrick directive link function or any other child element:
scope.$emit('$resizeMasonry');
Use $emit to send an event up the scope tree and $broadcast to send an event down the scope tree.

Categories

Resources