Angularjs directive for sorting bootstrap tables - angularjs

My app have multiple bootstrap tables and i want to create a global angularjs directive for sorting these tables to use this custome directive.
I have tried few but they don't help me enough what i want.
Please find plunk below for example.
http://plnkr.co/edit/BFkWWWtuZK4x6dHiEy3I
In the above example is not sorting as the scope is not updated.

So your directive isn't manipulating the outside scope. It can't do that because it doesn't have access to the variables in the parent scope because javascript is copy by value. You can manipulate what you are passed, but you can't manipulate the value of variable that was passed to you. So angularjs wants you to put objects between you and the references you are binding between so manipulations in across isolated scopes will propagate back to the caller. So if you modify your app to use that it works.
http://plnkr.co/edit/6sqQZk
Now it's a bit messy for my tastes. I'd probably move the whole table definition into the directive, and let it handle the whole thing. It would also make it easier to setup a sorting table without all of the plumbing, but that's up to you.

Related

AngularJS - how to have a single updatable variable affect multiple, separate sections of app

I have an AngularJS app that contains multiple sets of data, created from nested ng-repeat directives. I'd like to be able to alter the state of all of these items using a navigation bar - i.e. to change a variable to see 'this week', 'last week' etc.
The current directives I have each take a variable to filter the data to the correct time frame. However I am stuck at creating a variable that can be used by these nested items and then how to update that variable.
I know that global variables should be avoided - can anybody help in terms of how I should do this correctly?
Thanks
You could use $rootScope.
Here is a really simple plunker: http://plnkr.co/edit/lp47y3WgTHQBbVnV9o7U?p=preview
BTW, $rootScope is recomended for properties that are static or constant. If not, you could use events to communicate between your directives and controllers. This is done with $on, $broadcast and $emit.
Read this for a clear understanding : http://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/
You could also use a service that is a singleton. Take a look at this Global variables in AngularJS

How to output data from a directive to a controller, and should I?

