AngularJS: directives vs. controllers - What logic to put where? [closed] - javascript

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I'm quite new to angular and try to really learn how to organize my code, so future coworkers will be able to find their way around it quickly.
One rule I know is "If it manipulates the DOM, put it into a directive", which I abide by.
But still there are times where I am unsure where to put my methods, as I can put them into the main app controller, into a controller supplied as the "controller" option within the directive or even within the function that inits the directive (option "link").
With filters and services it's pretty clear to me, but with controllers and directives the line becomes pretty blurry.
I already realized that even with a little app I spread some of the code here and there and it's already confusing, even to myself. So I'd like to get some Tipps on organizing my code better.
So I guess my main question is:
1) Is there a good rule of thumb to know what code to put where?
Or if this is too abstract here are some examples:
2) I have a directive with a template which I only use within my app. Something should happen, when I click on the element. I already know its preferable to use the ng-click directive over binding a click event within the linked function.
But where should I define the method supplied in ng-click?
A) The main controller of the app.
B) The "link" function of the directive.
C) Add a controller to the directive (using the "controller" option) and define it there.
3) Would the answer to 2) be different if I plan to reuse the directive elsewhere?
4) Different Scenario:
I have a Button and when clicked and dragged it should move a completely unrelated Element.
Should I...
A) Create one directive and influence template & behavior based on a passed attribute?
B) Create two directives (one for the handle, one for the target Element)
If so, it again poses the question of where to put the methods to handle the dragging?
Notes:
I am aware the answers might be a little dependent on personal opinion, but I kinda hope there are some "rules" or "right ways to do it" to which I can abide by for future development.
I didn't include any code for conciseness reasons.
Should it be needed for an answer I'd be happy to provide it.
thank you for your time.

First of all, great question. I think every new-with-angular developer struggles with the differences with all the given components (controller, directive, service, filter etc.).
Let's start with the basic formal definition:
Directives are markers on a DOM element that tell AngularJS's HTML compiler to attach a specified behavior to that DOM element.
And on the other hand
Controller is a JavaScript constructor function that is used to augment the Angular Scope
The defined behavior does guide us through some rule-of-thumbs.
So for your questions above:
In simple words, we user controllers to manage an area (scope) in
The HTML template with all the great abilities a controller brings
(two-way-binding, scoped behavior, services injections etc.) And we
Use directives when we wish to manipulate an existing HTML element or
Custom out own, in most scenarios - when we think about reusing
This elements.
That depends on the context of what ng-click should do. Lets say you have your customized directive for a numeric input that has a customized designed and behavior as you defined in your directive configuration. And you use it in a form that ng-click suppose to pop a modal with optional values and use it in a different place in the application and ng-click will do something else. In this case the function need to be a scope.fucntion. but let's say both location and every other will do exactly the same, this take the function to the directive scope.
Answered above :)
Each of your options will do, this where "opinion" takes in and less rule-of-thumbs exists. why? because both ways will work when each has it pros and cons. The rule of thumb I can find in the scenario is that if both elements are part of the directive template, I would expect the 'behavious' (the dragging function) to be part of the directive scope.
Good luck

1) Is there a good rule of thumb to know what code to put where?
Lots of things at play here; and I'm not sure that there is a 'directives vs controllers' battle going on. They seem different enough to me. Directives can have their own controllers, if you didn't know.
I view directives as a single, specific, set of encapsulated code including the HTML (View) and JavaScript code (Controller). I use directives when I want to reuse something as an 'encapsulated' component.
If I just have a bit of JavaScript code that I want to reuse; I'll put in an AngularJS service which I see as just a collection of JavaScript code without any HTML.
I have a directive with a template which I only use within my app.
Something should happen, when I click on the element. I already know
its preferable to use the ng-click directive over binding a click
event within the linked function. But where should I define the method
supplied in ng-click?
I would define the handler as part of the directive's isolated scope; something like this:
scope:
{
onButtonClick: '&onButtonClick'
}
Define default behavior as part of the Directive's link or controller.
link: function ( $scope, element, attrs ) {
$scope.myDefaultButtonClick = function(){
// do stuff
}
this.onInit = function(){
if(!$scope.onButtonClick){
$scope.onButtonClick = $scope.myDefaultButtonClick;
}
}
this.onInit();
}
In your JavaScript; you can call the function that is passed in as an argument:
$scope.onButtonClick();
And you can do the same in your HTML template.
<img src="button.png" ng-click="onButtonClick()">
3) Would the answer to 2) be different if I plan to reuse the
directive elsewhere?
If I do not plan for reuse; I would probably not use a directive.
I have a Button and when clicked and dragged it should move a completely unrelated Element.
Should I...
A) Create one directive and influence template & behavior based on a passed attribute?
B) Create two directives (one for the handle, one for the target Element)
If so, it again poses the question of where to put the methods to handle the dragging?
I would chose item A probably; passing in the element that needs to be manipulated as an argument to the directive. However, it depends how much I care about re usability of this piece of functionality.
Everything I say should be considered subjective.

Related

knockout and applyBindings - how to control the scope?

