AngularJS how to write functions in directive - javascript

For me AngularJS directives looks like wrapped functionality, like some web component. I am trying to wrap progress bar from Bootstrap i.e.:
I made basic directive thats fine, but I would like to add some API to that component. Some functions which I can use in controller that could control component behaviour. E.g. start, stop, reset, gotopercent etc. I made that functions at controller but I think it is not best practice since that functions should belong to directive, not a controller so I can easily reuse it in other pages.
Question:
How to create functions in AngularJS directive which allows me to control component behavior.

I made that functions at controller but I think it is not best practice since that functions should belong to directive, not a controller so I can easily reuse it in other pages.
Well, no. Functions do belong into controllers - but directives can have their own controller where you define your "directive API" independent from you ApplicationController or PageController.
Take a look at the angular-ui/bootstrap repo, where there's already a directive for the bootstrap progressbar.

Related

Is ng-init truely a bad way set variable in view without controller

I am using ui-select to create a complex page with multuple views. For those that don't know each view is loaded via ajax when a user selects a tab (or in my case a dropdown). Views traditionally have their own controller and html templet. Views inherrit the scope of the parent page they are on, but have their own child scope.
In my case some of our views have no controller at all. They are so simple that we only have html pages for them. The problem is that I would like them to be able to toggle a boolean on a configuration object on the parent page's scope, to turn off on on some control fields on the parent page. Everything already exists, multiple pages use these views, I simply want to expand them to toggle something on the parent.
I could do this in three ways. I could in my ui-router use the resolve method to explicitly toggle the field I want when the view is loaded, but then I have to do this in each ui-router for each page explicitly; no cool code-reuse.
I could have my ui-router load a controller, where currently I have no controller, and the controller could do this. However, this means manually adding a controller that is mostly unneeded everywhere I use the view, and again doesn't feel like code reuse.
Or I could use ng-init to have my view's explicitly set the value I want at load time. To me this feels cleanest and easiest, since I don't already have a controller.
However, ng-init is frowned on, and should only be used for aliasing supposedly. Is there a cleaner approach then using the ng-init? is it really bad to have a view without a controller (to be more exact they have the parent controller still, but that isn't reloaded when a new view is opened).
The fact that you don't specify controller for the route doesn't change anything. The view has its own scope, so it can be manipulated with either route controller or with directives that don't have their own scope (e.g. ngInit).
The objective of well-known remark on ngInit
The only appropriate use of ngInit is for aliasing special properties
of ngRepeat, as seen in the demo below. Besides this case, you should
use controllers rather than ngInit to initialize values on a scope
is to keep you from 'html programming'. And it doesn't have too much sense because the separation of concerns is not among Angular's virtues. Angular's habit of evaluating the code from html attributes and the way how it is used by core directives prompt you to defy it even further.
Just dont init that variables at all. ng-show="smth", ng-if="smth" works ok if 'smth' is not defined and later u change it using ng-click="smth = !smth".

Angularjs - create globally accessible (accessible in templates too) function in a module

We want to create a reusable library/module for our ACL functionality. Basically, to check if the logged-in user has access to the particular UI resource and also has permissions to perform actions like click etc... So, we want to expose a hasAccess() function which can be called from inside directives like - ng-show, ng-hide, ng-disabled etc...
We explored few options like angular-acl (https://github.com/mikemclin/angular-acl). With this solution, in every controller, we need to add a line like this: $scope.can = AclService.can; which is bit pain, given that we are having many controllers in our application.
So, was exploring on what is the best way this can be achieved? Is there a way we can export global functions from inside our module service, so that the same is accessible in every template?
Some of the solutions we found was to write code inside module.run() method, and set $rootScope.fn = . But, can we write a run() for a module that is being shared as a library? Is this valid, and if yes, then, when exactly will this run() be called is not clear.
Any idea, what is the best practice solution for such a scenario, where we want to expose a global method/function from inside a service, such that the same can be accessed in templates?

AngularJS : How can I avoid tightly coupling stacked directives when both need values from parent scope?

I'm trying to wrap a 3rd party (non-Angular) library in a set of Angular directives for use in my application. So far I've got a base control that I wrapped in a directive (basically on link it just replaces a div in the template with the 3rd party control). I wanted to setup this directive to be re-used across my app as I'll need the control multiple times. This seemed to call for an Isolate scope and to setup bindings on my views to avoid tight coupling. This works for the base functionality, but in some cases I need to extend the functionality of that control.
My thought was to create a second directive that leveraged different parts of the 3rd party library to add functionality to the base control. This directive would need to communicate with the 3rd party control in the first directive, as well as bind to values on the parent view. I setup both directives as restrict: 'A' to make them attributes, then I stack them on a single div in my view -
<div directive-base directive-add-on />
The problem here is communicating between all the different pieces. If the base directive is an isolate scope, the add-on can't be isolate and thus can't communicate with the parent view. If I make them both child scope I can see everything on the parent scope, but then I'm more tightly coupling my directives to my view's controller.
Is there a different approach to doing this that avoids the tight coupling?
Can you treat the add-ons as attribute arguments to the directive-base?
Normally, I'd have a directive on an element like:
<directive-base add-on="true_or_scope_variable"></directive-base>
In your directive link function or associated controller, you can adjust the content based on the attribute values.
Does this work?

Calling into or between controllers in AngularJS

I have some fairly complicated logic in a bootstrap dialog which I've moved into its own controller for isolation.
There are times I want to launch the dialog or call a function in the controller based on some logic that occurs elsewhere in the app - in another controller, or a service. I've achieved this by adding an id to the ng-controller element then looking up the element by that id, and calling things off the .scope() of that controller. Essentially this:
In html:
<div id="modalController" ng-controller="modalController">
And in another service or controller:
angular.element("#modalController").scope().somefunction()
This seems pretty weird that I can't just get a controller by name. Is there a way to do this?
Create a service and bind the model to data maintained in that service. Make a change to the model within the service and it's made everywhere.
You could also create a service that provides a pubsub interface to the changes you need to make.
Yet another way to do it would be to have a single model representing the state of your system and modify that. Attach the relevant parts of that model to the scopes of each widget as necessary and you have a communication device built in.
It sounds like you are making a change in one place that should cause a change in another place. If that's the case, I'd argue having a service that updates all parts of the model correctly is the best way to go. Always imagine what you'd do if you added another widget that hangs off this functionality.

Communication between directives (and other parts) in AngularJS

I'm looking for guidelines about when and why should I use the "require" option in the directive definition,
why not communicate just using the Scope - like most of the times in Angular ?
How is it, that suddenly in directives, I ask for the controller itself and not just attaching things to the scope(s) ?
Generally speaking - there are many ways to communicate between directives/controllers/scopes in Angular -
Scope inheritance.
RootScope "emits".
Services (factory/service/provider).
Requiring controllers in directives.
Requiring new Scope/ isolate scope / "normal" scope in directives.
More?
And while I understand how they work technically, it's not clear to me what are the guidelines to decide which one to use and why.
Will be happy for some general/high level guidelines. Thanks.
require is particularly useful if you want to create custom form controls (see section Implementing custom from controls) -- i.e., you want to create your own form control with a directive. You can require the ngModelController to get access a lot of existing functionality via its API/functions.
Another use case is found on the AngularJS home page, section Create Components, where the pane directive uses require: '^tabs' to get access to the tabs controller. Since both of these components/directives create isolate scopes, scope inheritance is not an option. A service wouldn't be a good fit either, since your app could have multiple tabs directives. So, in this case, the controller is used as a means for the pane directive to be able to affect the tabs scope -- to be able to get at the scope. Without using require, the pane directive can't get at the tabs scope. I discuss this in more detail (with a picture) in this SO answer: 'this' vs $scope in AngularJS controllers
require can only be used if the controller-of-interest is defined on the same element (e.g., ngModelController), or if there is a hierarchy (e.g., pane -> tab).

Categories