Where to implement ng-click functions, controller or directive? - javascript

I'm a beginner in AngularJS, I understand most of the mechanics but I'm still grasping the "culture".
I'd like have clean separation between my HTML, DOM, data and communications.
My impression of a controller is a module that implements a "data" model, but is void of UI semantics (i.e. DOM manipulation).
However in my HTML, if I use an ng-click it is the controller's scope that is accessed for the click function implementation, which more then likely will need to call back into the DOM.
So where should I implement my click functions if I do not want DOM manipulation in my controller? Are DIRECTIVES the universal answer to this?
Suppose I have 2 controls on a page that need to interact with each other, should I create a directive on the parent of those controls parent that implements the click functions of both child controls? Or perhaps create a directive for each control and possibly pass the ID of the other control as an attribute? OR maybe a directive for the parent AND children?
--------- UPDATE 1 -----------
The following HTML is a simplified and contrived example that [hopefully] illustrates my question.
<div id="searchComponent">
<input id="txtSearchText" ng-keyup="..."/>
<input name="Go" id="btnDoSearch" ng-click="..."></input>
<div id="autoCompleteResults"></div>
<div id="fullResults"></div>
</div>
As the user types in the txtSearchText, the autoCompleteResults is populated, factoring in the usual minimum characters and timouts.
When the user presses or clicks the btnDoSearch, the autoCompleteResults is cleared/hidden and the fullResults is populated.
Finally, if the user begins typing new txtSearchText, the fullResults would be cleared/hidden and the autoCompleteResults is again seen with results.
Any guidance would be appreciated!

So where should I implement my click functions if I do not want DOM manipulation in my controller? Are DIRECTIVES the universal answer to this?
DOM manipulations, in my opinion, means something like document.querySelector(), addCliss, etc. ng-click is the event, which is supposed to deal with some business logic. Put it in the controller is fine.
Of course, directive is your another choice. directive is usually used to extract some reuseable components, such as modal, across different pages. If you repeat some code in different controllers, consider extracting them to directives or service.
Suppose I have 2 controls on a page that need to interact with each other,...
In short: use service, which is designed for that scenarios.

The general philosophy is to reference the DOM explicitly as little as possible. Most (if not all) things you want to do can be done by binding an aspect of you HTML element to a property on $scope, and manipulating that property. So you never have to do some like "Change the class of <span id="foo"> to red now that isRed is true". Instead you would have <span ng-class="{ red: isRed }>". So if you have two click handlers that share information, it's perfectly valid to have them change some common variable in your controller, and have state of the UI accordingly with DOM bindings. Directives are used more for reusing DOM elements, or when you do have to explicitly refer to DOM elements, i.e. adjusting the scroll properties of a div. You could use a directive to add the same click handler to many elements for example. Services can be used to share information, although if both of the controls belong to the same scope there's less of a reason to do that.

Related

Directive controller to parent communication

The question I am asking is to figure out which is the best practice for communication from the child to the parent controller in a certain scenario. Say for example we have an element with a controller called "ListController", inside which we have a list of directives populated through ng-repeat. The directive is linked to a different controller called "ListItemController" and has an isolated scope, so can no directly use the functions of the parent ListController. In this case with my beginner knowledge of angular I've narrowed it down to 2 options that seem the most intuitive. The first option would be to $emit an event in the ListItemController and catch it $on the ListController, but events are commonly considered a poor choice for communication. The second possible option would be to access the parent ListController through the use of the $parent field of the ListItemController, but, assuming because of the fact that our list items are created through ng-repeat, we can only reach our ListController by using $scope.$parent.$parent, which doesn't really smell of good code either. I would like to know which option out of these two or others would be considered the best practice and why?

Add new properties to Knockout viewmodel after ko.applyBindings is called

