Im using Angular 1.5.6.
I have a directive for checking for a double click:
angular.module('redMatter.analyse')
.directive('iosDblclick',
function () {
var DblClickInterval = 300; //milliseconds
var firstClickTime;
var waitingSecondClick = false;
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.bind('click', function (e) {
if (!waitingSecondClick) {
firstClickTime = (new Date()).getTime();
waitingSecondClick = true;
setTimeout(function () {
waitingSecondClick = false;
}, DblClickInterval);
}
else {
waitingSecondClick = false;
var time = (new Date()).getTime();
if (time - firstClickTime < DblClickInterval) {
scope.$apply(attrs.iosDblclick);
}
}
});
}
};
});
I use this here:
<div ios-dblclick="onDoubleClick($event, graph)" ></div>
graph is an object inside an ng-repeat directive. In onDoubleClick, I need access to $event:
$scope.onDoubleClick = function($event, graph){
console.log('in onDoubleClick and arguments are ', arguments);
var element = $event.srcElement;
However I'm unsure of how to pass the event from the directive to onDoubleClick. In the console log, arguments prints out as:
[undefined, Object]
Where Object is graph. How can I also pass back the event?
Since you could pass locals with $eval method, consider using it while calling attrs.iosDblclick. Internally it uses $parse API to evaluate method and uses local as a parameter.
scope.$eval(attrs.iosDblclick, {$event: e});
Plunker Demo
See also
Custom ng-enter directive not passing $event from html to the controller
UPDATED
http://jsfiddle.net/ADukg/14470/ - working example.
So, you could pass the function to directive like this:
<div ios-dblclick="onDoubleClick" ios-dblclick-arg="graf" ></div>
inside your directive:
return {
restrict: 'A',
scope: {
myCallback: '=iosDblclick',
graph: '=iosDblclickArg'
},
link: function (scope, element, attrs) {
element.bind('click', function (e) {
if (!waitingSecondClick) {
firstClickTime = (new Date()).getTime();
waitingSecondClick = true;
setTimeout(function () {
waitingSecondClick = false;
}, DblClickInterval);
}
else {
waitingSecondClick = false;
var time = (new Date()).getTime();
if (time - firstClickTime < DblClickInterval) {
scope.myCallback(e, scope.graph)
}
}
});
}
}
You can pass callback to directive as: onDoubleClick: '&' - isolate scope
webApp.directive('iosDblclick',
function () {
var DblClickInterval = 300; //milliseconds
var firstClickTime;
var waitingSecondClick = false;
return {
restrict: 'A',
scope: {
onDoubleClick: '&'
},
link: function (scope, element, attrs) {
element.bind('click', function (e) {
if (!waitingSecondClick) {
firstClickTime = (new Date()).getTime();
waitingSecondClick = true;
setTimeout(function () {
waitingSecondClick = false;
}, DblClickInterval);
}
else {
waitingSecondClick = false;
var time = (new Date()).getTime();
if (time - firstClickTime < DblClickInterval) {
scope.onDoubleClick({data: {e:e, val: "someValue"}});
}
}
});
}
};
});
Where HTML is:
<div ios-dblclick on-double-click="onDoubleClick(data)" ></div>
And controller:
$scope.onDoubleClick = function(data){
var element = data.e.srcElement;
}
Plunker Demo
Related
Hi I have a situation here,
I have created a dual input directive. Can you please help to in the below scenario.
When I change the model value via controller to undefined, the view values are not cleared. Here is the Codes,
My Dual Input Directive is as follows,
angular.module("awcQuoteBuy.directives")
.directive('dualInput', function($timeout, inputValidationService) {
return {
restrict: 'E',
templateUrl: 'app/partials/common/doubleInput.html',
scope: {
modelValue: '=',
size: '#',
fieldError: '#',
blurFn: '&loseFocus'
},
link: function postLink(scope, element, attrs, ctrl) {
scope.leftFocused = false;
scope.rightFocused = false;
scope.$watch('left', function(newVal, oldVal) {
if (newVal!==oldVal) {
var tmp = (newVal) ? inputValidationService.formatNumber(newVal+'') : '';
scope.modelValue = tmp + '|'+ scope.getOtherValue(scope.right);
}
});
scope.$watch('right', function(newVal, oldVal) {
if (newVal!==oldVal) {
var tmp = (newVal) ? inputValidationService.formatNumber(newVal+'') : '';
scope.modelValue = scope.getOtherValue(scope.left) + '|' + tmp;
}
});
scope.getOtherValue = function(value) {
return (value && value!=='') ? inputValidationService.formatNumber(value+'') : '';
};
//will set the value initially
$timeout(function() {
if (!scope.modelValue || scope.modelValue===null) {
scope.modelValue = '';
}
if (scope.modelValue!=='') {
var splitIndex = scope.modelValue.indexOf('|');
if (splitIndex>=0) {
var values = scope.modelValue.split('|');
scope.left = values[0];
scope.right = values[1];
} else {
scope.left = scope.modelValue;
}
}
});
/*
Below functions will add on-blur (lose focus) support to both fields.
*/
scope.focusLeft = function() {
scope.leftFocused = true;
};
scope.blurLeft = function() {
scope.leftFocused = false;
$timeout(function() {
if (!scope.rightFocused) {
scope.blurFn();
}
}, 100);
};
scope.focusRight = function() {
scope.rightFocused = true;
};
scope.blurRight = function() {
scope.rightFocused = false;
$timeout(function() {
if (!scope.leftFocused) {
scope.blurFn();
}
}, 100);
};
}
};
});
The HTML Piece of code is as follows,
<dual-input model-value="dualInput[$index]" ng-switch-when="DUAL_NUMBER" size="{{q.length}}"
field-error="{{q.invalid || (nextClicked && !validateGeneralQuestion(acc.memberId, q))}}" id="{{'gQDual'+$index}}"
lose-focus="saveGeneralAnswer(acc.memberId, q)"></dual-input>
In My Controller when I set the scope value to undefined or null, the entered values in the view is not cleared. Please help me here what I should do to clear this value
$scope.dualInput[$index]=undefined;
The directive itself has got the auto initialize feature. So I had to re render the directive whenever I wanted to reinitialize.
If you want to explicitly update user ctrl.$setViewvalue = ""
I am working on a directive and I am facing an issue.
Basically, in my directive I am trying to use a plugin (this: https://github.com/soundar24/roundSlider) and I am getting this error:
element.roundSlider is not a function
The code for my directive is the following:
angular.module('myPicker', []).directive('picker', [
"$timeout", function($timeout) {
var linkFunction, setColor;
setColor = function(value) {
var el, temperatureValue;
el = $('.rs-range-color');
temperatureValue = parseInt(value);
el.attr('class', '');
return el.addClass('rs-path rs-transition rs-range-color animate-color-change temp-' + temperatureValue);
};
$.fn.roundSlider.prototype.defaults.create = function() {
var endLabel, numberTag1, numberTag2, o, startLabel;
o = this.options;
startLabel = this._valueToAngle(o.min);
numberTag1 = this._addSeperator(startLabel, 'rs-tooltip-text custom-label num1 ');
numberTag1.children().html(o.min).rsRotate(-startLabel);
endLabel = this._valueToAngle(o.max);
numberTag2 = this._addSeperator(endLabel, 'rs-tooltip-text custom-label num2 ');
numberTag2.children().html(o.max).rsRotate(-endLabel);
return setColor(o.value);
};
linkFunction = function(scope, element, attrs) {
var hiddenElement;
element.roundSlider({
min: attrs.min,
max: attrs.max,
radius: attrs.size,
sliderType: 'min-range',
startAngle: 315,
circleShape: 'pie',
width: attrs.width,
value: attrs.value,
step: 0.5,
editableTooltip: false,
tooltipFormat: function(attrs) {
return attrs.value.toFixed(1) + ' °C';
}
});
if (scope.tempPicker.provvisorial === null) {
scope.tempPicker.provvisorial = attrs.value;
}
element.on('change drag', function(e) {
if (e.value !== void 0) {
return scope.$apply(function() {
return setColor(e.value);
});
}
});
element.on('stop', function(e) {
return scope.tempPicker.provvisorial = e.value;
});
element.on('change', function(e) {
return scope.tempPicker.provvisorial = e.value;
});
scope.$on('$destroy', function() {
element.roundSlider("destroy");
return element.remove();
});
hiddenElement = angular.element("#picker-hidden-text-box");
return hiddenElement.on('change', function() {
element.roundSlider("option", "value", parseInt(hiddenElement.val()));
return scope.tempPicker.provvisorial = parseInt(hiddenElement.val());
});
};
return {
restrict: 'E',
templateUrl: './directives/Picker/Picker.tpl.html',
replace: true,
link: linkFunction
};
}
]);
Now, what I don't understand is:
I am also modifying a function for roundSlider plugin $.fn.roundSlider.prototype.defaults.create = function() {... and it works here. I can see that $.fn.roundSlider is actually there and not undefined.
I really don't know why it's doing this, any idea?
I also tried to use angular.element.roundSlider(like pointed here: https://medium.com/#darilldrems/angularjs-jquery-in-angularjs-directive-96ad3d150d86#.4mc3ymoq6) but I get the same error
If you need any other piece of code, don't hesitate to ask
thanks
Try adding roundSlider javascript before making use of it
At the end, I fixed it adding
element = $(el)
element.roundSlider
it's working now
I am trying to create a reusable progress bar directive with isolate scope. This directive will have the public functions to start, stop and reset the progress bar. This directive will be used within ng-repeat
Here is the definition of directive:
chatApp.directive('jsProgress', function() {
var Stopwatch = function(options, updateCallback) {
// var timer = createTimer(),
var offset, clock, interval;
// default options
options = options || {};
options.delay = options.delay || 1;
// initialize
reset();
function start() {
if (!interval) {
offset = Date.now();
interval = setInterval(update, options.delay);
}
}
function stop() {
if (interval) {
clearInterval(interval);
interval = null;
}
}
function reset() {
clock = 0;
// render(0);
}
function update() {
clock += delta();
// render();
updateCallback();
}
function delta() {
var now = Date.now(),
d = now - offset;
offset = now;
return d;
}
// public API
this.start = start;
this.stop = stop;
this.reset = reset;
this.update = update;
};
return {
restrict : 'AE',
replace : true,
scope: { api: '=', key: '#'},
template: '<div class="dot" ng-attr-id="dots"><ul id="{{key}}" data-currentState="2"><li class="dot-red"></li><li></li><li></li><li></li></ul></div>',
link : function($scope, elem, attr) {
var timer = new Stopwatch( {delay: 5000}, updateCallback);
timer.start();
function updateCallback()
{
var currentCount;
currentCount = $(elem).find('#' + $scope.key).attr('data-currentState');
currentCount++;
$(elem).find('#' + $scope.key).attr('data-currentState',currentCount);
$(elem).find('#' + $scope.key+' li:nth-child(' + currentCount + ')').addClass('dot-red');
}
$scope.api =
{
reset: function()
{
timer.reset();
},
start: function()
{
timer.start();
},
stop: function()
{
timer.stop();
}
};
}
};
});
This is how it will be used within ng-repeat
<js-progress api="{{activeUserId}}" key="{{activeUserId}}_{{activeCompanyId}}" />
Now I want to get a particular instance of directive within ng-repeat and call its public API to start, stop and reset the particular progress bar. How can I do the same? In the above definition, it doesn't allow me to use the variable {{activeUserId}} because I want to refer each instance individually in the ng-repeat.
You are overwriting your activeUserId which is being passed from your Ctrl to your directive at at this line:
$scope.api = {};
I believe that you should keep track of your api objects in your controller in this way:
in your controller
$scope.bars = [
{
activeUserId: "id",
activeCompanyId: "companyId",
api: {} //this allows angularjs to reuse this object instance
},
{
activeUserId: "id2",
activeCompanyId: "companyId2",
api: {} //this allows angularjs to reuse this object instance
},
];
the html template for your controller
<div ng-repeat="b in bars">
<js-progress api="b.api" your-extra-params-here />
</div>
Later on in your controller, you will be able to do:
$scope.bars[0].api.start();
I would like to have button or link with icon, default glyphicon-play or glyphicon-pause if interval is enabled. How can I refactor this directive especially $element.hasClass("glyphicon-pause") or $element.removeClass("glyphicon-pause").addClass("glyphicon-play"); in more "angular way"?
<button play class="btn glyphicon glyphicon-play"></button>
Current directive:
app.directive('play', ['$interval', function ($interval) {
return {
restrict: 'A',
link: function ($scope, $element, attrs) {
var i = 0,
interval;
var play = function () {
$interval.cancel(interval);
interval = $interval(function () {
$scope.states[i].active = false;
$scope.states[i++].active = true;
i = i % 3;
}, 1000);
};
var stop = function () {
$interval.cancel(interval);
};
console.log($element, attrs);
$element.on('click', function ($event) {
if ($element.hasClass("glyphicon-pause")) {
$element.removeClass("glyphicon-pause").addClass("glyphicon-play");
stop();
} else {
$element.removeClass("glyphicon-play").addClass("glyphicon-pause");
play();
}
});
}
};
}]);
Using ng-class and ng-click would be the two most angular-like improvements here.
<button play class="btn glyphicon" ng-class="{glyphicon-play: isPlaying, glyphicon-pause: !isPlaying}" ng-click="togglePlay()"></button>
app.directive('play', ['$interval', function ($interval) {
return {
restrict: 'A',
link: function ($scope, $element, attrs) {
$scope.isPlaying = false;
var i = 0,
interval;
var play = function () {
$scope.isPlaying = true;
$interval.cancel(interval);
interval = $interval(function () {
$scope.states[i].active = false;
$scope.states[i++].active = true;
i = i % 3;
}, 1000);
};
var stop = function () {
$scope.isPlaying = false;
$interval.cancel(interval);
};
console.log($element, attrs);
$scope.togglePlay = function() {
if($scope.isPlaying){
stop();
}else{
play();
}
};
}
};
}]);
I am using infinite scroll directive and this is the code:
angApp.directive('infiniteScroll', [
'$rootScope', '$window', '$timeout', function ($rootScope, $window, $timeout) {
return {
link: function (scope, elem, attrs) {
var checkWhenEnabled, handler, scrollDistance, scrollEnabled;
$window = angular.element($window);
scrollDistance = 0;
if (attrs.infiniteScrollDistance != null) {
scope.$watch(attrs.infiniteScrollDistance, function (value) {
return scrollDistance = parseInt(value, 10);
});
}
scrollEnabled = true;
checkWhenEnabled = false;
if (attrs.infiniteScrollDisabled != null) {
scope.$watch(attrs.infiniteScrollDisabled, function (value) {
scrollEnabled = !value;
if (scrollEnabled && checkWhenEnabled) {
checkWhenEnabled = false;
return handler();
}
});
}
handler = function () {
var elementBottom, remaining, shouldScroll, windowBottom;
console.log($window);
windowBottom = $window.height() + $window.scrollTop();
elementBottom = elem.offset().top + elem.height();
remaining = elementBottom - windowBottom;
shouldScroll = remaining <= $window.height() * scrollDistance;
if (shouldScroll && scrollEnabled) {
if ($rootScope.$$phase) {
return scope.$eval(attrs.infiniteScroll);
} else {
return scope.$apply(attrs.infiniteScroll);
}
} else if (shouldScroll) {
return checkWhenEnabled = true;
}
};
$window.on('scroll', handler);
scope.$on('$destroy', function () {
return $window.off('scroll', handler);
});
return $timeout((function () {
if (attrs.infiniteScrollImmediateCheck) {
if (scope.$eval(attrs.infiniteScrollImmediateCheck)) {
return handler();
}
} else {
return handler();
}
}), 0);
}
};
}
]);
The problem with this code is sometimes it works sometimes it doesn't. If I do a hard refresh by pressing Ctrl + F5 it most certainly throws the below error.
This is the error:
I am using Firefox 29.0.1. What am I missing?
Normally this should work, but there might be some problem with requireJS. Using $() instead should ensure you are using Jquery. You might need the order plug-in for requireJS since normally RequireJS loads and evaluates scripts in an undetermined order.