How to trigger ngAnimate class sequence when using element.addClass inside directive - angularjs

Inside of a directive I'm adding various classes on an element based on user interaction.
How can I get the ngAnimate class sequence (e.g. my-class-add -> my-class-add-active) when using element.addClass in place of ngClass directive?
I want to use CSS transitions, not JS animations.
Thanks.

You need to add the class via the animate service (angularjs 1.2) like
module.directive('directive', function ($animate) {
return {
restrict:"A",
link: function($scope,$element) {
$element.on("click", function() {
$animate.addClass($element,"my-animation");
});
};
}
});
Doc

Ideally you'd delegate that to another directive which is tied in with ngAnimate (i.e. ng-show, ng-if, ng-repeat .etc.)
You can set a scope property and let another directive handle the animation classes.

Related

Render from angularjs directive via eventRender Fullcalendar

Is there a way to dynamically Render a template from angularjs directive via eventRender Fullcalendar ?
What I want to achieve is something like below:
eventRender: function(event, element, view) {
var template = '<my-directive></my-directive>';
element.find('.fc-event').append(template);
scope.$apply();
}
Following Documentation
The eventRender callback function can modify element.
For example, it can change its appearance via jQuery’s .css().
On eventRender insert the css classes with
eventRender: function (event, element) {
element.addClass(event.class)
}
Documentation on this topic is not broad in the subject.
It doesn't give any example of changing the template via Angularjs directives
and doesn't tell if there is any limitaion for template
to keep it treated by FullCalendar as event.
MyCodePen
EDITED_Code_Pen
1- add $compile to your controller
2- in eventRender make as bellow :
eventRender: function(event, element, view) {
var compiled = $compile('<div your-directive></div>')($scope);
element.find('.fc-content').replaceWith(compiled);
},
workingCodePen

Override controller functions in directive is a good idea?

I have a generic functionality implemented inside a controller. When i write a directive is it good idea to extend those controller functions inside the directive ?
Like in below implementation inside the link function.
var superCancel = scope.cancel;
// Overriding the cancel function from the controller
scope.cancel = function() {
if(element.hasClass('ng-dirty')){
element.removeClass("ng-dirty");
}
// Calling controller cancel
superCancel();
};
If your directive html is coming inside the controller in html then you can use $parent instead of rewriting
in directive:
$scope.$parent.cancel(); // only if controller coming as parent
If the controller is not coming as parent it's better to use a service or factory to implement that
Read here for more
It is better to have the directive use an attribute to set a parent scope value.
For example:
JS
app.directive("setModelApi", function() {
return {
require: "ngModel",
link: function(scope,elem,attrs, ngModelCtrl) {
scope.$eval(attrs.setModelApi, {$api: ngModelCtrl})
}
}
});
In the above example, the setModelApi directive evaluates the Angular Expression defined by the set-model-api attribute with $api exposing the ngModelController.
HTML
<input ng-model="x" set-model-api="xmodel=$api">
<button ng-click="xmodel.$setPristine()">Set Pristine</button>
The setModelApi directive sets the xmodel scope variable to the ng-model-controller API.
The button invokes the $setPristine method of the ng-model-controller API.
From the Docs:
$setPristine();
Sets the control to its pristine state.
This method can be called to remove the ng-dirty class and set the control to its pristine state (ng-pristine class). A model is considered to be pristine when the control has not been changed from when first compiled.
-- AngularJS ngModelController API Reference -- $setPristine
By using an HTML directive attribute to define the scope variable to which the API attaches, different inputs can use the directive and the connections are exposed in the HTML.
The DEMO on JSFiddle

AngularJS two directives with template on same element