In a (webforms) page I have a button that opens a jqueryui dialogue which is loaded dynamically.
The problem is that I want this dialogue to be master of its own knockout view model but the view model is already set in the main page.
I suppose it is not possible to add new properties to the view model after ko.applyBindings is called.
Instead I should be looking into another design. But which?
Applying bindings to different parts of the DOM would require me to some big redesigns I hope to avoid right now.
Having all the dialoge bindings as a list of key-values is possible but not very elegant IMHO. The main page would then only have to add a vm.dialogueKeyvalueCollection.
My present, possible, solution is to have the main form add the dialogue's properties vm.dialogue.userName() vm.dialogue.searchResult() but then my html controls won't bind as they are created after applyBindings is called. The present solution for this is to call ApplyBindings again like so: ko.applyBindings(vm, $('#dialog-form')[0]); for the added HTML. I was in the belief (and still somewhat am) that to call applyBindings for different DOM elements one must not be nested inside another. Binding to dynamic HTML is commented here and jsfiddled here.
?
I do a lot of composition with nested view models, often for the purpose of creating dialogues modals. See here for a full-fledged answer.
It might be simpler to try and get away with just using the with binding, though. You could create a dialogueViewmodel observable property on your viewmodel.
Just fill it with one or more observable keys when you're ready to show the dialogue, such as
this.dialogueViewmodel({
markup: ko.observable("<h1>Kittens!</h1>")
});
and wrap it in a with binding:
<!-- ko with: dialogueViewmodel -->
<div id="dialog" title="Basic dialog" data-bind="html: markup">
</div>
<!-- /ko -->
As long as dialogueViewmodel is null, nothing gets bound and rendered. This only happens when you add your dialogue data - no need to fiddle with applyBindings again.
You will probably have to write your own binding to interface with jQueryUI.dialogue, though.
Third option: I have written a modal library that comes with a Knockout binding out of the box. Here's a JSfiddle demo. If you're not set on jQueryUI, that might be an alternative; while the documentation is not perfect, I'd be happy to help you any way I can and fix the docs along the way.
Can you ko.applyBindingsToNode function to bind the appended html.
ko.applyBindingsToNode(appendedelement,{ binding options})
Hope this will help you.

Using and Communicating with Directive from different Controllers

I would like to create a directive that is similar to a control. It should be able to be instantiated from "anywhere" and I want to call functions on it's scope from a parent scope/controller.
A very easy example would be:
I have a special directive called myContactForm (which can be used in different places and pages). Now this myContactForm, in it's templace, uses the directive myEditControl. myEditControl is, when you look at the template nothing but an input and a button and in the scope has the function "clear()" so you can delete whatever is inside the input and maybe other functions.
Now when something happens in myContactForm (for example a button is pressed or a broadcast is received or whatever) I want to call "clear()" on the myEditControl that's inside it. Just like it was a normal control.
How would that be done?
I know about the require but that only works for parents and I would have to know the Controllername of the Directive that uses myEditControl. Instead I want any View/Controller to be able to use myEditControl. And I cannot require the myEditControl-Controller from myContactForm because it is a child not a parent.
What is the technique used in angular to solve these kind of problems?
My idea was to transmit a callback or "myContactForm"-Scope to the myEditControl with an attribute so the myEditControl can "register" itself on the parent and the parent then saves that controls scope for later reference and calling of functions. This seems very hacky though....

AngularJS : directives newbie

I'm starting my great adventure with angular and wanted to ask a question regarding directives usage, as I am not 100% sure after seeing multiple tutorials.
I want to make a simple app giving you directions:
1) click a button, fire in the controller a function to get current position from navigator geolocation (i think no service is necessary for this, and this can stay in the controller?)
2) after getting the coordinates I have some information about the place, which should be shown to the user, and here is the question: Should there be a directive with template for binding these information from the scope and showing in the dom, or is it enough to use simply "ng-hide" (which is in fact a directive - sic!:)) on a div, fetch the information on a place with a service, bind it with the hidden div, and set "ng-hide" to false to display the dom containing place information.
The "ng-hide" variant seems easy, but is it the proper "angular way" or just bad practice of beginners?
Thank You for your time and help in advance:)
IMHO
You can put it in a service if you want to use that method from different controllers or for clean-code purpose.
I use directives when I want a specific behaviour or a group of controls that repeat along the application. If you are using basic html controls and you just need to display/hide I would use ng-hide.

Angular 1.2.0-rc.3 Directive Priority Change Issue

