How to adapt directive auto-focus to auto-focus="true"/"false"? - angularjs

I'm not much in Angularjs and use the directive that put focus on particular element. It looks as following:
appModule.directive('autoFocus', function ($timeout) {
return {
restrict: 'AC',
link: function (scope, element, attrs) {
$timeout(function () {
element[0].focus();
}, 0);
}
};
});
The usage looks as following:
<button auto-focus class="uui-button lime-green btn" ng-click="copyToClipboard()">
Copy
</button>
I'd like to rearrange directive above to have ability to write: auto-focus="true" or auto-focus="false".
UPDATE:
I've updated code as shown below, but it doesn't work that is the focus is always there regardless I write auto-focus="true" or auto-focus="false".
appModule.directive('autoFocus', function ($timeout) {
return {
restrict: 'AC',
link: function (scope, element, attrs) {
var hasFocus = attrs.autoFocus;
if (hasFocus) {
$timeout(function () {
element[0].focus();
}, 0);
}
}
};
});

You can set the property to true or false and access the value via attrs.autoFocus inside directive's link function
Edit:
appModule.directive('autoFocus', function ($timeout) {
return {
restrict: 'AC',
link: function (scope, element, attrs) {
var hasFocus = attrs.autoFocus;
if (hasFocus ==="true") {
$timeout(function () {
element[0].focus();
}, 0);
}
}
};
});

Related

How to $setDirty() form using directive in AngularJS

When the inputbox will change, then I'd like to set my form to dirty. I want to make it using directive. This is what I already did.
HTML:
<form name="myForm">
<type="text" ng-model="myModel" set-form-dirty>
</form>
Directive:
app.directive('setFormDirty', function () {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
scope.$watch(function () {
return ngModel.$modelValue;
}, function(newValue) {
console.log(newValue);
// Here I should set form to dirty. But I don't know how?
});
}
};
});
#EDIT
I need this because myModel is changing by different function. And that function won't set form to $dirty
You can watch the modelvalue on the ngModelController.
.directive('setFormDirty', function(){
return {
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
scope.$watch(function(){
return ngModel.$modelValue;
},function(newValue, oldValue){
//Check stuff here and use below code to set it to dirty
ngModel.setDirty();
});
}
};
Solved in this way:
angular.module("qs.forms").directive("setFormDirty", function () {
return {
require: ["ngModel", "^form"],
link: function (scope, element, attrs, ctrls) {
scope.$watch(function () {
return ctrls[0].$modelValue;
}, function (newValue) {
if (newValue) {
ctrls[1].$dirty = true;
}
});
}
};
});
Form could be passed by require: ["ngModel", "^form"],

Wrap Flexisel slider into Angular Directive

I try to use Flexisel with Angular but it fails to work somehow.
Here's plnkr link
var app = angular.module('angular.controls.flexSlider', [])
app.directive('flexCarousel', function () {
return {
restrict: 'E',
link: function (scope, element, attrs) {
var options = scope.$eval($(element).attr('data-options'));
console.log(options);
$(element).flexisel(options);
}
};
});
I have forked your plunk, please check the fixes there. http://plnkr.co/edit/zH4u3MwkD6HX39I8PFEr?p=preview
You need a template for your directive in first case:
var app = angular.module('angular.controls.flexSlider', [])
app.directive('flexCarousel', function () {
return {
restrict: 'E',
transclude : true,
template : "<ng-transclude></ng-transclude>",
scope : {
options : "="
},
link: function (scope, element, attrs) {
$('#flexisel').flexisel(scope.options);
}
};
});

How to use same directive multiple times with parent in AngularJS

I've got following script
// Code goes here
angular.module('default', [])
.directive('webAbc', function($log) {
return {
restrict: 'A',
controller: function($scope, $element, $attrs, $transclude) {
this.checkboxes = [];
this.updateLinkedCheckboxes = function(value) {
angular.forEach(this.checkboxes, function(checkbox, index) {
checkbox.setChecked(value);
});
};
}
};
})
.directive('webDef', function($log) {
return {
restrict: 'C',
require: '^webAbc',
link: function (scope, iElement, iAttrs, webAbc, transcludeFn) {
iElement.bind('change', function () {
webAbc.updateLinkedCheckboxes(iElement.prop('checked'));
scope.$apply();
});
}
};
})
.directive('webGhi', function($log) {
return {
restict: 'A',
require: '^webAbc',
link: function (scope, iElement, iAttrs, webAbc, transcludeFn) {
scope.setChecked = function(value) {
$log.log('This element ' + iAttrs.name + ' cheked: ' + (!value ? 'checked' : ''));
$log.log(value);
if (value)
{
iElement.attr('checked', 'checked');
}
else
{
iElement.remoteAttr('checked');
}
};
webAbc.checkboxes.push(scope);
}
};
});
it should select or deselect all checkboxes in table in marked column, but I can't make it work with following solution.
First of all it seems, that only last webGhi is visible due to print out in console. And even more, it seems, that I can't uncheck checkbox for some reason.
Link to an example: http://jsbin.com/difihabe/1/
Thank you.
Use an isolated scope in the webGhi directive or all four instances of it will push the same scope (the parent):
.directive('webGhi', function($log) {
return {
restict: 'A',
require: '^webAbc',
scope: {},
link: ...
Also instead of adding/removing the checked attribute either use:
jQuery's prop() function: iElement.prop('checked', value);
Directly setting the DOM element's checked property:
iElement[0].checked = value;
Demo: http://jsbin.com/tudotugi/2/

Angular wire up jQuery plugins after render

I have created a directive, ngAfterRender, that I am trying to use to wire up fancybox:
Template
<div ng-after-render="wireUpFancyBox($event)" ng-bind-html="Content"></div>
Directive
.directive('ngAfterRender', ['$timeout', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
$timeout(function () {
scope.$eval(attrs.ngAfterRender, { $element: element });
});
}
};
}]);
Controller
$scope.wireUpFancyBox = function ($element) {
$element.find('a').fancybox($.extend(true, {}, fancyboxOptions, {
scrolling: 'auto',
type: 'inline'
}));
};
Unfortunately, the wireUpFancyBox() method is not called when the HTML Content binding changes.
What can I do here?
If you want wireUpFancyBox called every time the ng-bind changes then you want something like this
myApp.directive('ngAfterRender', ['$timeout', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(function () {
return scope.$eval(attrs.ngBindHtml);
},
function (value) {
$timeout(function () {
scope.$eval(attrs.ngAfterRender, { $element: element });
});
});
}
};
}]);
Here we put a watch on the evaluated ng-bind-html expression and then fire off your eval on a change.
fiddle