I'm trying to create a context menu directive for my angular application.
I want this directive to be used for any element in the application that needs a context menu.
The problem is that in my app there are many 'user controls', that is - directives for extending buttons, inputs, grid etc. If I try to put the my-context-menu directive on any of them I get a multidir error, since both of the directives are defining templates.
The context menu directive looks something like this:
angular.module('myApp').directive('myContextMenu', function () {
var myContextMenu= {};
myContextMenu.restrict = 'A';
myContextMenu.templateUrl = 'templates/myContextMenuTemplate.html';
// Here I have scope and controller with all the functionality
return myContextMenu;
});
Another directive for example:
angular.module('myApp').directive('myGrid', function () {
var myGrid= {};
myGrid.restrict = 'E';
myGrid.templateUrl = 'templates/myGridTemplate.html';
// Here I have scope, controller and link function with all the functionality
return myGrid;
});
I want to have a context menu on the grid, and that in the grid directive I will be able to access the context menu controller.
What I tried is this: <my-grid id="grid" my-context-menu /> which failed as expected...
Do you have any solution or other idea on how to achieve this?
Thanks in advance!
My angular version: 1.3.8
You can share controllers between directives writing the same controller on the controller attribute of the directive object. And you can use transclude to embed html inside a directive with its own template. https://docs.angularjs.org/api/ng/directive/ngTransclude

css testing in angularjs

I am using controller to change the class of an object in angularjs
$scope.$watch('sideQuery',function(){
if($scope.sideQuery==""){
$(".indicator").removeClass('glyphicon-minus');
$(".indicator").addClass('glyphicon-plus');
}
else{
$(".indicator").removeClass('glyphicon-plus');
$(".indicator").addClass('glyphicon-minus');
}
});
How to test using karma? a function like
expect(scope.elem('.indicator').hasClass("glyphicon-plus")).toBe(true);
Please, do not use jQuery to toggle classes in Angular, it defeats the purpose of it. Use ng-class and apply your classes based on flags, like so:
<div class="indicator" ng-class="{'glyphicon-minus' : sideQuery != '', 'glyphicon-plus':sideQuery == ''} ></div>
Then in testing check the value of sideQuery and know that you'll have classes based off that.
If you are testing a directive, then you can compile a sample element and test the result object.
var $element;
beforeEach(inject(function ($compile) {
$element = $compile('<div data-my-directive></div>')($scope);
}));
it('should have the class "someClass"', function(){
expect($element.hasClass('someClass')).toBe(true);
});
However, be aware that tymeJV is right, you should use the ng-class directive and test your scope's values. If the scope value is right, then the class will be applied (you don't have to test the ng-class directive, that's something done in Angular's unit tests).

How can I call a method on a custom directive's isolated scope from within a transcluded controller in Angular.js

I created a directive called dt-modal under the dt module. In my main app's module called demo, I use this dt-modal which has an isolated scope. I created this directive such that the HTML form written within the directive is transcluded since I want to reuse this modal for many different forms.
<dt-modal>
<form ng-controller="ReviewFormController"
name="reviewForm"
novalidate
ng-submit="reviewForm.$valid && submitReview(review)">
<!-- form contents here -->
</form>
</dt-modal>
This transcluded form has a custom controller called ReviewFormController that listens for the submit event. How can I call the close() method on the dt-modal's scope from within submitReview() in ReviewFormController?
Here is a JSBin. If you hit ESC, you can see close() in the directive run.
http://jsbin.com/cukanole/1/edit
If this isn't possible, is there a better design for this directive?
Thanks in advance!
Since you are using an isolated scope, you could pass a control object to the directive...
<dt-modal id="review-form-modal" api="modal.api">
and add the close method to it via two-way binding:
scope: {
api: '='
},
link: function($scope, $el, attrs) {
$scope.api = {
close: function() {
$el.css({
display: 'none'
})
}
}
...
Then ng-click can use the control object to call close:
<button type="submit" ng-click="modal.api.close()">Submit</button>
If you want to try this code, here it is on Plunker.
My recommendation is to use $emit to trigger the event from the controller and use $on on the directly.
Controller
scope.$emit("ValueChanged", value);
In the directive the event will be captured using $on like:
$scope.$on("ValueChanged", function(event, ars){
... //your event has been triggered.
});
Important:
Directives should be always independent components, if inside the directive there is a call to a method from a controller(outside the directive) this will create a dependency between my directive and the controller and of course this will force one not being able to exist without the other.
If I would have to apply a design principle to a directive it will be the S in SOLID, Single responsibility principle. Directives should be able to encapsulate and work independently.

Resources