How to separate functionality of Angular directive - javascript

I have this extend text component which can add auto complete, tagging, and search query capabilities to a input/textarea (which can work independently or together). Instead of having all the functionality included in one big directive, I would like to move the auto complete, tagging, and search query functionality into there own directives in order to make changing or replacing them easier. Any type of communication that needs to happen between them I imagine would be done through the directive controller's.
The a small prototype on how I plan on doing this is here:
http://plnkr.co/edit/836sPtrBt4XXOeDI8mLf?p=preview
Where the element must have the extend-text attribute but then can also optional have any of the et-auto-complete, et-tagging, or et-search-query.
Is this the angular way of separating this type of functionality for directives?

Related

Best approach to component/visual inheritance in Angular 2?

I am new to Angular in general and starting in earnest with Angular 2, so I want to find out if I'm not thinking about this in the right way yet.
A "panel" is a page of content in my application. I'd like to use a base panel component to provide common UI and functionality to specific panel implementations that derive from it.
I'd like for the base panel component to have templated content that wraps the template provided by the derived component- this would, for instance, provide a standardized header whose content is provided by the derived implementation and allow for the child component to supply the page content itself.
The ultimate goal is to make it as easy as possible for developers to create new panels without having to worry about rendering the common parts, so that consistency can be enforced.
Also, I want for the developer to be able have two-way binding between base variables/properties that are programmatically set from the derived component and the corresponding elements that are rendered by the parent template... (in addition to the elements in their own child template).
Is this doable? Or am I thinking about this in the wrong way... not sure if I'm in an Angular state-of-mind yet.
As I've been working on this, I'm starting to think I may need to adjust to creating reusable components (like PanelHeaderComponent) that the developer would compose within their panel implementation rather than inheriting from a base for common UI... However, I do need for the container for all panels to be centrally managed somehow.
Want to be sure I do this the right way. Thanks!
I believe a valid approach is, like you said in your last paragraphs, to componentize your application.
Basically: Create reusable components for the different parts of your application (ie: Panel Header with Buttons with a certain style)
If you want different panels that will use the same header, you can then reuse that Panel Header in all of your your panels.
It would be analogous regarding buttons, and any other control that you want to use. Define your component, then reuse it everywhere, then reuse the reusable, composite components that you defined.
Important Note: As of now, and as far as I'm aware, there's no Visual Inheritance between components, so in order to simplify your job, you might want to use sass to define the styles and take advantage of #imports in order to DRY.

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

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.

What are the best practices for creating new elements of different type in Angularjs?

I have a list of elements of different types. Each has a toggle which toggles their visibility. Now there are two ways to hide an element either detach it from DOM or set the visibility to hidden.
As I understand Angular still updates hidden elements so this may impact the performance. Is this true? With jQuery one can detach the element from DOM and then attach it again when it needs to be visible. But is this approach even a good practice in Angular?
From reading Angular documentation and its API it gave me an impression that Angular prefers that all templates/HTML are declared at the start and their content is dynamically changed with controllers. So if you want to add/remove elements you'd use an ng-repeat directive and then by removing elements from an array in the scope you can add/remove elements from the template. This works well with primitive elements of the same type. However, how does this work if you have a list of elements of different type?
Edited:
http://jsfiddle.net/k26bA
An example here would be a list of tools which can be made available with a checkbox. In the example the first approach has a static list of elements which can not be dynamically changed. Which means you need to know in advance which tools will be available.
The second approach has a list in the controller to which you add and remove tools and in the template use ng-repeat to iterate over that list and create the tools. However, I'm stuck here as a tool can be a button, a text field, checkbox or even a complex div.
I find it a little hard to have a model first here because this is just a part that hides and shows available controls as opposed to displaying a domain model.
A good example of what I'm thinking would be Google Maps where you can hide or minimise various controls on the map.
You probably need to familiarize yourself with the ng-switch directive. The inactive items in an ng-switch are entirely unhooked from the DOM, as opposed to an ng-hide or ng-show, which simply set the CSS styles to show or hide.

Angular: possible to force recognition of model modifications made outside of $apply()?

According to the Angular guide on scopes,
Only model modifications which execute inside the $apply method will
be properly accounted for by Angular.
We have quite a few legacy formatting tools for our forum post editor that make changes to a textarea; they operate entirely outside of angular. Now we would like to bind the textarea to an angular model. If the formatting tool used to make the last change made to the textarea, angular does not update the model. Of course, if you were to click a button to add, say, bold to some text, then add some additional text, all changes would be recognized.
Short of rewriting all of our tools, is there any way we could force angular to recognize the changes made to the textarea by the formatting tool after, say, one of the formatting tool buttons is clicked?
Assuming the 3rd party formatting tool has some sort of callback after it has updated the text area you could do something like:
angular.element('#myTextArea').scope().$apply();
This will retrieve the scope associated with your textarea and fire off a digest cycle in angular.

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