How to expose a public API from a directive that is a reusable component?

Having a directive in angular that is a reusable component, what is the best practice to expose a public API that can be accessed from the controller?
So when there are multiple instances of the component you can have access from the controller
angular.directive('extLabel', function {
return {
scope: {
name: '#',
configObj: '='
},
link: function(scope, iElement, iAttrs) {
// this could be and exposed method
scope.changeLabel = function(newLabel) {
scope.configObj.label = newLabel;
}
}
}
});
Then when having:
<ext-label name="extlabel1" config-obj="label1"></ext-label>
<ext-label name="extlabel2" config-obj="label2"></ext-label>
<ext-label name="extlabel3" config-obj="label3"></ext-label>
How can I get the access the scope.changeLabel of extLabel2 in a controller?
Does it make sense?
Does this work for you?
angular.directive('extLabel', function() {
return {
restrict: 'E',
scope: {
api: '='
},
link: function(scope, iElement, iAttrs) {
scope.api = {
doSomething: function() { },
doMore: function() { }
};
}
};
});
From containing parent
<ext:label api="myCoolApi"></ext:label>
And in controller
$scope.myCoolApi.doSomething();
$scope.myCoolApi.doMore();
I like Andrej's and use this pattern regularly, but I would like to suggest some changes to it
angular.directive('extLabel', function {
return {
scope: {
api: '=?',
configObj: '='
},
// A controller, and not a link function. From my understanding,
// try to use the link function for things that require post link actions
// (for example DOM manipulation on the directive)
controller: ['$scope', function($scope) {
// Assign the api just once
$scope.api = {
changeLabel: changeLabel
};
function changeLabel = function(newLabel) {
$scope.configObj.label = newLabel;
}
}]
}
});
<ext-label name="extlabel1" config-obj="label1"></ext-label>
<ext-label api="label2api" name="extlabel2" config-obj="label2"></ext-label>
<ext-label name="extlabel3" config-obj="label3"></ext-label>
In controller of course label2api.changeLabel('label')
I faced this problem when writing a directive to instantiate a dygraph chart in my Angular applications. Although most of the work can be done by data-binding, some parts of the API require access to the dygraph object itself. I solved it by $emit()ing an event:
'use strict';
angular.module('dygraphs', []);
angular.module('dygraphs').directive('mrhDygraph', function ($parse, $q) {
return {
restrict: 'A',
replace: true,
scope: {data: '=', initialOptions: '#', options: '='},
link: function (scope, element, attrs) {
var dataArrived = $q.defer();
dataArrived.promise.then(function (graphData) {
scope.graph = new Dygraph(element[0], graphData, $parse(scope.initialOptions)(scope.$parent));
return graphData.length - 1;
}).then(function(lastPoint) {
scope.graph.setSelection(lastPoint);
scope.$emit('dygraphCreated', element[0].id, scope.graph);
});
var removeInitialDataWatch = scope.$watch('data', function (newValue, oldValue, scope) {
if ((newValue !== oldValue) && (newValue.length > 0)) {
dataArrived.resolve(newValue);
removeInitialDataWatch();
scope.$watch('data', function (newValue, oldValue, scope) {
if ((newValue !== oldValue) && (newValue.length > 0)) {
var selection = scope.graph.getSelection();
if (selection > 0) {
scope.graph.clearSelection(selection);
}
scope.graph.updateOptions({'file': newValue});
if ((selection >= 0) && (selection < newValue.length)) {
scope.graph.setSelection(selection);
}
}
}, true);
scope.$watch('options', function (newValue, oldValue, scope) {
if (newValue !== undefined) {
scope.graph.updateOptions(newValue);
}
}, true);
}
}, true);
}
};
});
The parameters of the dygraphCreated event include the element id as well as the dygraph object, allowing multiple dygraphs to be used within the same scope.
In my opinion, a parent shouldn't access a children scope. How would you know which one to use and which one to not use. A controller should access his own scope or his parent scopes only. It breaks the encapsulation otherwise.
If you want to change your label, all you really need to do is change the label1/label2/label3 variable value. With the data-binding enabled, it should work. Within your directive, you can $watch it if you need some logic everytime it changes.
angular.directive('extLabel', function {
return {
scope: {
name: '#',
configObj: '='
},
link: function(scope, iElement, iAttrs) {
scope.$watch("configObj", function() {
// Do whatever you need to do when it changes
});
}
}
});
Use these directives on the element that you want to go prev and next:
<carousel>
<slide>
<button class="action" carousel-next> Next </button>
<button class="action" carousel-prev> Back </button>
</slide>
</carousel>
.directive('carouselNext', function () {
return {
restrict: 'A',
scope: {},
require: ['^carousel'],
link: function (scope, element, attrs, controllers) {
var carousel = controllers[0];
function howIsNext() {
if ((carousel.indexOfSlide(carousel.currentSlide) + 1) === carousel.slides.length) {
return 0;
} else {
return carousel.indexOfSlide(carousel.currentSlide) + 1;
}
}
element.bind('click', function () {
carousel.select(carousel.slides[howIsNext()]);
});
}
};
})
.directive('carouselPrev', function () {
return {
restrict: 'A',
scope: {},
require: ['^carousel'],
link: function (scope, element, attrs, controllers) {
var carousel = controllers[0];
function howIsPrev() {
if (carousel.indexOfSlide(carousel.currentSlide) === 0) {
return carousel.slides.length;
} else {
return carousel.indexOfSlide(carousel.currentSlide) - 1;
}
}
element.bind('click', function () {
carousel.select(carousel.slides[howIsPrev()]);
});
}
};
})

Resources