So I have this code example that uses Angular 1.2 RC2 and everything works fine, you click on the handle to toggle the display of the content and the controller and directive have seperate scopes as intended:
http://plnkr.co/edit/e3XAZuhSMAxmkWzKKM39?p=preview
Now I upgraded to Angular RC3 yesterday and now the functionality does not work as it stands in the plunker, I get the error the specific requires generic which is can't find. Going through the change log, I though this might have to do with this breaking change:
$compile: due to 31f190d4, the order of postLink fn is now mirror opposite of the order in which corresponding preLinking and compile functions execute
To fix this they either suggest converting the post linking to a pre linking (which I can do since my post linking needs access to the scope which is not available in the pre linking) or to decrease the priority of the directive. So this plunker does that and functionality does work:
http://plnkr.co/edit/arP3aruH8HEdiwFg6mWp?p=preview
However there is a major issue and that is now because specific has a higher priority, the isolate scope that generic needs is no longer being created so now contentVisible is on the controller scope which is not good.
Now I could just move the scope: {} from the generic directive to the specific directive however it should be possible to use the generic directive by itself and if I did it would attached to whatever scope it is part of and not its own (which would make it impossible to have multiple instance of this directive, which is way it needs its own scope).
The only thing I can think of is to add a directive, called something like isoScope, make sure it has a very high priority, and have it define scope: {}. Then if I need to use generic by itself, I just have to make sure to also add the isoScope directive to make sure it has an isolate scope. Like this:
http://plnkr.co/edit/1NYHpUcPFWEbAzvkCeRH?p=preview
Now I am hoping there is a better way to accomplish what I am looking for without the isolateScope directive. Am I missing a way of that this without that?
UPDATED EXAMPLE
So here is another plunker that includes hopefully better examples of what I am trying to convey (still has virtually no styles but should not be needed to get the point across):
http://plnkr.co/edit/KtRMa1c9giDrhi1Rqyho?p=preview
I have 3 directives:
expander
notification
isolateScope
The expander directive only adds functionality to be able expander and collapse content, nothing more. This functionality is something that should be able to be used alone or with another directive (which is why it has a controller).
The notification directive is used to display notification however since we don't want to display the notifications all the time, we use it with the expander directive so that the user can toggle the display of the actually notifications (similar to how stackoverflow.com does it in the top left).
While I imagine the expander would most likely be used with another directive it should be possible to use alone and that is where the isolateScope directive comes into play. Since the expander directive adds data to the scope and you may want to have multiple expanders on the same page, it needs to have an isolate scope in order to work. Now on a users profile page you have have data like developer key and address that you don't really need to display all the time so lets have the user control that. I have the isolate scope to be able to control both of those independently because without the isolate scope, both of them would be on the same scope and be controlled by the same instance on contentVisible.
I just don't see anyway with how directives now run in 1.2.0 RC3 to be able to accomplish this without that isolateScope directive (though I would be happy to be proven wrong).
I have updated your code so that it does what I think you want (at a minimum this works the way your old code does, but under rc3 as you wanted): http://plnkr.co/edit/nsq4BGAih3lfNmS2mLP7?p=preview
But I made quite a few changes and a significant architectural change so let me know if this moves away from the spirit of what you're trying to achieve.
I think the gist of the issue was that your two directives (generic and specific) were tightly coupled around contentVisible which created a complex dependency that resulted in you having to very carefully manage invocation timing. My approach was to decouple the two directives- encapsulating contentVisible within generic. This allows generic and specific to instantiate fully independently. So you're not dependent on any invocation timing. And thus the directive priority change no longer has any impact on this code. So, one big win with the solution I propose is it should be robust against further changes by the Angular team.
Specifically, I moved the template in to the same directive (generic) as the controller which manages contentVisible . This way the controller that changes contentVisible lives on the same scope as the template which uses it.
Now specific just calls over to the required: generic controller to toggle visibility (effectively as a setter function).
I also moved the ng-class assignment into the template in order to encapsulate that change within one place (the template) and so you don't need jquery or a link:/compile: on generic.
This is a regression. A fix is in the works: https://github.com/angular/angular.js/issues/4431
I have problem, looks very close to your. So if anything will change want to be notified.
My task: I have contact, that could be shown in defferent ways (very common task), but difference between views is in templates, whereas help functions and preparations are same, so I need generic directive for all views.
What I found:
1. in rc2 it works fine in rc3 unstable
2. in rc3 it could work same only when template is inline, but not when it is templateUrl (even if cached)
So I created two planks rc2 version and rc3 version.
Hope this will help.

Categories