Move function from controller to directive - angularjs

I have a function in my controller that manipulates the DOM. I understand this to be a bad practice and DOM manipulations should be moved into a directive. I'm having trouble pulling it out of the controller and into its own directive.
I have the following example code in my controller:
$scope.sidebarToggle = function() {
if ($scope.request = null) {
$(#container).switchClass('bottom', 'top', 400, 'linear');
$scope.editing = true;
}
else {
$(#container).switchClass('top', 'bottom', 400 'linear');
$scope.editing = false;
{
};
The above code's if conditions are very simplified, in the live code there are multiple conditions. Otherwise an ng-show/hide directive might have been possible.
The purpose of the code is to recognize the state the user is in, reveal/hide an off-screen sidebar (the class assignments), and set the 'editing' state of the controller.
How can this be refactored into a directive to accomplish the same goal?

Take a look at the documentation for angular directives to get started.
https://docs.angularjs.org/guide/directive
For 'angularising' the class of the container, use ng-class.
example # Adding multiple class using ng-class

You probably don't need to create your own directive for that.
Angular have already created some directives that could help you out.
In your case, you should use angular directive : ng-show and ng-class or ng-style
Exemple :
HTML
<div ng-show="request == null"> Edit </div>
<div ng-class="{'class-top': request == null,'class-bottom' : request != null}"> <div>
CSS :
.class-top{
...
}
.class-bottom{
...
}
Let me know if it works for you,
Nico

Try this:
app.directive('test', function() {
return {
restrict: 'E',
scope: {
},
link: sidebarToggle
};
});
I think it's creating a directive and link to your function sidebarToggle

Related

Run 'ng-click' inside a directive's isolated scope

Thanks in advance for taking the time to look into this question
I have serverside generated code that renders a directive wrapped around pre-rendered content.
<serverside-demo color="blue">
<p><strong>Content from Server, wrapped in a directive.</strong></p>
<p>I want this color to show: <span ng-style="{color: color}">{{color}}</span></p>
<button ng-click="onClickButtonInDirective()">Click Me</button>
</serverside-demo>
This means that 1.) the directive tag, 2.) the content inside the directive tag, 3.)the ng-click and 4.) The curly braces syntax are all generated by the server.
I want AngularJs to pick up the generated code, recompile the template and deal with the scope.
The problem is that I am having trouble getting it working. I understand that because the ng-click is inside the controller block, it is picked up not by the directive isolated scope, but the parent controllers. Instead I want the opposite... to pick up the onClickButtonInDirective scope function inside the serversideDemo link
I have created a jsfiddle best explaining my problem, which aims to clearly demonstrate the working "traditional" way of loading the template separately (which works) comparing it to the server-side way.
https://jsfiddle.net/stevewbrown/beLccjd2/3/
What is the best way to do this?
Thank you!
There are two major problem in your code
1- directive name and dom element not matched, - missing in dom element
app.directive('serverSideDemo', function() {
use <server-side-demo color="blue"> instead of <serverside-demo color="blue">
2- you need to compile the html code of server-side-demo dom with directive scope in link function
$compile(element.contents())(scope);
Working jsfiddle
Use templateUrl instead of template to fetch the content of directive from server:
app.directive('serverSideDemo', function() {
return {
restrict: 'AE',
scope: {
color: '='
},
templateUrl: 'link/that/returns/html/content',
link: function(scope, element, attrs) {
scope.onClickButtonInDirective = function() {
console.log('You clicked the button in the serverside demo')
scope.color = scope.color === 'blue' ? 'red' : 'blue';
}
}
};
});
Have a look at angular docs for more details

AngularJS : directives which take a template through a configuration object, and show that template multiple times

I'm looking to create a custom directive that will take a template as a property of a configuration object, and show that template a given number of times surrounded by a header and footer. What's the best approach to create such a directive?
The directive would receive the configuration object as a scope option:
var app = angular.module('app');
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
config: '=?'
}
...
}
}
This object (called config) is passed optionally to the directive using two way binding, as show in the code above. The configuration object can include a template and a number indicating the number of times the directive should show the template. Consider, for example, the following config object:
var config = {
times: 3,
template: '<div>my template</div>'
};
It would, when passed to the directive, cause the directive to show the template five times (using an ng-repeat.) The directive also shows a header and a footer above and below the template(s):
<div>the header</div>
<div>my template</div>
<div>my template</div>
<div>my template</div>
<div>the footer</div>
What's the best way to implement this directive? Note: When you reply, please provide a working example in a code playground such as Plunker, as I've run into problems with each possible implementation I've explored.
Update, the solutions I've explored include:
The use of the directive's link function to append the head, template with ng-repeat, and footer. This suffers from the problem of the template not being repeated, for some unknown reason, and the whole solutions seems like a hack.
The insertion of the template from the configuration object into middle of the template of the directive itself. This proves difficult because jqLite seems to have removed all notion of a CSS selector from its jQuery-based API, leading me to wonder if this solution is "the Angular way."
The use of the compile function to build out the template. This seems right to me, but I don't know if it will work.
You could indeed use ng-repeat but within your directive template rather than manually in the link (as that wouldn't be compiled, hence not repeated).
One question you didn't answer is, should this repeated template be compiled and linked by Angular, or is it going to be static HTML only?
.directive('myDirective', function () {
return {
restrict: 'E',
scope: {
config: '=?'
},
templateUrl: 'myTemplate',
link: function(scope) {
scope.array = new Array(config.times);
}
}
}
With myTemplate being:
<header>...</header>
<div ng-repeat="item in array" ng-bind-html="config.template"></div>
<footer>...</footer>
I'd think to use ng-transclude in this case, because the header & footer wrapper will be provided by the directive the inner content should change on basis of condition.
Markup
<my-directive>
<div ng-repeat="item in ['1','2','3']" ng-bind-html="config.template| trustedhtml"><div>
</my-directive>
Directive
var app = angular.module('app');
app.directive('myDirective', function($sce) {
return {
restrict: 'E',
transclude: true,
template: '<div>the header</div>'+
'<ng-transclude></ng-transclude>'+
'<div>the footer</div>',
scope: {
config: '=?'
}
.....
}
}
Filter
app.filter('trustedhtml', function($sce){
return function(val){
return $sce.trustedHtml(val);
}
})

AngularJS - ng-if - show when body has a class

I want to show/hide an element using ng-if
e.g. I would like to show an element if body has got specific class
I've tried the following, but no success - is this even valid expression?
<div ng-if="document.querySelector('body').className.indexOf('bodyHasThisClass') >= 0"></div
Any suggestions much appreciated. Here is a plunk example: http://plnkr.co/edit/kzXXg0sne8nyQZuufao7?p=preview
From AngularJS docs: https://docs.angularjs.org/guide/expression
Angular expressions do not have access to global variables like window, document or location. This restriction is intentional. It prevents accidental access to the global state – a common source of subtle bugs.
How about adding a controller to your directive
app.directive('wrapperDirective', function() {
return {
restrict: 'A',
templateUrl: 'wrapper-directive.html',
replace: true,
transclude: true,
controller: function($scope){
$scope.a = (document.querySelector('body').className.indexOf('bodyHasThisClass')>= 0)? true : false;
$scope.b = (document.querySelector('body').className.indexOf('noSuchClass')===-1)? true:false;
}
};
});
And in template:
<div ng-if="a==true">
visible if body has class bodyHasThisClass
</div>
<div ng-if="b==false">
this should be hidden
</div>

Angular - condition, transclude

I've written a sample directive with a conditional content (component.html):
<div class="panel panel-default">
<div class="panel-heading">{{title}}</div>
<div class="panel-body">
<p ng-show="loadingVisible()" class="text-center">Loading...</p>
<div ng-show="!loadingVisible()" ng-transclude></div>
</div>
Directive code (component.js):
app.directive('component', function () {
return {
restrict : 'A',
transclude : true,
replace : true,
templateUrl : 'component.html',
require : '^title',
scope : {
title : '#',
loadingVisible : '&'
},
controller : [ '$scope', function ($scope) {
if (!$scope.loadingVisible) {
$scope.loadingVisible = function () {
return false;
};
}
} ]
};
});
The main use of this directive is something like this (sample.html):
<div component title="Title">
<div id="t"></div>
</div>
And the controller code (sample.js):
app.directive('sample', function () {
return {
restrict: 'A',
templateUrl: 'sample.html',
controller: [ '$scope', function ($scope) {
$('#id');
} ]
};
});
0
The problem is that the div aquired by using jQuery selector isn't visible. I guess it's because the loadingVisible method (conditional content) hides that div in the construction phase. So when the sample directive tries to get it it fails. Am I doing something wrong? What's the coorect resolution of this problem? Or maybe my knowledge of directives is wrong?
I'll appreciate any help :)
if you're trying to interact with the DOM (or the directive element itself), you'll want to define a link function. The link function gets fired after angular compiles your directive and gives you access to the directives scope, the element itself, and any attributes on the directive. so, something like:
link: function (scope, elem, attrs) {
/* interact with elem and/or scope here */
}
I'm still a little unclear about what your directive is trying to accomplish though, so it's tough to provide much more help. Any additional details?
so if you want to ensure that a title is specified, you can just check for the presence of the title scope var when the directive gets linked, then throw an error if it's not there.
also, is there any reason loadingVisible needs to be an expression? (using '&' syntax). If you just need to show/hide content based on this value, you could just do a normal one-way '#' binding. so overall, something like:
app.directive('component', function () {
return {
restrict : 'A',
transclude : true,
replace : true,
templateUrl : 'component.html',
scope : {
title : '#',
loadingVisible : '#'
},
link : function (scope, elem, attrs) {
if (!scope.title) {
throw 'must specify a title!';
}
if (!attrs.loadingVisible) {
scope.loadingVisible = false;
}
}
};
});
If you need to get access to any of your transcluded content, you can use the elem value that gets injected into your link function, like so:
elem.find('#a');
an (updated) working plnkr: http://embed.plnkr.co/JVZjQAG8gGhcV2tz1ImK/preview
The problem is that directive structure is like this:
<div component>
<div id="a"></div>
</div>
The directive is used somewhere like this:
asd
The test directive uses a "a" element in its controller (or link) function. The problem is that the test controller code is run before the div is transcluded and it cannot see the content :/ A simple workaround is that the component should be before the test directive. Do you have any other solutions to this problem?

How to use addClass method in angularjs

I have an angularjs directive restricted to class. How can I add this by using addClass method in angularjs
directive
app.directive('number', function() {
return {
restrict: 'C',
link: function () {
}
};
});
code
<div id="test"></div>
I know it is possible by using jQuery like the following.
$( "#test" ).addClass( "number" );
Please help me in doing it by using angularjs
You don't need to use .addClass() angular provide ng-class directive to add class conditionally.
ng-class="{number:[condition]}"
it will add class number whenever your condition return true
yes but your directive will not be used by this I misunderstood your question previously.
You should add ng-class="yourClass" to element on view and add in your controller
$scope.yourClass = "your-new-class", not in directive
try this
link: function (lEmel) {
lElem.addClass("Foo");
}

Resources