Use case
For use in a form, I created a directive that tracks changes in an array. It allows changes to be reverted and deletions and additions to be stored separately. It allows for an array (one to many mapping in the database) to be updated incrementally (rather than requiring the server to either diff, or rewrite the entire list).
Problem?
My question is about the way I expose the functionality to the controller's scope. I currently use an two-way databound attribute on the directive's scope. This works, and it seems reliable (of course you can easily break it by reassigning the scope's value, but intentionally you can break anything).
Code
You can see this plunk to see this in action. It allows methods on the directive's controller to be called from the view and the view's controller. (I am using the directive controller intentionally because that's what I do in my actual code for the directive to directive communication, but this could also just be placed in the linking function.)
Question
Is this way of doing it bad design? Am I completely throwing AngularJS out of the window now and hacking in my own code. Are there any better ways to expose functions from a directive (keep in mind that there'll be multiple of these in a single form).
It's very easy to pass in my-attribute="someFunction()" to have the directive be a consumer of the view controller. I can't find a better way to do the opposite and have the view controller consume from the directive.
Alternative?
I've been thinking about using a service here, in which the service will provide an object that is instanciated in the view, passed to the directive, and have the directive blurp out it's results to that object. Then in turn have the view controller consume the information from that service's object. Would this be a better approach?
There's nothing wrong with your approach. In fact built-in angular directives such as ng-form use this approach to store the controller in the scope (see the name property of ng-form) http://docs.angularjs.org/api/ng.directive:ngForm
For more re-usability though I would put the api methods on the controller and then put the controller itself in the api:
this.getChanges = function () {};
this.resetChanges = function(){};
$scope.api = this;
In directives, the main purpose of the controller is to serve as an api for other directives (if you didn't need an api for other directives you could just do everything in the link function). Doing it this way ensures the api is available both on the scope as well as to any directive that 'requires' the oneToMany directive.

Angular.JS — A Directive, inside a directive, inside a directive, inside a directive. Scope issues

I have some issues regarding scope in nested directives. I have two directives: column and nested-menu. Their structure can be something like:
column
nested-menu
nested-menu
nested-menu
other content
nested-menu
other content
column
other content
The column has a variable to which all the nested-menu's should react. I have tried to make this work in several ways that I found Googling, such as broadcasting events from the column directive (for some reason, the nested-menu's only saw the event when I broadcasted it from $rootScope), setting a directive controller in column and storing the variable there (I can read it, but I can't $watch it).
It's important that both directives have an isolated scope, as they're supposed to be reusable in several areas, and sometimes even nested in themselves.
I have made a simplified Plnkr of a base structure, that's not working.
http://plnkr.co/edit/1GP7SKacO777og8PysNF
Thank you!
I am not sure how you want this to behave exactly. But here is a plunker that solves the two directives interacting with each other.
The nested-menu only expects column in its parent currently, But you could change it to expect another nested-menu in its parent if you wish.
Here is the working plunker. Hope this helps.
http://plnkr.co/edit/IAn9Ib8sSkQwKx6mpsm5?p=preview

Partially apply an AngularJS directive

Say I want to create 'desktop-only' and 'mobile-only' directives that hide or show an element based on and env test. There are a dozen ways I could go about it, but a cool one would be to "partially apply" the existing 'ng-show' directive. So 'desktop-only' would be the equivalent of, and actually delegate to, 'ng-show="env.isDesktop"' but without the need to do the env test in some parent controller and put env in my scope.
Another common example would be a tabs plugin. I could write a tab directive to show and hide elements using show and hide jQuery functions, but this would lose a lot of the functionality already in the ngShow directive. Couldn't I have my tab directive extend the ngShow directive in order to get access to all that variable parsing, integration with ngAnimate, etc...
Is this possible?
Thanks
When using isolate scopes you could manually proxy some variables into the scope: $scope.current.device = $rootScope.current.device;
Appart from that, you could use the afforementioned approach to put a device model on $rootScope and have methods and fields, such as 'hasTouch' that did calls to Modernizr and similar.
So yes, this is possible.

When writing a directive in AngularJS, how do I decide if I need no new scope, a new child scope, or a new isolated scope?

I'm looking for some guidelines that one can use to help determine which type of scope to use when writing a new directive. Ideally, I'd like something similar to a flowchart that walks me through a bunch of questions and out pops the correct answer – no new new scope, new child scope, or new isolate scope – but that is likely asking for too much. Here's my current paltry set of guidelines:
Don't use an isolated scope if the element that will use the directive uses ng-model
See Can I use ng-model with isolated scope? and Why formatters does not work with isolated scope?
If the directive doesn't modify any scope/model properties, don't create a new scope
Isolate scopes seem to work well if the directive is encapsulating a set of DOM elements (the documentation says "a complex DOM structure") and the directive will be used as an element, or with no other directives on the same element.
I'm aware that using a directive with an isolated scope on an element forces all other directives on that same element to use the same (one) isolate scope, so doesn't this severely limit when an isolate scope can be used?
I am hoping that some from the Angular-UI team (or others that have written many directives) can share their experiences.
Please don't add an answer that simply says "use an isolated scope for reusable components".
What a great question! I'd love to hear what others have to say, but here are the guidelines I use.
The high-altitude premise: scope is used as the "glue" that we use to communicate between the parent controller, the directive, and the directive template.
Parent Scope: scope: false, so no new scope at all
I don't use this very often, but as #MarkRajcok said, if the directive doesn't access any scope variables (and obviously doesn't set any!) then this is just fine as far as I am concerned. This is also helpful for child directives that are only used in the context of the parent directive (though there are always exceptions to this) and that don't have a template. Basically anything with a template doesn't belong sharing a scope, because you are inherently exposing that scope for access and manipulation (but I'm sure there are exceptions to this rule).
As an example, I recently created a directive that draws a (static) vector graphic using an SVG library I'm in the process of writing. It $observes two attributes (width and height) and uses those in its calculations, but it neither sets nor reads any scope variables and has no template. This is a good use case for not creating another scope; we don't need one, so why bother?
But in another SVG directive, however, I required a set of data to use and additionally had to store a tiny bit of state. In this case, using the parent scope would be irresponsible (again, generally speaking). So instead...
Child Scope: scope: true
Directives with a child scope are context-aware and are intended to interact with the current scope.
Obviously, a key advantage of this over an isolate scope is that the user is free to use interpolation on any attributes they want; e.g. using class="item-type-{{item.type}}" on a directive with an isolate scope will not work by default, but works fine on one with a child scope because whatever is interpolated can still by default be found in the parent scope. Also, the directive itself can safely evaluate attributes and expressions in the context of its own scope without worrying about pollution in or damage to the parent.
For example, a tooltip is something that just gets added; an isolate scope wouldn't work (by default, see below) because it is expected that we will use other directives or interpolated attributes here. The tooltip is just an enhancement. But the tooltip also needs to set some things on the scope to use with a sub-directive and/or template and obviously to manage its own state, so it would be quite bad indeed to use the parent scope. We are either polluting it or damaging it, and neither is bueno.
I find myself using child scopes more often than isolate or parent scopes.
Isolate scope: scope: {}
This is for reusable components. :-)
But seriously, I think of "reusable components" as "self-contained components". The intent is that they are to be used for a specific purpose, so combining them with other directives or adding other interpolated attributes to the DOM node inherently doesn't make sense.
To be more specific, anything needed for this standalone functionality is provided through specified attributes evaluated in the context of the parent scope; they are either one-way strings ('#'), one-way expressions ('&'), or two-way variable bindings ('=').
On self-contained components, it doesn't make sense to need to apply other directives or attributes on it because it exists by itself. Its style is governed by its own template (if necessary) and can have the appropriate content transcluded (if necessary). It's standalone, so we put it in an isolate scope also to say: "Don't mess with this. I'm giving you a defined API through these few attributes."
A good best practice is to exclude as much template-based stuff from the directive link and controller functions as possible. This provides another "API-like" configuration point: the user of the directive can simply replace the template! The functionality all stayed the same, and its internal API was never touched, but we can mess with styling and DOM implementation as much as we need to. ui/bootstrap is a great example of how to do this well because Peter & Pawel are awesome.
Isolate scopes are also great for use with transclusion. Take tabs; they are not only the whole functionality, but whatever is inside of it can be evaluated freely from within the parent scope while leaving the tabs (and panes) to do whatever they want. The tabs clearly have their own state, which belongs on the scope (to interact with the template), but that state has nothing to do with the context in which it was used - it's entirely internal to what makes a tab directive a tab directive. Further, it doesn't make much sense to use any other directives with the tabs. They're tabs - and we already got that functionality!
Surround it with more functionality or transclude more functionality, but the directive is what it is already.
All that said, I should note that there are ways around some of the limitations (i.e. features) of an isolate scope, as #ProLoser hinted at in his answer. For example, in the child scope section, I mentioned interpolation on non-directive attributes breaking when using an isolate scope (by default). But the user could, for example, simply use class="item-type-{{$parent.item.type}}" and it would once again work. So if there is a compelling reason to use an isolate scope over a child scope but you're worried about some of these limitations, know that you can work around virtually all of them if you need to.
Summary
Directives with no new scope are read-only; they're completely trusted (i.e. internal to the app) and they don't touch jack. Directives with a child scope add functionality, but they are not the only functionality. Lastly, isolate scopes are for directives that are the entire goal; they are standalone, so it's okay (and most "correct") to let them go rogue.
I wanted to get my initial thoughts out, but as I think of more things, I'll update this. But holy crap - this is long for an SO answer...
PS: Totally tangential, but since we're talking about scopes, I prefer to say "prototypical" whereas others prefer "prototypal", which seems to be more accurate but just rolls off the tongue not at all well. :-)
My personal policy and experience:
Isolated: a private sandbox
I want to create a lot of scope methods and variables that are ONLY used by my directive and are never seen or directly accessed by the user. I want to whitelist what scope data is available to me. I can use transclusion to allow the user to jump back in at the parent scope (unaffected). I do NOT want my variables and methods accessible in transcluded children.
Child: a subsection of content
I want to create scope methods and variables that CAN be accessed by the user, but are not relevant to surrounding scopes (siblings and parents) outside the context of my directive. I also would like to let ALL parent scope data to trickle down transparently.
None: simple, read-only directives
I don't really need to mess with scope methods or variables. I'm probably doing something that doesn't have to do with scopes (such as displaying simple jQuery plugins, validation, etc).
Notes
You should not let ngModel or other things directly impact your decision. You can circumvent odd behavior by doing things like ng-model=$parent.myVal (child) or ngModel: '=' (isolate).
Isolate + transclude will restore all normal behavior to sibling directives and returns to the parent scope, so don't let that affect your judgement either.
Don't mess with the scope on none because it's like putting data on scope for the bottom half of the DOM but not the top half which makes 0 sense.
Pay attention to directive priorities (don't have concrete examples of how this can affect things)
Inject services or use controllers to communicate across directives with any scope type. You can also do require: '^ngModel' to look in parent elements.
After writing a lot of directives, I've decided to use less isolated scope. Even though it is cool and you encapsulate the data and be sure not to leak data to the parent scope, it severely limits the amount of directives you can use together. So,
If the directive you're going to write is going to behave entirely on its own and you are not going to share it with other directives, go for isolated scope. (like a component you can just plug it in, with not much customization for the end developer) (it gets very trickier when you try to write sub-elements which have directives within)
If the directive you're going to write is going to just make dom manipulations which has needs no internal state of scope, or explicit scope alterations (mostly very simple things); go for no new scope. (such as ngShow, ngMouseHover, ngClick, ngRepeat)
If the directive you're going to write needs to change some elements in parent scope, but also needs to handle some internal state, go for new child scope. (such as ngController)
Be sure to check out the source code for directives: https://github.com/angular/angular.js/tree/master/src/ng/directive
It greatly helps on how to think about them
Just thought I'd add my current understanding and how it relates to other JS concepts.
Default (e.g. not declared or scope: false)
This is philosophically equivalent to using global variables. Your directive can access everything in the parent controller but it is also affecting them and being affected at the same time.
scope:{}
This is like a module, anything it wants to use needs to be passed in explicitly. If EVERY directive you use is an isolate scope it can be the equivalent of making EVERY JS file you write its own module with a lot of overhead in injecting all the dependencies.
scope: child
This is the middle ground between global variables and explicit passthrough. It's similar to javascript's prototype chain and just extends you a copy of the parent scope. If you create an isolate scope and pass in every attribute and function of the parent scope it is functionally equivalent to this.
The key is that ANY directive can be written ANY way. The different scope declarations are just there to help you organize. You can make everything a module, or you can just use all global variables and be very careful. For ease of maintenance though it's preferable to modularalize your logic into logically coherent parts.There is a balance between an open meadow and a closed jail-house. The reason this is tricky I believe is that when people learn about this they think they are learning about how directives work but actually they are learning about code/logic organization.
Another thing that helped me figure out how directives work is learning about ngInclude. ngInclude helps you include html partials. When I first started using directives I found that you could use it's template option to reduce your code but I wasn't really attaching any logic.
Of course between angular's directives and the work of the angular-ui team I haven't yet had to create my own directive that does anything substantial so my view on this may be completely wrong.
I concur with Umur. In theory, isolated scopes sound wonderful and "portable," but in building my app to involve non-trivial functionality I came across the need to incorporate several directives (some nested inside others or adding attributes to them) in order to fully write in my own HTML, which is the purpose of a Domain Specific Language.
In the end, it's too weird to have to pass every global or shared value down the chain with multiple attributes on each DOM invocation of a directive (as is required with isolate scope). It just looks dumb to repeatedly write all that in the DOM and it feels inefficient, even if these are shared objects. It also unnecessarily complicates the directive declarations. The workaround of using $parent to "reach up" and grab values from the directive HTML seems like Very Bad Form.
I, too, wound up changing my app to have mostly child scope directives with very few isolates -- only those which don't need to access ANYTHING from the parent other than what they can be passed through simple, non-repetitive attributes.
Having dreamed the dream of Domain Specific Languages for decades before there was such a thing, I'm elated that AngularJS provides this option and I know that, as more devs work in this area, we're going to see some very cool apps that are also easy for their architects to write, expand, and debug.
-- D

Resources