I am new to knockout, so going to fire a lot of questions. But I'm not new to data binding. So I am knocking my head on the difference between my expectations and reality. Here is a very basic question about applyBindings.
It looks like applyBindings has the parameter "view model", which is to me the domain object graph (as javascript objects) plus perhaps additional helper things added for the purpose of the view creation. But what I am completely missing at first is the scope of the bindings! I expected this to be applied to the current parent DOM element. But no, it is applied globally, in the entire page!
So is the expectation that in one HTML document there can only ever be one view model? This is very surprising to me! How am I supposed to create a single page web app where I have one panel showing the address book, another panel showing my appointments, another panel showing one loan application to review, and yet another showing the underwriting of another loan? They are all completely different things, am I really supposed to link them all into a single view model???
In my expectation, you bind a javascript object to a DOM element, and everything in there renders it. With every new nested DOM element, the focus object may change. It may be flowing out of some foreach binding from the parent's object. But then two sibling (or cousin) DOM elements might be sitting side by side and having completely different view model, and also a different life cycle. Like while I am in my underwriting workflow, I quickly need to bring up an address book or my calendar. All of it in a single page app. There should be no global interference between different view models used by different unrelated DOM elements.
And yet here we are with knockout I see it has only one ko.applyBindings(viewModelObject) for a the entire page.
What am I missing? What is preventing us from modifying ko.applyBindings to take two arguments, the view model-object and the DOM element in which to show it? I could try doing that, but I am afraid if knockout has been designed in this global mindset, there might be lots of issues running the knockout machinery more than once on the same page?
I'm sorry if I am frustrating people by shooting an answer already. But since I have a very urgent project I need to try to resolve my issues ASAP, and I am reading the knockout source code, which is quite nicely organized and uses good names, so it's quite intelligible; therefore I have found the answer myself.
The answer is that, yes, you can applyBindings to a parent element that you choose. The document.body is only the default if you don't say anything.
Therefore, from now on, I shall (tell my team to) always call applyBindings with the second argument specified for the rootNode. Like this:
<div>
... all my UI elements for this thing ...
... then last element in this div:
<script type="text/javascript">ko.applyBindings(viewModelObject, document.currentScript.parentElement);</script>
</div>
so, that way I can have multiple view models each in their own DOM element.
And additionally also, I was wondering, does knockout not somehow assign the model object to the DOM element? I could do that in my script tag too:
<div>
... all my UI elements for this thing ...
... then last element in this div:
<script type="text/javascript">
const viewElement = document.currentScript.parentElement;
viewElement.viewModelObject = viewModelObject;
ko.applyBindings(viewModelObject, viewElement);
</script>
</div>
and this allows me then -- if only for debugging -- to find the current view model object on the DOM element that is the root of a view. (It would be nice if that would happen with all other descendant bindings too, but that is perhaps the subject of another question.)
In fact, I decided to put into our general configuration a hard replacement of the ko.applyBindings function:
ko._applyBindings = ko.applyBindings;
ko.applyBindings(viewModelObject, rootNode, extendContextCallback) {
rootNode = rootNode || document.currentScript.parentElement;
rootNode.viewModelObject = viewModelObject;
ko._applyBindings(viewModelObject, rootNode, extendContextCallback);
}
now I don't even have to convince my team to do it this way, they will automatically, even without being aware of it.

How to find out which directives were applied to an element?

Is there a way to find out, for any given element, which AngularJS directives were applied on it/its attributes?
In a large web application, I am looking at an HTML element that has an additional attribute that I suspect might invoke a directive (the attribute name is obviously specific to our application). Or it might simply be dead code.
It looks something like this:
<button ctl-remember-special data-ng-click="handleAction()"></button>
(ctl-remember-special being the possible directive in this example)
I cannot seem to find a directive of the appropriate name in our project, but I am not entirely sure because some of the identifiers in our application are assembled at runtime. (To provide an impression of the dimensions, I am seeing more than 6000 directive definitions in our code.)

Angular directive loading order.

