When using server-side templating and client-side angularjs, I'm not able to get angularjs to recognize values I've templated in on the server.
For example (or on jsfiddle):
<div ng-app>
<div ng-controller="Ctrl">
<textarea ng-model="data" placeholder="Enter a name here">Templated in</textarea>
{{data}}
</div>
</div>
Angularjs will always replace the value in the text area with the value of $scope.data (which is null). What I want is for the value of $scope.data to take on "Templated in", on app bootstrap, then to continue normally from there.
How can I template in from the server a value, then have angularjs model bind that value once on the client?
Use ng-init
<textarea ng-model="data" placeholder="Enter a name here"
ng-init="data='Templated in'"></textarea>
See also AngularJS - Value attribute on an input text box is ignored when there is a ng-model used? and
rails + angularjs loading values into textfields on edit
I came across this answer while trying to figure this out for myself, but never liked that I had to move information from the part of the field where the HTML spec wants it. That just didn't feel like the right approach to me.
For a while, I ended up taking advantage of the fact that Angular sits on top of jQuery (or the embedded jqLite subset if the full jQuery isn't available) to solve this without moving the content, or even touching ng-init. Specifically, you can access the contents of the textarea using (the Angular/jqLite version of) standard jQuery methods during the controller initialization phase.
So I just did
var doc = angular.element(document.documentElement);
$scope.data = doc.find('textarea').eq(0).val();
in my controller, exactly where I would initialize any other scope variable. (Alternately, here's the modified jsFiddle...)
With the full jQuery library available, the code is even simpler, since you have access to full jQuery selectors at that point and can jump straight to $('textarea').eq(0).val() (or even add an id to the field and select on that: $('#data-textarea').val()).
The nice thing about this approach is that it will work for any form elements, though since most of those are <input> tags, and jqLite doesn't support selectors, finding the exact one you want can be a tad tricky. I would simply include the full jQuery library at that point and take advantage of the selectors, but that's me.
This approach also has the major drawback of placing DOM-aware code in the controller, which completely breaks both Angular conventions (The Angular Way (TM)) and the MVC/MVVM programming paradigm. Not the best solution.
UPDATE: So I eventually realized I would need a more long-term solution, one which didn't violate so many best practices. The answer actually comes from the most essential element of Angular, without which none of the rest of it would work (you could argue that for other components, but it's simply more true for this one): Directives. More specifically:
app.directive('input', ['$parse', function ($parse) {
return {
restrict: 'E',
require: '?ngModel',
link: function (scope, element, attrs) {
if(attrs.value) {
$parse(attrs.ngModel).assign(attrs.value, scope);
}
}
};
}]);
This has a number of advantages. First, it doesn't break Angular conventions that help reinforce MVC/MVVM. Second, it doesn't even touch jqLite/jQuery, nor the underlying DOM functions. Third, it has the desired effect of preserving HTML conventions for defining default values, allowing (or at least simplifying) the use of Angular with other existing technologies, such as server-side templating engines.
Why doesn't Angular do this by default? Well, I don't know the actual answer without a bit more research, but a likely answer is that HTML conventions favor static page content, and Angular is designed for dynamic page content. That means breaking out of HTML conventions in many places, not letting it limit what is possible with Angular apps. Since controllers are expected to carry the responsibility of initializing models (and in most cases such an expectation is the correct one), the Angular team would have incentive to ignore the contents of a value attribute (and its analogs among the other form tags).
Of course, with this approach, for any applicable elements that already exist before the controller is initialized, the controller init can override the value attribute. This behavior is not consistent, however, across the entire application, because new elements will trigger the directive evaluation, but not the controller init phase. (Partials with their own controllers hold this behavior as well - elements in place prior to the partial's controller init can be overridden by said init, but other elements added after that won't re-trigger the init.)
There are a number of other ways to write such a directive, and it can be extended to do any number of other things as well, but hopefully this approach helps someone else.
Related
Sometimes when I build an Angular custom directive, I will hear a comment as to, if it is an Angular directive, it should not use jQuery code in it, because it should be built in an AngularJS way.
And I thought it might be true, but is it possible? For example, what if the directive template has 2 sections, one is the words and one is the tiny images (such as review stars), and so you need 2 sections in your template, labeled as .description and .star-images -- so then you should need to use $.find(".description") to find that section inside your template if you need to do something to it or inside it. jqLite won't work as jqLite's find() is limited to tags only.
Another example is, what if you have a directive that doesn't have a template, but just limit the keypress to digits only, say, for an input box. So you don't want your directive to have a template as <input type="text"> but just want the user of the directive to say <input type="text" digits-input-only> and your directive is called digitsInputOnly. So in that case, don't you need to use jQuery's elem.on() or elem.bind() to listen on keypress or keydown events, and when the key down code is not a digit, then do a event.preventDefault()? So in that case, it has to use jQuery?
Or other there other ways to do it so that you really shouldn't need to use jQuery?
As a long time user of jQuery it is easy while learning angular to lean on jQuery however none of the cases mentioned are difficult to work around not having it...and in most cases are actually easier
Try removing jQuery completely from your project to avoid temptation and you will quickly realize how little you really need it
The core directives provide the majority of the event handlers needed and angular.element (jQlite) also has bind() which will accept virtually any event name. $document.bind('contextmenu', function(event) for example.
The core dom event directives all let you pass in $event for things like event.preventDefault()
<input ng-keydown="somefunc($event)">
For traverses you can always use a native method to query DOM to find an element and wrap that element (or collection) in angular.element() the same way you would with $(). The more you focus on data models and core directives first however, the less you find need to actually do dom traverses
As for plugins ... it's not a sin to use jQuery plugins in directives. There are some very commonly used angular modules that are wrappers for well known jQuery plugins ... fullcalendar and Datatables are a couple that quickly come to mind along with numerous datepickers. However often you will find situations where you may have previously leaned on a plugin to do simple tasks that angular makes easy itself and you no longer would use such plugins
The sin with using jQuery plugins is using ones that are actually easier to achieve (and test) using angular itself
In conclusion, the biggest adjustment is learning how to focus on data models first, before thinking about the DOM. Also being intimately familiar with the left side menu of the API reference where all the core directives and services are listed is a huge help
Say, if you define a directive with the template:
<div>
<button id="add-button">add</button>
{{ value }}
<button id="minus-button">minus</button>
</div>
Then what is a correct way to find your button in your link(scope, element, attrs) function?
Some thoughts and details:
I have seen
angular.element(document.getElementById("add-button"))
(the angular.element is just to convert it back to a jqLite/jQuery object)
but what if a user adds your directives multiple times on their page, then that line will find the wrong element because document.getElementById("add-button") will probably find the first matching element on the whole page with that ID.
Since jqLite can only find by tag name, so we may be able to do, by going back to DOM methods:
angular.element(element[0].querySelector("#add-button"))
Or, I don't know why jqLite doesn't let you find by class name or by ID, so it may be recommended to actually load jQuery before AngularJS, and use the full version of jQuery:
element.find("#add-button")
Or, supposedly only one element with the same ID should exist on the whole page, so to be more correct, we really should use class name instead of ID to identify the element, such as <button class="add-button"> and find it by
element.find(".add-button")
I think searching by tag name and then using the 1st found element or 2nd found element is not such a good way, because what if the template is changed or rearranged later, then you need to change your code too, so it is too tightly coupled together.
The most common way (seen by John Papa and Todd Motto) is to go with jqLite.
The worst way is using JQuery inside directives without restrictions.
Absolutly worst case:
$('.some-class')
Much better:
element.find('.some-class')
Also seen and valid:
$(element.find('.some-class'))
So, why you gonna do such things, manipulating DOM from a directive? There are a whole bunch of reasons. Maybe you want to initialize some JQuery plugins like datepicker or nanoscroller, then you need such things.
Styleguides are just philosphy, you can accept them, you just believe in small parts of the whole guide or you create your own. There are some guidelines out there, which can improve your code heavily (performance, readablity, etc.).
Styleguides
Angular 2 John Papa
Angular 1.x John Papa
Angular 1.x Todd Motto
We're trying to lock down the best method for using services and have come up with three possibilities. This is a much smaller example of a much bigger problem but I hope it gets the point accross. The service in these examples is very simple:
.service("markerService", function(){
var self = this;
self.showMarkers = false;
self.toggleMarkers = function(value){
self.showMarkers = angular.isDefined(value) ? !!value : !self.showMarkers;
};
})
And the dom after it's compiled is also very simple:
<div ng-controller="ParentCtrl" class="ng-scope">
<div class="assetsDirective">
<div>
<div class="assetDirective">Hidden Name: 1</div>
<div class="assetDirective">Hidden Name: 2</div>
</div>
</div>
<div class="toolBarDirective">
<div>
<button>Toggle Markers</button>
</div>
</div>
</div>
The options we've come up with are:
Option A: http://jsfiddle.net/nxhn31yv/ (both assets and toolbar have service)
Option B: http://jsfiddle.net/oszop5j4/ (assets has service, parent of toolbar has service and toolbar updates it)
Option C: http://jsfiddle.net/a7nf31d3/ (Parent of all of them has service, passes it down)
The first one injects the server into two sibling controllers and one of them calls a toggle function to update the value stored within that service.
The second option has it injected into two services that have a parent/child relationship. The parent passes the value to one child which can update it (like an ngmodel) while the child controller with the injected service passes it's value onto it's children which use it.
The third option has the parent pass the value all the way through to both siblings, which in turn has the one sibling pass it to it's children as well.
Which of these fall in line with angulars methodology of how to implement services?
Option A is the clear winner for me. It more closely matches the idea of discrete, decoupled web components - the direction in which Angular and web development generally seems to be heading.
For Angular 1.x this means using directives to encapsulate all markup/logic that is bound together. Typically ng-controller in your markup should NOT be required. It should be possible to identify the dependencies (injected or related directives) for any directive by looking ONLY at the declaration of that directive.
Option B and C violate these simple principles and therefore (assuming actual implementations are more complex than these simple examples) are likely to be much more painful in terms of maintenance and debugging.
I would say option A is the best way to do it.
I believe one of the greatest things Angular teaches you is how to modularize your code. Programming in such a way where if you wanted to, you could remove, add any piece anywhere. So with that, you would try to avoid using anything that is dependent upon another piece. Your Option A succeeds in this way.
Of course sometimes it is unavoidable, but I use that as a general rule. It makes things easier to understand and follow, keeps things clean.
edit: It also protects yourself from changes to scope in the future.
According to Pro Javascript Development by Den Odell,
applying a set of methods and properties from one Object directly to another or a prototype of a Class can be a "hack", especially for that developers approaching Angular from Object Oriented Background.
Neverthless, when it is being used carefully, can simplify development and code mainteance.
That is a mixin design pattern, which avoids extensing subclassing and inheritance chains.
Injecting service into encapsulated directives, will help you avoid parent controllers and inheritance.
That is why i choose option A, but use it carefully.
More about mixin.
Another article
I do not quite understand when to use a directive and when it would be more appropriate to use nginclude. Take this example: I have a partial, password-and-confirm-input-fields.html, that is the html for entering and confirming a password. I use this both under signup-page and under change-password-page. Those two pages has a controller each, the partial html has no dedicated controller.
Should I use directive or ngInclude for this?
It all depends on what you want from your code fragment. Personally, if the code doesn't have any logic, or doesn't even need a controller, then I go with ngInclude. I typically put large more "static" html fragments that I don't want cluttering up the view here. (ie: Let's say a large table whose data comes from the parent Controller anyway. It's cleaner to have <div ng-include="bigtable.html" /> than all those lines cluttering up the View)
If there is logic, DOM manipulation, or you need it to be customizable (aka render differently) in different instances it's used, then directives are the better choice (they're daunting at first, but they're very powerful, give it time).
ngInclude
Sometimes you will see ngInclude's that are affected by their exterior $scope / interface. Such as a large/complicated repeater lets say. These 2 interfaces are tied together because of this. If something in the main $scope changes, you must alter / change your logic within your included partial.
Directives
On the other hand, directives can have explicit scopes / controllers / etc. So if you're thinking of a scenario where you'd have to reuse something multiple times, you can see how having its own scope connected would make life easier & less confusing.
Also, anytime you are going to be interacting with the DOM at all, you should use a directive. This makes it better for testing, and decouples these actions away from a controller / service / etc, which is something you want!
Tip: Make sure not to use restrict: 'E' if you care about IE8! There are ways around this, but they are annoying. Just make life easier and stick with attribute/etc. <div my-directive />
Components [Update 3/1/2016]
Added in Angular 1.5, it's essentially a wrapper around .directve(). Component should be used most of the time. It removes a lot of boilerplate directive code, by defaulting to things like restrict: 'E', scope : {}, bindToController: true. I highly recommend using these for almost everything in your app, in order to be able to transition to Angular2 more easily.
In conclusion:
You should be creating Components & Directives a majority of the time.
More extensible
You can template and have your file externally (like ngInclude)
You can choose to use the parent scope, or it's own isolate scope within the directive.
Better re-use throughout your application
Update 3/1/2016
Now that Angular 2 is slowly wrapping up, and we know the general format (of course there will still be some changes here and there) just wanted to add how important it is to do components (sometimes directives if you need them to be restrict: 'E' for example).
Components are very similar to Angular 2's #Component. In this way we are encapsulating logic & html in the same area.
Make sure you encapsulate as many things as you can in components, it will make the transition to Angular 2 that much easier! (If you choose to make the transition)
Here's a nice article describing this migration process using directives (very similar if you were going to use components of course) : http://angular-tips.com/blog/2015/09/migrating-directives-to-angular-2/
Egghead.io has a great explanation of directive restrictions in AngularJS. A custom directive can be defined as follows:
angular.module("app", []).directive("blah", function () {
return {
restrict: "A",
template: "blah directive. nothing to see here."
};
});
This creates a what I will call an attribute directive (due to restrict: "A") for the purposes of asking this question. This is actually Angular's default restriction on a custom directive, and this directive can be used like:
<div blah>
<!-- content of directive -->
</div>
When I want to create a custom directive, however, I normally go for an element directive, like:
<blah>
<!-- content of directive -->
</blah>
How is the former attribute directive any better than the latter element directive and why was it chosen as the default?
This is my opinion, only:
There are three possible ways to define a directive in the HTML - attributes, elements and classes. Classes are too lax and confusing, and a best practice would be to separate logic from style. Element directives are too strict - you can only have one "element" directive in a DOM element. That makes them special right from the start.
An attribute seems to be the best middle ground between these two extremes - it is clear, allows for multiple directives in an element (If you are following Egghead's videos, the superhero example on "directive to directive communication" shows a kind of "directive hierarchy", elements superseding attributes. Also, and this is very important most of the times (I program for intranet apps, so to me it isn't) attributes allow angularJS templates to be valid HTML.
EDIT - my two cents would be that it doesn't matter - in any real scenario, it is a bad idea to trust the "default" configuration of something as primary as the restrict option - setting it explicitly makes for clear, no doubt directives (especially working in team projects, but also anytime, really)
Angular 1.3 changed the default to be 'AE'. See: https://docs.angularjs.org/api/ng/service/$compile#directive-definition-object.
I'm guessing it was changed because it's "restrict", not "include". So, the default for "restrict" should restrict very little.
What does that mean going forward -- if you see a "restrict: 'E'" line, it actually means "This wasn't meant to be used as an attribute", rather than the potential current meaning of "I simply want to use this as an element".
My take on this would be:
An element-based directive would most often represent structural functionality.
For instance, I use element-based directives for popups, dialogs, tabbed widgets, and reusable widgets in general. I then can add attribute-based directives to them (say, adding an ng-click to a <ui-button> directive), but the element name (i.e. the directive name) represents the structural semantics of what is being built.