I have a view containing two nested directives. The inner-directive depends upon the outer-directive to be present. When I load the page from scratch the directives are loaded as expected i.e the outer exists before the inner. But when I navigate from the page and then return, the order of directive-loading is reversed i.e the inner-directive is loaded before the outer-directive. Both directives are costume directives. When I change the outer-directive to instead being a ng-controller, the problem is solved. To perform the navigation ui-router is used. Does anybody know how to solve this issue without having to use a ng-controller as the outer-directive? And why ng-controller solves the problem?
<div outer-directive>
<inner-directive></inner-directive>
</div>
Without knowing a ton about your specific directives, you can take a look a this blog post about directive priority.
(Controller gets processed in a different cycle than directives, so it makes sense that putting whatever 'dependent' code inner-directive requires into a controller solves your problem).
Here's the interesting part: Angular will follow, it appears, this "path" for your nested directives:
outer-directive: compile
inner-directive: compile
outer-directive: pre-link
inner-directive: pre-link
inner-directive: post-link
outer-directive: post-link
The linked blog post explains "why," but this processing order explains your discrepancy between initial page load (where compile is run for the directive) and your page-back case (where the directive is already compiled and we just need to link).
Again, without knowing much about your directives, maybe you need to relocate the dependent outer-directive code into pre-link (instead of link) so that it gets processed first?
You do that by breaking up your Link object into:
Link: {
pre: function(){
},
post: function() {
}
}
Still need help, [here's a decent post] (http://www.undefinednull.com/2014/07/07/practical-guide-to-prelink-postlink-and-controller-methods-of-angular-directives/) on the breaking up link into pre/post.

Difference between the directives and static DOM elements in AngularJS

I have already read the
Compilation process, and directive matching
on AngularJS Doc.
but I really didn't understand the directives.
Example:
I have static html:
<div class="test test2" cid="549" sid="a5e3c4f8a9">text-text-text</div>
when I do it manually, I know it will only created and called one time at browser's parse time.
but what happens when I create a directive with the same dom element ?
<x my-directive>text-text-text</x>
is this the same effect?
I am asking such a newbie question, because I am using over 200 elements on my html page.
If I change them to single directive: for sure it will be much more easier to manage them.
and its no problem if its only slow at browser's compile time but what happend in run time?
and I am sorry, if the qustion is not pro enought. I am just new to Stackoverflow.
Thank You
Daniel
If I understand you correctly, you want to know how AngularJS creates a directive and how many times your directive methods are called.
When you create a directive (with module.directive('myDirective', ...)), you are really just creating a definition. Every time you use that directive (like <div my-directive>), AngularJS will run through the process described in the guide: that is, it will compile and link each use. It has to be this way because the directive doesn't exist in isolation; it exists not only in the $scope in which it was called, but also it can make use of element attributes and transcluded contents. The definition occurs once, but each instance is compiled and linked.
Once the directive is created, it's technically done; if you don't set up any $watch or $observe or event bindings, your "directive" is now just whatever's in the DOM at the end of your link function - there's no more computation. In other words, what happens after compilation and linking is entirely up to you.
So back to your example: if you use 200 of the same directive on the page, the directive will be defined once, but all 200 will be compiled and linked separately. But I'm not really sure what you're implying by asking. What's the question behind your question?

How to do day to day Javascript stuff with Angular js? [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
over the past 3-4 days I have looked in to Angular js. From the look of it (with tutorials & videos) it looks pretty intuitive to use.But when I actually began to replace my current web app code with Angular js I faced lot of issues to startup.
For example if on a certain event I want some HTML to be added to an already present element somewhere in the DOM then by traditional jQuery route i'll simply get the element and add HTML. I can not fathom how to do the exact same thing with Angular because DOM manipulation is just not advised in Angular controller.So how do I access the element that I am supposed to expand.
Another example could be when I have to reset a Form element at some point. Again I face the dilemma of using jQuery style element selector inside Angular controller.
Could somebody please explain it to me how to do all these things with Angular.
Let me answer your title question first off: don't.
AngularJS is a powerful tool (and I'm only scratching it's surface, having worked exclusively on angular for 2 months # work, now) but it is far too opinionated for it to be a good choice for scripting. It has a bit of a learning curve (not too steep, but certainly more so than the "hey, {{model}} is magic!" intro you find on their page. Don't take me wrong - I absolutely love it, it is extremely structured, which forces me to be organized, and makes the code extremely scalable. So, if you are willing to learn its "MO", you'll love it once you do.
Now, on DOM manipulation: you can go about it in numerous ways, but basically, if you want DOM manipulation, you want a directive. The tutorials are good to help you on this, but if you are moderately good at JS, I advise you to explore the source code (ngRepeat, which you may already know, is a straight up regular directive, exactly like one you could create). An angular scope is tied to a DOM element, and vice versa. In a directive, you can address the element it is tied to, and there use jQuery (or any other framework, or just plain JS) to change it. A change can be tied to the moment the element appears, to the changing of a model (via the $watch function), or to an event you specify (and trigger) yourself (see $broadcast ad $emit).
I changed an example script that tackled a different kind of problem (namely triggering events on the end of a ngRepeat) to include a bit of different ways of dealing with DOM: http://plnkr.co/edit/or5mys
In Angular, all the DOM manipulation code resides in directives. So if you need to add HTML to an already present element, for example loading the content of a modal dialog from a remote url, you will need to write a directive for that. See the documentation here: Directives in AngularJS
It depends on what type of DOM manipulation you want to do; as others have said, a Directive could work, but that's more for complex DOM stuff or repeatable widgets. For regular form stuf, if it's simply filling in text somewhere you can just do that with the contoller's scope.
For example if you want to update a status message, instead of directly updating the DOM, you can bind it to a field on your scope:
<div>{{statusMsg}}</div>
and in your controller, when appropriate:
scope.statusMsg = 'Done loading';
Since Angular has two-way data binding, the DOM will automatically get updated with the message.
For the example of a form reset, your form would be bound to a model, so to reset the form you would just reset the model. Here is a very simple example: http://jsfiddle.net/Kbsqx/

Categories

Resources