Compare commits

...

510 Commits

Author SHA1 Message Date
Caitlin Potter 16dfcb61ae fix(ngResource): don't convert literal values into Resource objects when isArray is true
Previously non-object literals would be thrown out of Resource responses with isArray===true, or
otherwise converted into Objects (in the case of string literals). The reason for this is because
shallowClearAndCopy iterates over keys, and copies keys into the destination. Iterating over String
keys results in integer keys, with a single-character value.

Not converting non-objects to Resources means that you lose the ability to perform Resource operations
on them. However, they become usable as strings, numbers, or booleans, which is important.

In the future, it would be useful to make these useful as Resources while still retaining their primitive
value usefulness.

Closes #6314
Closes #7741
2014-06-13 13:41:18 -07:00
rodyhaddad 65a44dd49c test(isWindow): add tests for isWindow 2014-06-13 11:35:27 -07:00
Igor Minar 35358fddc1 perf($compile): move ng-binding class stamping for interpolation into compile phase 2014-06-13 11:35:27 -07:00
Igor Minar fd5f389676 perf(ngBind): set the ng-binding class during compilation instead of linking 2014-06-13 11:35:27 -07:00
Igor Minar 41d2eba5f8 perf(jqLite): cache collection length for all methods that work on a single element
This affects jqLite#html, #text, #attr, #prop, #css and others.
2014-06-13 11:35:27 -07:00
Igor Minar 3cbc8e5563 test(jqLite): add a missing test for jqLite#text 2014-06-13 11:35:27 -07:00
Igor Minar 92489886dc perf(jqLite): improve performance of jqLite#text
This change is not compatible with IE8.
2014-06-13 11:35:27 -07:00
Igor Minar ea230ea91d chore: name the event callback used by ngClick and friends
This maskes looking at stack traces easier.

Since we generate the callbacks for each event type at runtime and we can't
set function's name because it's read-only, we have to use a generic name.
2014-06-13 11:35:27 -07:00
Igor Minar b68ac4cb4c perf: optimize internal isWindow call
Each window has a reference to itself, which is pretty unique so we can use
that to simplify our isWindow check
2014-06-13 11:35:27 -07:00
Igor Minar 31faeaa729 perf(jqLite): optimize adding nodes to a jqLite collection
This code is very hot and in most cases we are wrapping just a single Node so
we should optimize for that scenario.
2014-06-13 11:35:27 -07:00
Igor Minar e35abc9d2f perf(jqLite): optimize element dealocation
Iterate only over elements and not nodes since we don't attach data or handlers
to text/comment nodes.
2014-06-13 11:35:27 -07:00
Igor Minar a196c8bca8 fix(jqLite): data should store data only on Element and Document nodes
This is what jQuery does by default: https://github.com/jquery/jquery/blob/c18c6229c84cd2f0c9fe9f6fc3749e2c93608cc7/src/data/accepts.js#L16

We don't need to set data on text/comment nodes internally and if we don't
allow setting data on these nodes, we don't need to worry about cleaning
it up.

BREAKING CHANGE: previously it was possible to set jqLite data on Text/Comment
nodes, but now that is allowed only on Element and Document nodes just like in
jQuery. We don't expect that app code actually depends on this accidental feature.
2014-06-13 11:35:26 -07:00
Igor Minar ea9a130a43 perf(jqLite): don't use reflection to access expandoId
Since we allow only one copy of Angular to be loaded at a time it doesn't
make much sense randomly generate the expando property name and then be
forced to use slow reflective calles to retrieve the IDs.
2014-06-13 11:35:26 -07:00
Igor Minar 04468db441 perf(shallowCopy): use Object.keys to improve performance
This change is not IE8 friendly
2014-06-13 11:35:26 -07:00
Igor Minar 55991e33af perf(forEach): cache array length
Micro-optimization :-)

BREAKING CHANGE: forEach will iterate only over the initial number of items in
the array. So if items are added to the array during the iteration, these won't
be iterated over during the initial forEach call.

This change also makes our forEach behave more like Array#forEach.
2014-06-13 11:35:26 -07:00
Igor Minar 8c6a8171f9 perf(Scope): change Scope#id to be a simple number
In apps that create lots of scopes (apps with large tables) the uid generation
shows up in the profiler and adds a few milliseconds. Using simple counter
doesn't have this overhead.

I think the initial fear of overflowing and thus using string alphanum sequence
is unjustified because even if an app was to create lots of scopes non-stop,
you could create about 28.6 million scopes per seconds for 10 years before
you would reach a number that can't be accurately represented in JS

BREAKING CHANGE: Scope#$id is now of time number rather than string. Since the
id is primarily being used for debugging purposes this change should not affect
anyone.
2014-06-13 11:35:26 -07:00
XrXr 9971fbb3e0 docs(CONTRIBUTING.md): fix link to unit testing docs
The old link points to a page that doesn't exist

Closes #7830
2014-06-13 13:56:01 -04:00
Lucas Galfaso 600a41a7b6 fix($parse): Handle one-time to null
Handles when a one-time binding stabilizes to `null`

Closes #7743
Closes #7787
2014-06-13 09:05:19 -07:00
Peter Bacon Darwin 398053c563 fix($compile): ensure transclude works at root of templateUrl
If a "replace" directive has an async template, which contains a transclusion
directive at its root node, then outer transclusions were failing to be
passed to this directive.  An example would be uses of `ngIf` inside and
outside the template.

Collaborated with @caitp

Closes #7183
Closes #7772
2014-06-13 14:28:47 +01:00
Matias Niemelä 0ebab08e66 docs(forms): additional documentation for $touched vs $pristine states 2014-06-12 21:46:39 -04:00
Matias Niemelä 1be9bb9d35 fix(NgModel): ensure pattern and ngPattern use the same validator
When the pattern and ng-pattern attributes are used with an input element
containing a ngModel directive then they should both use the same validator
and the validation errors of the model should be placed on model.$error.pattern.

BREAKING CHANGE:

If an expression is used on ng-pattern (such as `ng-pattern="exp"`) or on the
pattern attribute (something like on `pattern="{{ exp }}"`) and the expression
itself evaluates to a string then the validator will not parse the string as a
literal regular expression object (a value like `/abc/i`).  Instead, the entire
string will be created as the regular expression to test against. This means
that any expression flags will not be placed on the RegExp object. To get around
this limitation, use a regular expression object as the value for the expression.

    //before
    $scope.exp = '/abc/i';

    //after
    $scope.exp = /abc/i;
2014-06-12 21:18:36 -04:00
Matias Niemelä 26d91b653a fix(NgModel): make ngMinlength and ngMaxlength as standalone directives
Fixes #6750
2014-06-12 21:18:24 -04:00
Matias Niemelä 5b8e7ecfeb fix(NgModel): make sure the ngMinlength and ngMaxlength validators use the $validators pipeline
Fixes #6304
2014-06-12 21:17:03 -04:00
Matias Niemelä e63d4253d0 fix(NgModel): make sure the pattern validator uses the $validators pipeline 2014-06-12 21:16:36 -04:00
Matias Niemelä e53554a0e2 fix(NgModel): make sure the required validator uses the $validators pipeline
Fixes #5164
2014-06-12 21:16:16 -04:00
Matias Niemelä a8c7cb81c9 feat(NgModel): introduce the $validators pipeline 2014-06-12 21:16:02 -04:00
Tero Parviainen 545d22b470 fix($injector): report circularity in circular dependency error message
Change the error message for a circular dependency to display the full
circle back to the first service being instantiated, so that the problem
is obvious. The previous message stopped one dependency short of the full
circle.

Changes the content of the cdep error message, which may be considered
a breaking change.

Closes #7500
2014-06-12 17:23:09 -07:00
Brian Ford 3de07aa2eb docs(TRIAGING.md): clarification for issues that are not reproducable 2014-06-12 14:38:52 -07:00
Aiden N abf31ae624 docs(guide/bootstrap): fix link to angular.js script in example
code.angularjs.org/angular.js is 404, updated it

Updated http://code.angularjs.org/angular.js in the example to http://code.angularjs.org/snapshot/angular.js
It works fine now.

Closes #7807
2014-06-12 14:24:03 -04:00
Michal Kawalec dd1d189ee7 perf($http): move xsrf cookie check to after cache check in $http
$http was previously checking cookies to find an xsrf-token prior to checking
the cache. This caused a performance penalty of about 2ms, which can be very
significant when loading hundreds of template instances on a page.

Fixes #7717
2014-06-12 10:28:17 -07:00
fuqcool b32d0f8649 docs(guide/services): fix link to services api
Closes #7795
2014-06-12 00:09:56 -04:00
Dave Wells 2d5a84963e docs(errors/$compile/nonassing): fix reversed attribute and scope property names 2014-06-11 14:51:37 -07:00
Arturo Guzman adcc5a00bf feat(input): add $touched and $untouched states
Sets the ngModel controller property $touched to True and $untouched to False whenever a 'blur' event is triggered over a control with the ngModel directive.
Also adds the $setTouched and $setUntouched methods to the NgModelController.

References #583
2014-06-10 23:06:31 -04:00
Alexander Karpan 94bcc03f3e docs(guide/services): fix link to wikipedia article containing parentheses
Fix in a mis-parsed link with ')' symbol in it

Closess #7775
2014-06-10 16:10:21 -04:00
fvanderwielen d8e4093b5a docs(loader): improve explanation of modules 2014-06-10 11:47:56 -07:00
Buu Nguyen 2cde927e58 fix($compile): always error if two directives add isolate-scope and new-scope
Previously, the compiler would throw an error if a directive requested new non-isolate scope
after a directive had requested isolate scope. But it would not error if a directive
requested an isolate scope after a directive had requested a new non-isolate scope.

Since it is invalid to have more than one directive request any kind of scope if one of
them has requested isolate scope, then the compiler should error whatever order the
directives are applied.

This fix addresses this situation by throwing error regardless of order of directives.

BREAKING CHANGE:

Requesting isolate scope and any other scope on a single element is an error.
Before this change, the compiler let two directives request a child scope
and an isolate scope if the compiler applied them in the order of non-isolate
scope directive followed by isolate scope directive.

Now the compiler will error regardless of the order.

If you find that your code is now throwing a `$compile:multidir` error,
check that you do not have directives on the same element that are trying
to request both an isolate and a non-isolate scope and fix your code.

Closes #4402
Closes #4421
2014-06-10 12:08:55 +01:00
Rocky Assad 73e3e8551c chore(jshint): dedupe jshint option 2014-06-10 01:46:12 -07:00
Brian Ford 0675938931 docs(tutorial/step_09): fix formatting 2014-06-10 01:36:46 -07:00
Brian Ford b21122002a docs(tutorial/step_09): fix link to filter guide 2014-06-10 01:34:41 -07:00
Kevin Western b6cb045627 docs($animate): $animate.enabled's param is optional
The docs show that param 'element' (of type DOMElement) is required when it is optional.
2014-06-10 01:07:19 -07:00
Amar Patel ac3f0d0b58 docs(api/index): add Oxford comma
Verified grammar at https://owl.english.purdue.edu/owl/resource/607/01/
2014-06-10 00:57:21 -07:00
Dylan Semler 650f14eb28 docs(error/$compile/tplrt): note that html comments can cause this 2014-06-10 00:52:15 -07:00
Carl Sutherland 56084b8718 docs(directive/input): document ngTrim for textarea 2014-06-10 00:36:42 -07:00
Matt Johansen c6088da00f docs(tutorial/step_09): note about 'Filter' suffix
Reminder that 'Filter' is appended to filter names when injected.
Link to Filter guide where this is mentioned.
2014-06-09 21:44:48 -07:00
Caitlin Potter e4419daf70 feat(ngInclude): emit $includeContentError when HTTP request fails
This adds a scope event notification when a template fails to load.

This can have performance implications, and unfortunately cannot at this moment
be terminated with preventDefault(). But it's nice to be notified when problems
occur!

Closes #5803
2014-06-09 21:20:01 -07:00
Stephen Nancekivell 63ea0c1aac docs(error/$sce/unsafe): suggest including ngSanatize 2014-06-09 19:53:05 -07:00
Matias Niemelä d9b90d7c10 feat(attrs): trigger observers for specific ng-attributes
When an observer is set to listen on the pattern, minlength or maxlength attributes
via $attrs then the observer will also listen on the ngPattern, ngMinlength and the
ngMaxlength attributes as well.

Closes #7758
2014-06-09 21:48:07 -04:00
Arjunkumar 8fddaa23c8 docs(guide/index): add codeschool link 2014-06-09 15:50:18 -07:00
Uri Goldshtein a958bd88a4 docs(guide): add UI-Map to Complementary Libraries 2014-06-09 14:30:36 -07:00
Uri Goldshtein e2f339e044 docs(guide): add ngTagsInput to UI Widgets 2014-06-09 14:01:13 -07:00
Joseph Orbegoso Pea 63b3060808 docs(ngController): improve wording 2014-06-09 13:39:55 -07:00
Caitlin Potter 3df2ccae0f docs(CHANGELOG.md): remove mention of strict-DI from v1.2.17
Strict-DI feature was originally merged into v1.2.x, but was reverted by https://github.com/angular/angular.js/commit/373078a94cf3d525b9ae11a2f2876acb6e26f6a3
2014-06-09 00:00:18 -04:00
Nikita Vasilyev 1064686599 docs(guide/databinding): add line breaks after images
This looks much better, thanks a bunch @NV

Closes #7748
2014-06-08 22:12:42 -04:00
Peter Bacon Darwin 4124a653d9 docs(error/$rootScope/inprog): improve understanding and diagnosis of the error
See #5549
2014-06-08 19:33:57 +01:00
Sebastian Müller 751ebc17f7 perf(isArray): use native Array.isArray
see benchmark: http://jsperf.com/isarray-performance

Closes #7735
2014-06-06 20:06:38 -04:00
Peter Bacon Darwin 560f00860d docs(tutorial): mention additional Debian install step
Thanks to GSC Leticia (gsc-leticia) for identifying this problem.

Closes #7665
2014-06-06 21:27:23 +01:00
Peter Bacon Darwin ebf59b4206 chore(CHANGELOG.md): add changes for 1.2.17 and 1.3.0-beta-11 2014-06-06 20:22:50 +01:00
Samuel Rats 808dab83e5 docs($http): remove duplicate 'to' in withCredentials description
Typo in $http config documentation

Closes #7731
2014-06-06 11:12:33 -04:00
Caitlin Potter ff791c9330 test($http): test that timed out $http request rejects promise
Closes #7688
Closes #7686
2014-06-05 20:12:26 -04:00
rodyhaddad c97c5cec2f test(jqLite): adapt missed test to new expando name 2014-06-05 14:45:26 -07:00
rodyhaddad ee8fae8c0f test(jqLite): adapt tests to new expando name 2014-06-05 14:14:48 -07:00
rodyhaddad 1ec6d551bd chore(jqLite): remove special characters from the expando property
Having special characters in the expando property created a memory bloat.
See https://code.google.com/p/chromium/issues/detail?id=378607#c6 to reproduce

Closes #7701
2014-06-05 13:47:32 -07:00
Matias Niemelä 1d90744f40 fix(ngShow): ensure that the display property is never set to block
Fixes #7707
2014-06-05 14:35:28 -04:00
rodyhaddad ff0369883e refactor($parse): remove unused parameters and methods
After removing the json and unwrapPromise mode, some parameters
and methods were no longer used.
2014-06-04 15:47:52 -07:00
Lucas Galfaso 6fb121e64c refactor(parse): simplify the Parser's filter function 2014-06-04 15:47:52 -07:00
rodyhaddad f7a0a386ee refactor($parse): don't make the lexer mark tokens as literals 2014-06-04 15:47:52 -07:00
Vojta Jina a5df219031 chore(travis): run npm install twice
This is not optimal solution at all, but I can’t think of any better.
2014-06-04 14:31:43 -07:00
Wes Alvaro aa26856006 docs(misc core): Use @kind function instead of @function. 2014-06-04 20:22:10 +01:00
Matias Niemelä 5044238561 docs(ngShow): calirfy info about the .ng-hide class and the display style 2014-06-04 15:19:34 -04:00
Matias Niemelä 7c011e79d8 fix($animate): remove the need to add display:block!important for ngShow/ngHide
Since ngShow/ngHide animations add and remove the .ng-hide class, having to remember
to write display:block on your own is a hassle and leads to problematic animation
code. This fix places a default on the animation for you instead.

Closes #3813
2014-06-04 14:45:17 -04:00
Richard Littauer 36b2bba05f docs(guide/directive): explain controllerAs option
There was nothing about controllerAs in the developer guide.

Helps #6710 and #6759
2014-06-03 17:56:37 -07:00
Juan Manuel Palacios 45fed49abf docs(tutorial/step_12): clarify that jQuery should be loaded first
jQuery needs to be loaded before *all* AngularJS modules in the app,
because otherwise AngularJS will not detect the presence of jQuery and
animations  will not work as expected.
2014-06-03 17:40:33 -07:00
rodyhaddad d3c50c8456 feat($resource): allow props beginning with $ to be used on resources
BREAKING CHANGE:

If you expected `$resource` to strip these types of properties before,
you will have to manually do this yourself now.
2014-06-03 17:16:18 -07:00
rodyhaddad c054288c97 fix(angular.toJson): only strip properties beginning with $$, not $
BREAKING CHANGE:

If you expected `toJson` to strip these types of properties before,
you will have to manually do this yourself now.
2014-06-03 17:15:58 -07:00
Jake Buob 8c02a49bb4 docs(tutorial/step_07): fix typo 2014-06-03 16:37:52 -07:00
Dken b5d2a2a813 docs(tutorial/index): update requisite version of node
Not all 0.10.x support `^` in versions in `package.json`
2014-06-03 16:30:11 -07:00
Richard Littauer 46a53b22d0 docs(misc/faq): note that jQuery 2.x currently doesn't work with angular 2014-06-03 16:27:56 -07:00
JMRodriguez24 b12bd05057 docs(index/api): fix link o templates
Link points to templates. Modified the link label templates instead of types.
2014-06-03 16:25:14 -07:00
Tim Ruffles 011914121c docs(guide/expression): fix docs re $window
The documentation on context is incorrect and misleading:

1. "Angular expressions must use $window explicitly to refer to the global
   `window` object": expressions cannot access `$window`
1. The example doesn't actually attempt to use $window in a expression. It's in a
   function called from an expression, which incorrectly implies to readers that:
  1. functions ARE expressions
  1. functions called by expressions can't access `window`

Here's [a plunkr](http://plnkr.co/edit/Gd4xAV?p=preview) to make both these issues clear.

This change fixes the errors and informs the reader about Angular's `$window` etc services,
and adds an explicit example of an expression not being able to access `window`.
2014-06-03 16:20:59 -07:00
Anuj More 847b9705cb docs(tutorial/index): fix typo 2014-06-03 15:58:35 -07:00
Oivvio Polite 575b197c27 docs(guide/providers): fix typo 2014-06-03 15:39:49 -07:00
Oivvio Polite cc495e136d docs(guide/providers): fix grammar 2014-06-03 14:32:33 -07:00
Pavel Pomerantsev f165e08b16 refactor(bootstrap): remove an unused argument
The $animate service is injected, but not used within the code anymore.

Closes #7649
2014-06-02 17:42:11 -04:00
Isaac Shapira 222d47370e fix(ngAnimate): $animate methods should accept native dom elements 2014-06-02 13:40:12 -07:00
cexbrayat 24c844df3b fix($parse): fix parsing error with leading space and one time bind
Closes #7640
2014-06-02 13:23:21 -07:00
Caitlin Potter 0eb2c2af82 chore(ngLocale): update ngLocale scripts
This is a batch of updated ngLocale scripts, based on the recent fixes.
2014-06-02 16:20:05 -04:00
Caitlin Potter cbab51cac5 fix(ngLocale): fix i18n code-generation to support get_vf_, decimals_, and get_wt_
The updated Closure I18N code relies on these methods to enhance the localization quality.

This fix prevents ngLocale files from referencing undefined values. In the short term, this
means adding references to versions of these methods in locales where they are necessary.
2014-06-02 16:20:05 -04:00
Caitlin Potter 36a0e59dec chore(ngLocale): change update-closure script to pull from github, not code.google.com
The closure-library migrated a while ago, so the script would fail. This is change
should allow us to update these more frequently.

Closes #7668
2014-06-02 16:19:58 -04:00
Phil Westwell 0dc35efd7c docs(*): fix its/it's grammar
Closes #7580
2014-05-31 18:43:26 -04:00
zainengineer 4a57b1501e docs(orderBy): add example of directly calling $filter('orderBy')
It's not a bad example of sorting fields in a table, which is something people are frequently wanting
to do. So I say, LGTM!
                        ~caitp, 1988-2014

Closes #7602
2014-05-31 18:24:56 -04:00
Caitlin Potter 16aa3aff51 docs(tutorial): fix minor errors in step 11
I added these when amending the previous commit. My bad ._.
2014-05-31 17:37:54 -04:00
aschokking 42a367df81 docs(tutorial): add karma config update reference to step 11
When including the ng-resource module you appear to need to add a reference to the karma config file
as well or the unit tests will fail. This burned me for a while when going through the tutorial.

Closes #7651
2014-05-31 17:33:03 -04:00
Jan 55818331a8 docs(tutorial): add instructions to make experiment render readably in step 5
Update step_05.ngdoc

Closes #7653
2014-05-31 13:19:59 -04:00
dumpweed 10406de450 docs(tutorial/step_05): improve explanation of _-prefix 2014-05-30 17:27:42 -07:00
ImaginaryDevelopment dd7b508326 docs(ngKeyup): improve example
show implicit `$event` argument
2014-05-30 16:18:24 -07:00
Hallvard Nygård 3252a50165 docs(CONTRIBUTING.md): fixing link to Google Closure I18N library
The old seems to link to the source code of I18N. Found the same folder on their new Github repo.

"Closure Library's source repository has moved to GitHub.", https://code.google.com/p/closure-library/

Closes #7638
2014-05-30 16:06:14 -04:00
Caitlin Potter a594fa523f style(ngClassSpec): fix indentation to make jscs happy 2014-05-30 15:13:05 -04:00
Shahar Talmi 7eaaca8ef2 fix(ngClass): support multiple classes in key 2014-05-30 11:53:50 -07:00
rodyhaddad 083f496d46 fix(angular.copy): support circular references in the value being copied
Closes #7618
2014-05-30 11:45:57 -07:00
rodyhaddad a87135bb43 chore(shallowCopy): handle arrays and primitives, and switch to using it where possible
In many cases, we want a shallow copy instead of a full copy

Closes #7618
2014-05-30 11:45:47 -07:00
Paul Jolly 11170aee42 docs($rootScope): fix incorrect docs about how dirty checking is done 2014-05-30 10:32:29 -07:00
Firexion 6341224d60 docs(tutorial): make code samples in step 7 match code in angular-phonecat
Update step_07.ngdoc

Modifying the code snippets provided to match what was on git

Closes #7521
2014-05-30 09:57:05 -04:00
Andrew Kulinich 9f5c437048 fix(ngAnimate): fix property name that is used to calculate cache key
Fix property name that introduced a bug that occurs when there are 2 animations per page
with similar signature. Due to mistype they were assigned same cache key so second
animation was processed incorrectly

Closes #7566
2014-05-30 01:04:07 -04:00
Vojta Jina 440be33d48 test($compile): transcludeFn is available in compile of templateUrl directive 2014-05-29 12:54:44 -07:00
Vojta Jina 56c60218d1 fix($compile): bound transclusion to correct scope
Nested isolated transclude directives.

This improves/fixes the fix in d414b78717.

See the changed ng-ifunit test: The template inside ng-if should be bound to the
isolate scope of `iso` directive (resp. its child scope). Not to a child of
the root scope. This shows the issue with ng-if. It’s however problem with
other directives too.

Instead of remembering the scope, we pass around the bound parent transclusion.
2014-05-29 12:54:03 -07:00
Vojta Jina 0c8a2cd2da fix($compile): set the iteration state before linking
This issue was introduced in b87e5fc092.
The state for each row has to be set up *before* linking.

The cloneFn (the function passed into $transclude) is called *before* actual linking and thus it is enough to update the state inside the cloneFn callback.
2014-05-29 12:53:06 -07:00
Peter Bacon Darwin 2ee29c5da8 fix($compile): don't pass transcludes to non-transclude templateUrl directives 2014-05-29 12:52:12 -07:00
Peter Bacon Darwin e3003d5342 style(ngRepeat): jshint was complaining about var names 2014-05-29 12:52:12 -07:00
Peter Bacon Darwin b87e5fc092 fix(ngRepeat): ensure that the correct (transcluded) scope is used 2014-05-29 12:52:12 -07:00
Peter Bacon Darwin d71df9f83c fix(ngIf): ensure that the correct (transcluded) scope is used 2014-05-29 12:52:12 -07:00
Peter Bacon Darwin 19af039745 fix($compile): don't pass transclude to template of non-transclude directive
If a directive provides a template but is not explicitly requesting transclusion
then the compiler should not pass a transclusion function to the directives
within the template.
2014-05-29 12:52:12 -07:00
Peter Bacon Darwin 6e15462267 refactor($compile): no need to use bind 2014-05-29 12:52:12 -07:00
Peter Bacon Darwin d414b78717 fix($compile): fix nested isolated transclude directives
Closes #1809
Closes #7499
2014-05-29 12:52:11 -07:00
Peter Bacon Darwin ad0c5107ba refactor($compile): change parameter name
The boundTransclusionFn that is passed in is really the one from the
parent node.  The change to parentBoundTranscludeFn clarifies this compared
to the childBoundTranscludeFn.
2014-05-29 12:52:11 -07:00
Peter Bacon Darwin 1fef5fe823 fix($compile): pass transcludeFn down to nested transclude directives
If you have two directives that both expect to receive transcluded content
the outer directive works but the inner directive never receives a
transclusion function. This only failed if the first transclude directive
was not the first directive found in compilation.

Handles the regression identified in e994259739

Fixes #7240
Closes #7387
2014-05-29 12:52:11 -07:00
Peter Bacon Darwin bea77e4b15 style($compileSpec): fix typos 2014-05-29 12:52:11 -07:00
Vojta Jina c4b04cec74 style(ngTouch): make lint happy 2014-05-29 12:51:48 -07:00
Carlo s A. Guillen 84218af475 docs(ngClick): improve style 2014-05-28 18:02:50 -07:00
Pavel Pomerantsev 7a9d24551f docs($rootScope): fix incorrect docs and make them clearer
During the first $digest loop after registering a $watch
the listener always run, so the example was incorrect

Closes #7598
2014-05-28 17:38:09 -07:00
Erin Altenhof-Long 0dbec22bc7 docs(ngClick): improve style 2014-05-28 17:29:33 -07:00
Brian Ford 279f00b05b docs($http): use HTTPS URLs in JSONP example 2014-05-28 14:04:22 -07:00
venticello 4eb9522f90 docs(tutorial/step-12): fix typo
Closes #7596
2014-05-27 23:46:05 -07:00
Peter Bacon Darwin 2859fc408e docs(ngController): add formatting to controller as description and example
Adds to #7591
2014-05-27 22:43:37 +01:00
Richard Littauer 462eefc1e4 docs(ngController): add more description of controller as syntax
Using `controller as` in the template is not described well
in the docs, as both `scope` injection and `this` are presented
equally without too much discussion of the advantages of using
either. I added a bit more discussion based on google's internal
style guidelines.

Closes #7591
Closes #5076 (until Angular 2.0 comes out and we refactor everything)
2014-05-27 22:06:17 +01:00
Richard Littauer 3fc95e06e7 docs(ngView): remove reference to scope
$route.name.scope.current returns undefined in the docs example,
as scope is never injected into the relevant controller.
Scope doesn't need to be there, so it's best to just remove it.

Suggested in #5076.
2014-05-24 13:41:37 -07:00
Igor Minar 3ec4af9413 chore(parseSpec): fix bad merge 2014-05-23 15:08:36 -07:00
Brian Ford a96b659770 docs(changelog): release notes for 1.3.0-beta.10 excessive-clarification 2014-05-23 14:57:10 -07:00
Matias Niemelä 98b9d68ea3 fix($animate): retain inline styles for property-specific transitions
Transitions that are run through ngAnimate which contain a specific property
cause any inline styles to be erased after the animation is done. This has
something to do with how the browsers handle transitions that do not use
"all" as a transition property.

Closes #7503
2014-05-23 14:45:20 -07:00
Lucas Galfaso cee429f0aa feat(*): lazy one-time binding support
Expressions that start with `::` will be binded once. The rule
that binding follows is that the binding will take the first
not-undefined value at the end of a $digest cycle.

Watchers from $watch, $watchCollection and $watchGroup will
automatically stop watching when the expression(s) are bind-once
and fulfill.

Watchers from text and attributes interpolations will
automatically stop watching when the expressions are fulfill.

All directives that use $parse for expressions will automatically
work with bind-once expressions. E.g.

<div ng-bind="::foo"></div>
<li ng-repeat="item in ::items">{{::item.name}};</li>

Paired with: Caitlin and Igor
Design doc: https://docs.google.com/document/d/1fTqaaQYD2QE1rz-OywvRKFSpZirbWUPsnfaZaMq8fWI/edit#
Closes #7486
Closes #5408
2014-05-23 14:40:51 -07:00
Julie 701ed5fdf6 tests(docsAppE2E): fix race condition flake with switching to new frame
Closes #7569
2014-05-23 11:28:53 -07:00
rodyhaddad fa6e411da2 fix($parse): remove deprecated promise unwrapping
The feature has been deprecated in #4317

BREAKING CHANGE: promise unwrapping has been removed.
It has been deprecated since 1.2.0-rc.3.

It can no longer be turned on.
Two methods have been removed:
* $parseProvider.unwrapPromises
* $parseProvider.logPromiseWarnings
2014-05-22 22:50:34 -07:00
Juan Manuel Palacios 3f365f5238 docs(tutorial): match html from angular-phonecat in step 7 instructions
Place phoneId binding in a proper HTML node

The code where the phoneId binding in the phone-detail.html template is first explained in step 7
of the tutorial doesn't make it a child of a proper HTML node, which makes the end-to-end test
against the view (also introduced in step 7) fail.

The test acquires the binding right from the view (by.binding('phoneId')), and apparently this
operation fails when the binding is not a child of an HTML node, and therefore the entire test also
fails. As soon as the binding is placed inside a <span></span> tag pair, the binding is found and
the test passes. The code on github for step 7 has it right, the binding is within the span tags,
but in the documentation I'm patching here the span's are missing.

☆.。.:*・゜☆ MERCI ☆.。.:*・゜☆

Closes #7561
2014-05-23 01:31:46 -04:00
Julie e9fad96c7c chore(grunt): check files in src for ddescribe/iit
Previously, only files in test/ were checked. This does not capture
end to end tests, which are in comments in src/.
2014-05-22 18:08:07 -07:00
Julie 7e1950e9f1 chore(tests): remote iit that sneaked into ngStyle 2014-05-22 18:00:46 -07:00
Julie abaf9466ac chore(travis/e2e): update protractor to 0.23.1 2014-05-22 17:49:31 -07:00
rodyhaddad 1d37239eaf refactor($parse): remove the support of JSON parsing mode
It's a feature that isn't exposed to the public, and is
no longer used internally.
2014-05-22 15:18:37 -07:00
Zorigt Bazarragchaa f37a2dab7f docs(tutorial/index): explain E2E acronym
It was confusing to read "end 2 end" as a numeric two. I kept wondering what two end(s).
Later in the tutorial, the text switched to "End to End" which made more sense than numeric two.
2014-05-22 13:19:40 -07:00
Jesse Palmer f694951b3f style: added whitespace
spaces between the ')' and '{' make code a tad easier to read.

Closes #7542
2014-05-22 08:50:26 -07:00
Max Tobias Weber 6e2ab81937 docs(tutorial): update code snippet for step 7
replaced bootstrap 2 with bootstrap 3 classes

Closes #7433
2014-05-21 17:10:30 -07:00
Kevin Aenmey 1445130869 docs($cacheFactory): fix typo 2014-05-21 16:28:07 -07:00
Tobias Bosch eec6394a34 docs($compile): deprecate replace directives
BREAKING CHANGE:

The `replace` flag for defining directives that
replace the element that they are on will be removed in the next
major angular version.
This feature has difficult semantics (e.g. how attributes are merged)
and leads to more problems compared to what it solves.
Also, with WebComponents it is normal to have custom elements in the DOM.
2014-05-21 16:08:02 -07:00
Jeff Whelpley 1ab6e908b1 fix($compile): do not merge attrs that are the same for replace directives
If a directives specifies `replace:true` and the template of the directive contains
a root element with an attribute which already exists at the place
where the directive is used with the same value, don't duplicate the value.

Closes #7463
2014-05-21 15:51:19 -07:00
Jesse Palmer 40b8721ba3 docs($interpolate): removed function name to match other examples 2014-05-21 15:49:24 -07:00
Freek Wielstra 1fe0d9990e docs($http): clear up Interceptor docs and code
The documentation and code example of $http interceptors is unclear about whether config can be null
or not, and whether the result should always be a promise or not. This pr clears up the documentation
a bit and removes the literal 'or a promise' interpretation of the docs in the code example.

Closes #7431
Closes #7460
2014-05-21 15:35:55 -07:00
NateRedding f2f5a1dd39 docs(ngHide): corrected logic in example
Closes #7436
2014-05-21 15:26:29 -07:00
Peter Bacon Darwin b696822c1c docs(misc/downloading): fix HTML example formatting
Closes #7060
2014-05-21 22:33:43 +01:00
Brice b5fb2d4bec docs(tutorial): improve instructions for causing test failure (step 2)
Increase readability for test fail line

Closes #6943
2014-05-21 17:32:04 -04:00
specialorange 6c3a0519fa docs(error/$injector/unpr): clarify which code is being referred to in the text
Closes #7020
2014-05-21 22:26:43 +01:00
Edward Brey 510c328d74 docs(filter): description formatting fix
as it was, the third parameter's description was displayed as
a code block, rather than a bulletted list.
2014-05-21 13:57:25 -07:00
Tim van den Eijnden d3163ff1cf docs(tutorial): fix controller unit tests to match phonecat / avoid ReferenceError
fix 'ReferenceError: PhoneDetailCtrl is not defined' & 'ReferenceError: PhoneListCtrl is not defined' in Karma unit tests

Closes #7421
2014-05-21 16:37:47 -04:00
Adam Bradley b64c709808 docs(css): remove Firefox select element drop-down icon
In firefox the version picker's dropdown icon from the default `select` element
is still showing. This CSS forces FF to hide the ugly default.

Closes #6878
2014-05-21 21:33:56 +01:00
kalvn 0a3268f106 docs(tutorial): corrected version of jquery installed via bower (step 12)
Corrected the version of jquery in the text content.

Closes #7441
2014-05-21 16:29:57 -04:00
James Vanneman 636b3799b3 docs($filter): add runnable example
Closes #6871
2014-05-21 21:26:20 +01:00
Richard Littauer ff16d224ca test(ngClass): add missing assertions 2014-05-21 13:23:30 -07:00
ABitTooCalm d02fa32f36 docs(ngStyle): clarify using styles that are not valid property names
Closes #6838
2014-05-21 21:07:13 +01:00
Andrew Mortimer f5f1fd1963 docs(guide/controller): fix typo 2014-05-21 12:31:26 -07:00
Kristof Mattei 84402b11c8 docs(shallowCopy): fixed typo
Closes #7456
2014-05-21 11:41:57 -07:00
Tobias Bosch d606e66f7a docs(ngModelController): use $sce and $sanitize in the contenteditable example.
Closes #7464
2014-05-21 11:36:34 -07:00
Tobias Bosch 326fd809c8 docs(): mention implicit usage of 2014-05-21 11:23:38 -07:00
markau bdfbceb3d3 docs(guide/concepts): The service is finance2.js
Closes #7470
2014-05-21 10:47:21 -07:00
Tobias Bosch f9264eec07 docs(guide/concepts): clarify definitions 2014-05-21 10:47:09 -07:00
Sergei Z 854bf5b74d feat(ngMock): add support of mocha tdd interface
Closes #7489
2014-05-21 10:43:32 -07:00
Erich 9fb0e16719 docs(guide/concepts): clarify definitions
Closes #7483
2014-05-21 10:33:11 -07:00
Sam Dornan 20221f9582 docs(guide/services): fix link to minify Wikipedia page
Closes #7488
2014-05-21 10:29:13 -07:00
Yaron Uliel 7e4598663a docs(ngRepeat): fix formatting 2014-05-21 10:27:37 -07:00
Takashi Nakagawa 21a3cea657 docs(ngModelController): fix indentation code block
Closes #6660
2014-05-21 17:09:37 +01:00
Caitlin Potter d8c55864d0 docs(guide/$location): clarify "$watchers" / "$observers" terminology with links
Add links to API docs for Scope#$watch and Attributes#$observe in $location in order to clarify
the meaning of that terminology.

Closes #7497
2014-05-21 11:58:52 -04:00
Firexion 92f588cd15 docs(tutorial): add <span> to index.html to match phonecat step4
Add missing <span> to app/index.html to match actual code on github

☆.。.:*・゜☆ Merci beaucoup! ☆.。.:*・゜☆

Closes #7520
2014-05-21 11:28:17 -04:00
GSC Leticia ef13820cad docs(tutorial): add instructions for running node in Debian-based distributions
Specifying command for Debian based distributions, like Ubuntu, Mint, etc.

☆.。.:*・゜☆ Fantastique! ☆.。.:*・゜☆

Closes #7422
2014-05-21 11:20:20 -04:00
Sam Dornan 6cb35adfe8 chore(rootScope): fix grammar in a test description
it's -> its

☆.。.:*・゜☆ Yeah, why not ☆.。.:*・゜☆

Closes #7534
2014-05-21 10:41:47 -04:00
Peter Bacon Darwin e3375ae37e docs(guide/expression): mention the ternary operator
Closes #6498
2014-05-21 14:46:05 +01:00
Peter Bacon Darwin 826450da20 docs($compile): remove reference to ngRepeat providing compile function
ngRepeat no longer has the compile function in its directive definition
object, since it retrieves its transclusion via the link function.

Closes 5638
2014-05-21 14:27:06 +01:00
Peter Bacon Darwin 16a7d54351 docs(tutorial/step-5): update image to match code
Closes #2753
2014-05-21 13:58:36 +01:00
Eli Dalbey 9332439d1d docs(guide/concepts-module-service.png): Typo in image
The ng-app was called ng=app in diagram image.

Closes #6473
Closes #7528
2014-05-21 12:51:13 +01:00
Wes Alvaro ad0d3e0f1b docs(ngRoute): $routeParams is Object.<string, string>
Closes #7386
2014-05-21 12:48:37 +01:00
Igor Minar 82f45aee5b fix(Scope): $broadcast and $emit should set event.currentScope to null
When a event is finished propagating through Scope hierarchy the event's `currentScope` property
should be reset to `null` to avoid accidental use of this property in asynchronous event handlers.

In the previous code, the event's property would contain a reference to the last Scope instance that
was visited during the traversal, which is unlikely what the code trying to grab scope reference expects.

BREAKING CHANGE: $broadcast and $emit will now reset the `currentScope` property of the event to
null once the event finished propagating. If any code depends on asynchronously accessing thei
`currentScope` property, it should be migrated to use `targetScope` instead. All of these cases
should be considered programming bugs.

Closes #7445
Closes #7523
2014-05-21 00:20:20 -07:00
Ralph Giles def5b57de8 docs(*): fix its vs it's typos.
Only use the apostrophe if you can expand "it's" to "it is" and
still have a grammatical phrase.

Closes #7524
2014-05-20 17:45:08 -04:00
Caitlin Potter 6b5284807a docs($interpolate): replace jerk with something nicer/more neutral
I thought `jerk` illustrated the "defacing" factor nicely, but I suppose it's not very nice to have
in official documentation.
2014-05-20 16:04:21 -04:00
Caitlin Potter e3f78c17d3 feat($interpolate): escaped interpolation expressions
This CL enables interpolation expressions to be escaped, by prefixing each character of their
start/end markers with a REVERSE SOLIDUS U+005C, and to render the escaped expression as a
regular interpolation expression.

Example:

`<span ng-init="foo='Hello'">{{foo}}, \\{\\{World!\\}\\}</span>` would be rendered as:
`<span ng-init="foo='Hello'">Hello, {{World!}}</span>`

This will also work with custom interpolation markers, for example:

     module.
       config(function($interpolateProvider) {
         $interpolateProvider.startSymbol('\\\\');
         $interpolateProvider.endSymbol('//');
       }).
       run(function($interpolate) {
         // Will alert with "hello\\bar//":
         alert($interpolate('\\\\foo//\\\\\\\\bar\\/\\/')({foo: "hello", bar: "world"}));
       });

This change effectively only changes the rendering of these escaped markers, because they are
not context-aware, and are incapable of preventing nested expressions within those escaped
markers from being evaluated.

Therefore, backends are encouraged to ensure that when escaping expressions for security
reasons, every single instance of a start or end marker have each of its characters prefixed
with a backslash (REVERSE SOLIDUS, U+005C)

Closes #5601
Closes #7517
2014-05-20 15:41:20 -04:00
Igor Minar e994259739 revert: fix($compile): pass transcludeFn down to nested transclude directives
This reverts commit 113850602d.

This change introduced regressions for several scenarios.

reduction: http://jsfiddle.net/5242N/4/
2014-05-20 11:00:26 -07:00
Gaëtan PRIOUR f34de0ba35 docs(tutorial): fixed path to controllersSpec.js file
☆.。.:*・゜☆ DOMO ARIGATO MR ROBOTO ☆.。.:*・゜☆

Closes #7504
2014-05-20 12:16:27 -04:00
Vojta Jina 95cdb53e83 chore(travis/e2e): use Chrome on Mac
Because of this[1] issue, which is Linux specific, we see very frequent failures.

[1]: https://code.google.com/p/chromedriver/issues/detail?id=467
2014-05-20 08:46:26 +02:00
Vojta Jina 6708feffdb chore(travis/e2e): update Chrome to v34
See 28ef2637c1
2014-05-20 08:46:26 +02:00
Vojta Jina 6898ac5985 chore(travis): use latest selenium on SL 2014-05-20 08:46:26 +02:00
Vojta Jina 63e13a4df7 chore(travis): use SauceConnect v4
See https://saucelabs.com/docs/connect
2014-05-20 08:46:26 +02:00
Andrey Sidorov 35e53ca649 docs(guide): correct order of directive arguments within the animations chapter
Closes #7482
2014-05-19 14:27:58 -04:00
Igor Minar e927193de0 perf($interpolate): optimize value stringification
previously we stringified numbers via toJson which was expensive, I optimized
the code so that toJson is invoked only if really necessary

Closes #7501
2014-05-19 06:29:07 -07:00
Diego Plentz 0dfbf8ad67 docs(guide/$location) remove duplicate 'Crawling your app' section
The "Crawling your app" section was duplicated.
I just removed the duplication and moved the section to the 'Caveats' section.

Closes #7244
2014-05-19 14:05:46 +01:00
Kebabpizza d8bc44b7e9 docs(tutorial): fix link to bower in step 12
Muchas gracias! \o/

Closes #7507
2014-05-18 20:46:54 -04:00
Shahar Talmi facd904a61 fix(ngModel): do not dirty the input on $commitViewValue if nothing was changed
Calling `$commitViewValue` was was dirtying the input, even if no update to the view
value was made.
For example, `updateOn` triggers and form submit may call `$commitViewValue` even
if the the view value had not changed.

Closes #7457
Closes #7495
2014-05-18 08:35:21 +01:00
Brian Ford 96fa70511d test($compile): use trim helper 2014-05-16 18:18:26 -07:00
Brian Ford 819dd5df92 docs(changelog): release notes for 1.3.0-beta.9 release-naming 2014-05-16 15:14:12 -07:00
Shahar Talmi 5fbd618c2f fix(ngClass): handle index changes when an item is unshifted
Closes #7256
2014-05-16 14:02:41 -07:00
M Alix 63f284a55c chore($compile): make webstorm's regexp parser happy
Minor change puts \- at end of character pattern

In CLASS_DIRECTIVE_REGEXP and COMMENT_DIRECTIVE_REGEXP, putting the \- character at
the end of the character patter speeds up many IDE parsers and alleviates some
errors in certain IDE's. (WebStorm 8)

Functionally absolutely equivalent. No test change needed.

Closes #7093

Conflicts:
	src/ng/compile.js
2014-05-16 13:15:03 -07:00
Vojta Jina 4f03dc5a96 fix($compile): pass transcludeFn down to nested transclude directives
If you have two directives that both expect to receive transcluded content
the outer directive works but the inner directive never receives a
transclusion function. This only failed if the first transclude directive
was not the first directive found in compilation.

Fixes #7240
Closes #7387
2014-05-16 13:08:10 -07:00
Tobias Bosch d16102fa41 chore(release): publish ngMessages to bower 2014-05-16 11:33:02 -07:00
Jonathan Gotti d1f1a1abdd docs(guide/directives): improve links to isolate scope docs
Link "isolate scope" to "Directive Definition object" section

Closes #7484
2014-05-16 11:22:01 -07:00
Zachary Babtkis a4ec1187d6 docs(guide/css-styling): tiny grammar fix
Closes #5443
2014-05-16 14:52:24 +01:00
Peter Bacon Darwin 52cae5b59b docs(search): fix search submit functionality
When the search input box was submitted (i.e. by pressing enter) the
app was supposed to take you to the first item but this was not happening.
It turns out the app was just reading the wrong property for the path to
the item.

Closes #3078
2014-05-16 14:33:09 +01:00
Peter Bacon Darwin 6712ca5a6c docs(search): set minimum length to 2
With the minimum search length set to 3, it was not possible to search for `$q`.
Changing this to 2 fixes that without really upsetting the search display, since we
only display the first 40 API and 14 non-API items anyway.

Closes #3078
2014-05-16 14:23:35 +01:00
nderoche d85c5e949a docs(ngController): clarify that this is $scope in example
Replace `this` with `$scope` in second example to highlight the fact that
we are working with the `$scope` instead of an instance of the controller
in this example.

Closes #6478
2014-05-16 13:16:34 +01:00
Narretz c2c9d524b9 chore(docs): fix layout between 769px and 991px
Left nav and main content were tucked slightly under the version picker / breadcrumb navbar

Closes #6474
Closes #7079
2014-05-16 13:09:21 +01:00
gogotanaka 5aaf98d44f docs(CHANGELOG & CONTRIBUTING): fix trivial formatting
Closes #6407
2014-05-16 12:59:14 +01:00
linclark d5a92d2250 docs($resource): clarify paramDefaults usage
Closes #6200
2014-05-16 12:45:22 +01:00
Peter Bacon Darwin 35a6646a81 docs(guide/services): add reference to ngmin tool
Closes #5908
2014-05-16 12:33:05 +01:00
Lucas Galfaso 81d427b5f0 fix(numberFilter): fix rounding error edge case
Fix a number rounding error.

Closes #7453
Closes #7478
2014-05-15 21:18:59 +01:00
Christopher Rose a84344adb6 docs(guide/di): fix typos
Closes #7480
2014-05-15 20:56:54 +01:00
Caitlin Potter 7a3cbb3c79 test(ngMessages): use strict-di for ngMessages tests
This will hopefully prevent issues similar to the one fixed by 63b100c0

Closes #7477
2014-05-15 11:14:15 -04:00
Caitlin Potter 0282ca971d fix(ngMessages): annotate ngMessages controller for minification 2014-05-15 11:03:42 -04:00
Robin Böhm 5a568b4f96 feat(ngTouch): add optional ngSwipeDisableMouse attribute to ngSwipe directives to ignore mouse events.
This attribute is useful for text that should still be selectable
by the mouse and not trigger the swipe action.

This also adds an optional third argument to `$swipe.bind` to define
the pointer types that should be listened to.

Closes #6627
Fixes #6626
2014-05-14 14:17:19 -07:00
Rich Snapp e9bc51cb09 fix(jqLite): use jQuery only if jQuery.fn.on present
Make Angular not bind to jQuery versions older than 1.7 since older
versions of jQuery do not support necessary on()/off() methods.
2014-05-11 18:58:08 -06:00
Michał Gołębiowski 7f5e0f0bd0 docs(jqLite): explain what monkey-patching jQuery.cleanData does 2014-05-11 12:52:28 +02:00
Michał Gołębiowski d71dbb1ae5 refactor(jqLite): stop patching individual jQuery methods
Currently Angular monkey-patches a few jQuery methods that remove elements
from the DOM. Since methods like .remove() have multiple signatures
that can change what's actually removed, Angular needs to carefully
repeat them in its patching or it can break apps using jQuery correctly.
Such a strategy is also not future-safe.

Instead of patching individual methods on the prototype, it's better to
hook into jQuery.cleanData and trigger custom events there. This should be
safe as e.g. jQuery UI needs it and uses it. It'll also be future-safe.

The only drawback is that $destroy is no longer triggered when using $detach
but:

  1. Angular doesn't use this method, jqLite doesn't implement it.
  2. Detached elements can be re-attached keeping all their events & data
     so it makes sense that $destroy is not triggered on them.
  3. The approach from this commit is so much safer that any issues with
     .detach() working differently are outweighed by the robustness of the code.

BREAKING CHANGE: the $destroy event is no longer triggered when using the
jQuery detach() method. If you want to destroy Angular data attached to the
element, use remove().
2014-05-10 23:32:29 +02:00
Matias Niemelä be7c02c316 docs(changelog): release notes for 1.3.0-beta.8 accidental-haiku 2014-05-09 18:07:24 -04:00
Matias Niemelä 14f5734dbd chore(Gruntfile): remember to minify ngMessages 2014-05-09 17:37:41 -04:00
Shahar Talmi 562c4e424b fix($compile): set $isolateScope correctly for sync template directives
All isolated scope directives that do not have `templateUrl` were marked
as `$isolateScopeNoTemplate` even if they did have a `template` attribute.

This caused `jqLite#scope()` to return the wrong value for child elements
within the directive's template.

Closes #6942
2014-05-09 14:42:26 +01:00
Shahar Talmi a0ae07bd4e feat(FormController): commit $viewValue of all child controls when form is submitted
Use the new `NgModelController.$commitViewValue()` method to commit the
`$viewValue` on all the child controls (including nested `ngForm`s) when the form
receives the `submit` event. This will happen immediately, overriding any
`updateOn` and `debounce` settings from `ngModelOptions`.

If you wish to access the committed `$modelValue`s then you can use the `ngSubmit`
directive to provide a handler.  Don't use `ngClick` on the submit button, as this
handler would be called before the pending `$viewValue`s have been committed.

Closes #7017
2014-05-09 11:50:00 +01:00
Shahar Talmi adfc322b04 refactor(ngModelOptions): move debounce and updateOn logic into NgModelController
Move responsibility for pending and debouncing model updates into `NgModelController`.
Now input directives are only responsible for capturing changes to the input element's
value and then calling `$setViewValue` with the new value.

Calls to `$setViewValue(value)` change the `$viewValue` property but these changes are
not committed to the `$modelValue` until an `updateOn` trigger occurs (and any related
`debounce` has resolved).

The `$$lastCommittedViewValue` is now stored when `$setViewValue(value)` updates
the `$viewValue`, which allows the view to be "reset" by calling `$rollbackViewValue()`.

The new `$commitViewValue()` method allows developers to force the `$viewValue` to be
committed through to the `$modelValue` immediately, ignoring `updateOn` triggers and
`debounce` delays.

BREAKING CHANGE:

This commit changes the API on `NgModelController`, both semantically and
in terms of adding and renaming methods.

* `$setViewValue(value)` -
This method still changes the `$viewValue` but does not immediately commit this
change through to the `$modelValue` as it did previously.
Now the value is committed only when a trigger specified in an associated
`ngModelOptions` directive occurs. If `ngModelOptions` also has a `debounce` delay
specified for the trigger then the change will also be debounced before being
committed.
In most cases this should not have a significant impact on how `NgModelController`
is used: If `updateOn` includes `default` then `$setViewValue` will trigger
a (potentially debounced) commit immediately.
* `$cancelUpdate()` - is renamed to `$rollbackViewValue()` and has the same meaning,
which is to revert the current `$viewValue` back to the `$lastCommittedViewValue`,
to cancel any pending debounced updates and to re-render the input.

To migrate code that used `$cancelUpdate()` follow the example below:

Before:

```
  $scope.resetWithCancel = function (e) {
    if (e.keyCode == 27) {
      $scope.myForm.myInput1.$cancelUpdate();
      $scope.myValue = '';
    }
  };
```

After:

```
  $scope.resetWithCancel = function (e) {
    if (e.keyCode == 27) {
      $scope.myForm.myInput1.$rollbackViewValue();
      $scope.myValue = '';
    }
  }
```
2014-05-09 11:41:38 +01:00
Shahar Talmi 0ef17276e9 refactor(inputSpec): move call to $digest into compileInput helper
It is reasonable to expect a digest to occur between an input element
compiling and the first user interaction.  Rather than add digests to
each test this change moves it into the `compileInput` helper function.
2014-05-09 09:56:27 +01:00
Caitlin Potter ac37915ef6 fix(ngSwitch): properly support case labels with different numbers of transclude fns
Due to a regression introduced several releases ago, the ability for multiple transclude functions
to work correctly changed, as they would break if different case labels had different numbers of
transclude functions.

This CL corrects this by not assuming that previous elements and scope count have the same length.

Fixes #7372
Closes #7373
2014-05-08 17:08:04 -04:00
Richard Littauer 6593d83626 docs(CONTRIBUTING.md): grammar and link fixes
Fix dev-doc link, grammatical error.

Closes #7275
2014-05-08 18:05:52 +02:00
Chris Rose 7700024ef5 docs(CONTRIBUTING.md): anachor fix
Closes #7294
2014-05-08 11:26:44 +02:00
sap9433 4899e781ff docs(CONTRIBUTING.md): anchor fix
"Follow our Coding Rules" should link to "#rules" and not "#coding-rules"

Closes #7349
2014-05-08 11:12:58 +02:00
Joe Pettersson 28af74a901 docs(dateFilter): correct example for date 'fullDate filter'
The equivalent expanded example for 'fullDate' is given as 'EEEE, MMMM d,y'
whereas it should be 'EEEE, MMMM d, y'. With added whitespace.

Closes #7350
2014-05-08 10:53:34 +02:00
Taylor Hutchison 88335fdfcf docs(ngCookies): correct arguments in $cookieStore example
Closes #7390
2014-05-08 10:29:28 +02:00
mjfroehlich 72d63dbcc0 docs(Scope): fix typo in a link
Closes #7389
2014-05-08 10:23:03 +02:00
Thomas Tuts 055b738d4e docs(directive): fix misspelled HTML class for an alert
Muchas gracias

Closes #7381
2014-05-07 07:59:18 -04:00
Matias Niemelä 2faa4781c5 docs(NgMessages): fix up links and headings 2014-05-07 00:52:25 -04:00
barcahead 6f9bcd3307 docs($rootScope): use unshift to add items to the array at the beginning
Closes #7364
2014-05-06 21:29:30 -04:00
Mathew Foscarini e16e7df689 docs(minErr): small grammar fix for $compileMinErr ctreq
Closes #7365
2014-05-06 21:26:31 -04:00
Caitlin Potter 2972de8a44 chore(travis): run protractor tests with ff28
FirefoxDriver seems to have an issue with FF29 which is breaking a test case, and causing false negatives.

There is an issue opened on protractor regarding this at https://github.com/angular/protractor/issues/784

Closes #7369
2014-05-06 18:56:44 -04:00
Peter Bacon Darwin e0e9ccdb79 docs(tutorial): move bower_components into app folder 2014-05-05 20:54:07 +01:00
Jamie Krug 3f540e3d8a docs(tutorial/step-07): fix typo in route pattern
One instance of `/phones/:phoneId` erroneously had a singular version,
`/phone/:phoneId`, which does not match what was actually used in the code.

Closes #7313
2014-05-04 20:52:59 +01:00
Jack Hsu 8b91aa64b7 docs(directives): fix code lang matching
Closes #7339
2014-05-04 20:46:38 +01:00
spacemigas 1c0241e5b2 docs($interpolate): fix bug in example
Closes #7342
2014-05-04 20:37:34 +01:00
Joel Hooks c56e32a7fa fix(ngModelOptions): enable overriding the default with a debounce of zero
Because of how the logic was set up, a value of `0` was assumed to be the
same as `undefined`, which meant that you couldn't override the default
debounce delay with a value of zero.

For example, the following assigned a debounce delay of 500ms to the `blur`
event.

```
ngModelOptions="{ updateOn: 'default blur', debounce: {'default': 500, 'blur':
0} }"
```

Closes #7205
2014-05-04 20:18:27 +01:00
Peter Bacon Darwin 8c08fcfb1b test(ngModelOptions): fix e2e test focussing 2014-05-04 20:07:07 +01:00
Shahar Talmi fbf5ab8f17 fix(ngModelOptions): initialize ngModelOptions in prelink
Input controls require `ngModel` which in turn brings in the `ngModelOptions`
but since ngModel does this initialization in the post link function, the
order in which the directives are run is relevant.

Directives are sorted by priority and name but `ngModel`, `input` and `textarea`
have the same priority. It just happens that `textarea` is alphabetically
sorted and so linked before `ngModel` (unlike `input`).

This is a problem since inputs expect `ngModelController.$options`
to exist at post-link time and for `textarea` this has not happened.

This is solved easily by moving the initialization of `ngModel` to the
pre-link function.

Closes #7281
Closes #7292
2014-05-04 20:07:07 +01:00
raghudodda e395170eef docs(ngModelOptions): correct typo
Closes #7335
2014-05-04 18:59:12 +01:00
Yutaka Yamaguchi 627b0354ec fix(ngSanitize): encode surrogate pair properly
The encodeEndities function encode non-alphanumeric characters to entities with charCodeAt.
charCodeAt does not return one value when their unicode codeponts is higher than 65,356.
It returns surrogate pair, and this is why the Emoji which has higher codepoints is garbled.
We need to handle them properly.

Closes #5088
Closes #6911
2014-05-02 17:48:57 -04:00
Brian Ford 8d18038301 fix(ngSrc, ngSrcset): only interpolate if all expressions are defined
BREAKING CHANGE

If `bar` is `undefined`, before `<img src="foo/{{bar}}.jpg">` yields
`<img src="foo/.jpg">`. With this change, the binding will not set `src`.

If you want the old behavior, you can do this: `<img src="foo/{{bar || ''}}.jpg">`.

The same applies for `srcset` as well.

Closes #6984
2014-05-02 14:06:57 -07:00
Brian Ford c2362e3f45 feat($interpolate): add optional allOrNothing param 2014-05-02 14:06:57 -07:00
Peter Bacon Darwin 2b6c2c5fbd docs(Attributes): ensure code sample is not escaped
Closes #6649
2014-05-02 21:49:15 +01:00
Matias Niemelä 0f4016c84a feat(NgMessages): introduce the NgMessages module and directives
The ngMessages module provides directives designed to better support
handling and reusing error messages within forms without the need to
rely on complex structural directives.

Please note that the API for ngMessages is experimental and may possibly change with
future releases.
2014-05-02 16:31:34 -04:00
Caitlin Potter c0b4e2db9c fix(injector): invoke config blocks for module after all providers
This change ensures that a module's config blocks are always invoked after all of its providers are
registered.

BREAKING CHANGE:

Previously, config blocks would be able to control behaviour of provider registration, due to being
invoked prior to provider registration. Now, provider registration always occurs prior to configuration
for a given module, and therefore config blocks are not able to have any control over a providers
registration.

Example:

Previously, the following:

   angular.module('foo', [])
     .provider('$rootProvider', function() {
       this.$get = function() { ... }
     })
     .config(function($rootProvider) {
       $rootProvider.dependentMode = "B";
     })
     .provider('$dependentProvider', function($rootProvider) {
       if ($rootProvider.dependentMode === "A") {
         this.$get = function() {
           // Special mode!
         }
       } else {
         this.$get = function() {
           // something else
         }
       }
     });

would have "worked", meaning behaviour of the config block between the registration of "$rootProvider"
and "$dependentProvider" would have actually accomplished something and changed the behaviour of the
app. This is no longer possible within a single module.

Fixes #7139
Closes #7147
2014-05-02 14:12:22 -04:00
Caitlin Potter 924ee6db06 fix($interpolate): don't ReferenceError when context is undefined
546cb42 introduced a regression, which would cause the function returned from
$interpolate to throw a ReferenceError if `context` is undefined. This change
prevents the error from being thrown.

Closes #7230
Closes #7237
2014-05-02 13:58:13 -04:00
Peter Bacon Darwin cbc7496c1f chore(doc-gen): update to dgeni 0.3.0 2014-05-02 14:48:45 +01:00
Chris Rose 69d96e8b71 docs($injector): fix typos 2014-05-01 15:35:58 -07:00
mjfroehlich b426424e63 docs(guide/providers): minor edits 2014-05-01 14:39:55 -07:00
Dave Smith e1d6178457 fix($httpBackend): Add missing expectHEAD() method
This was documented but not implemented.

With accompanying unit test to ensure the $httpBackend.expect*() methods exist.

Closes #7320
2014-04-30 18:51:20 -04:00
Janas Page 3fb2d2ac3b docs($anchorScroll): minor copyedit.
Singular-tense verb. Definite article.

Closes #7319
2014-04-30 17:49:28 -04:00
Brian Ford e0d4c42148 chore(package.json): add qq
e0375a61d0 removed qq, but the changelog script still needs it
2014-04-30 14:02:58 -07:00
Caitlin Potter ad4336f935 chore($http): remove deprecated responseInterceptors functionality
Code cleanup! response interceptors have been deprecated for some time, and it is confusing to have
two APIs, one of which is slightly "hidden" and hard to see, which perform the same task. The newer
API is a bit cleaner and more visible, so this is naturally preferred.

BREAKING CHANGE:

Previously, it was possible to register a response interceptor like so:

    // register the interceptor as a service
    $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
      return function(promise) {
        return promise.then(function(response) {
          // do something on success
          return response;
        }, function(response) {
          // do something on error
          if (canRecover(response)) {
            return responseOrNewPromise
          }
          return $q.reject(response);
        });
      }
    });

    $httpProvider.responseInterceptors.push('myHttpInterceptor');

Now, one must use the newer API introduced in v1.1.4 (4ae46814), like so:

    $provide.factory('myHttpInterceptor', function($q) {
      return {
        response: function(response) {
          // do something on success
          return response;
        },
        responseError: function(response) {
          // do something on error
          if (canRecover(response)) {
            return responseOrNewPromise
          }
          return $q.reject(response);
        }
      };
    });

    $httpProvider.interceptors.push('myHttpInterceptor');

More details on the new interceptors API (which has been around as of v1.1.4) can be found at
https://docs.angularjs.org/api/ng/service/$http#interceptors

Closes #7266
Closes #7267
2014-04-30 17:00:52 -04:00
Vojta Jina 81147a8bfd chore(travis): disable captureTimeout
All of the recent failures were caused by too much load on SL, not
an actual problem.

I think a proper solution will be:
https://github.com/karma-runner/karma-sauce-launcher/issues/37
2014-04-30 15:21:48 +02:00
Peter Bacon Darwin 82751f5960 chore(travis-build): don't re-package when testing docs
Since .travis is already running `grunt package` in the `before_script`
block there is no need to run it again when testing the docs.
2014-04-30 11:16:21 +01:00
Peter Bacon Darwin 8b8992b145 revert: test(ngModelOptions): ensure input has focus in e2e tests
This reverts commit 8d38ec3892.
The protractor tests for ng-model-options were failing locally on Chrome
for me but this commit breaks the tests on Firefox.
2014-04-30 11:01:02 +01:00
Brian Ford 60b2851df7 chore(travis): use npm cache 2014-04-29 15:25:00 -07:00
Caitlin Potter f0e12ea7fe feat($compile): allow SVG and MathML templates via special type property
Previously, templates would always be assumed to be valid HTML nodes. In some cases, it is
desirable to use SVG or MathML or some other language.

For the time being, this change is only truly meaningful for SVG elements, as MathML has
very limited browser support. But in the future, who knows?

Closes #7265
2014-04-29 14:48:50 -04:00
Vojta Jina 28ef2637c1 chore(travis): update Chrome on SL to 34
For some reason, SL gives us Chrome 28 when no version is specified.
2014-04-29 15:56:37 +02:00
Peter Bacon Darwin 8d38ec3892 test(ngModelOptions): ensure input has focus in e2e tests
This was not failing on Travis or Jenkins but was reliably failing on my
local build.
2014-04-29 13:29:01 +01:00
Peter Bacon Darwin 8e404c4dc1 docs(directives): remove line numbers from code blocks 2014-04-29 12:33:20 +01:00
Brian Ford 7570e9f07d docs($sce): fix grammar 2014-04-28 23:53:37 -07:00
Peter Bacon Darwin d90f83cda8 docs(examples): don't attempt to convert example content from JSON
There are some files in the examples that look like JSON and the default
$http transformResponse handler was trying to convert these from strings
to object. An example was the style.css file in the
https://docs.angularjs.org/api/ng/type/ngModel.NgModelController docs.

This commit fixes this by simply removing this transform when loading
these files.
2014-04-28 22:18:55 +01:00
Uri Goldshtein fcc3a7a423 docs(guide): add another analytics library 2014-04-28 12:04:06 -07:00
Edward Brey e3814b1266 docs(angular.Module): add link to module.config() docs
Add a link on where to find more info about how to use `module.config()`

Closes #6270
2014-04-28 15:05:40 +01:00
Ryan Hall 0d691a6feb docs(orderBy): clarify how sorting is processed
An API was passing me numbers as strings (ex. '8.25'), and I was noticing
weird sorting behavior with `orderBy` because it was trying to sort the
numbers alphabetically.

Closes #5436
2014-04-28 14:53:34 +01:00
Thomas Tuts 2022fd768e docs(guide/unit-testing): remove unwanted whitespaces
Closes #5395
2014-04-28 14:39:54 +01:00
marcin-wosinek e37e67eadb docs(select): improve naming of c variable in example
It was felt that `c` did not make it clear what the variable held. This
has been changed to `color` to match the ng-repeat expression above.
In turn the model value has been changed to `myColor` to prevent a name
collision.

Closes #7210
2014-04-28 13:54:03 +01:00
Tom Yam af72f40a55 fix(grunt-utils): ensure special inline CSS works when angular is not a global
The build includes a little script to angular.js, which adds some CSS styles to
the page to support things like ngCloak. This script checks that angular is
not in CSP mode, but before this fix assumed that angular would be in the global
scope.

This commit, references `window.angular` instead of just `angular` because when
running angular in an environment where the top-level scope is not the window
(nodejs for example), we angular is actually a property of `window`.

Closes #7176
2014-04-28 13:36:04 +01:00
eydreeyawn 7a543c985e docs(ngCookies): added example usage of cookieStore
Closes #7278
2014-04-28 08:18:51 -04:00
Caitlin Potter 7d1719e219 docs(ngCookies): use GFM code-snippet rather than example tag
The example tag creates a big ugly white rectangle on the docs page, and this is not very helpful
and kind of looks bad. So GFM snippets are a better way to go.

This fix also removes the unnecessary example heading from the $cookieStore page, as there has not
been an example use of $cookieStore for 2 years now.

Closes #7279
2014-04-28 07:55:47 -04:00
Shahar Talmi accd35b747 chore(jshint): enforce jshint for tests
Closes #7264
2014-04-27 21:20:31 +01:00
Peter Bacon Darwin e16c6aa31d docs(tutorial/step-3): fix experiments
Closes https://github.com/angular/angular-phonecat/issues/142
2014-04-27 10:13:44 +01:00
Peter Bacon Darwin 305696c660 docs(tutorial/step-12): add info about app.css changes
Closes https://github.com/angular/angular-phonecat/issues/145
2014-04-27 09:10:10 +01:00
Brian Ford ff03d7b323 docs(changelog): release notes for 1.3.0-beta.7 proper-attribution 2014-04-25 15:00:17 -07:00
Igor Minar 498835a1c4 fix($location): don't clobber path during parsing of path
Closes #7199
2014-04-24 23:36:28 -07:00
@MizaelGalvez 6ac6621661 docs(tutorial/step_09): fix typos
phonecatApp module had a dot instead of semicolon
2014-04-25 00:47:40 +01:00
Mohamed Daif 39335d96d3 docs(tutorial/step_00): add correct description for attributes naming convention 2014-04-25 00:18:25 +01:00
Michael Payne 737ef25df1 docs(guide/di): fix typo
Fixed a typo. "depenendencies" -> "dependencies"

Closes #7232
2014-04-24 13:27:53 -04:00
Søren Louv-Jansen e19dbc7793 docs(ngMock): fix example for $http respond() helper with function as 1st param
The  `whenPOST` method should return a response object containing status, response body and headers.
If omitted the following error will be thrown:

`Uncaught TypeError: Cannot read property '2' of undefined`

The documentation doesn't make it very clear, so I think it will be appropriate to add it here.

Closes #6761
2014-04-24 09:41:54 -04:00
benjamingr 06d0f1aea6 style(Angular.js): remove redundant _angular
Going through the commit history on GitHub, the `_angular` is for noConflict mode,
the case where you have an old reference to a variable called Angular you want to preserve.

Here is the commit that added _angular in : https://github.com/angular/angular.js/commit/
12ba6cec4f

This feature was later removed here:
https://github.com/angular/angular.js/commit/9faabd1ba0b1929785bcf166dc0ff60008e7c442

The variable 'made it through', it's redundant now.

Closes #7215
2014-04-24 11:42:01 +01:00
Peter Bacon Darwin eda00ce23f docs(tutorial): clarify bower usage 2014-04-24 10:59:04 +01:00
James deBoer 8377e81827 perf(scope): 10x. Share the child scope class.
This change causes Scope.$destory to run 10x faster. I suspect
Scope.$new is significantly faster as well, but I didn't measure it.
2014-04-23 13:46:34 -07:00
Peter Bacon Darwin 828ad89e8a docs(ngSwitch): fix formatting of custom usage field 2014-04-23 19:32:41 +01:00
Peter Bacon Darwin bdec35cebc docs(examples): use form POST to create Plunkers
The previous solution for opening Plunkers from the docs relied on tight
coupling between the docs site and the plunkr site, in particular the
URL to the example code on the docs server was hard coded in the Plunker
site.

This change goes back to the old POST method of creating a Plunker, but
with a subtle difference: In the very old docs, the content was injected
directly into the example HTML at build time.  This was easy enough to
do as the example actually ran in the current page but also increased
the size of the doc page.

The new examples are run in completely separate iframes. This new version
of showing a Plunker loads the file content for the Plunker from the
server by accessing the example's manifest.json file using $http requests.

This also has the additional benefit that you can now generate plunkers
from examples that are running locally or, frankly, in any folder on any
server, such as personal builds on the Jenkins CI server.

Closes #7186
Closes #7198
2014-04-23 12:43:40 +01:00
Peter Bacon Darwin f0da1a3615 chore(npm-shrinkwrap): update dependencies
Dgeni-packages is updated to v0.8.3 to fix a bug in the docs (#7184)
Karma is updated to get us the fixed version of chokidar (v0.8.2)

Closes #7184
2014-04-23 12:38:58 +01:00
Peter Bacon Darwin 950c9724ba chore(clean-shrinkwrap): chokidar is now fixed at v0.8.2 2014-04-23 11:57:14 +01:00
Caitlin Potter e0375a61d0 chore(package.json): remove dependencies which are no longer required
Based on https://github.com/angular/angular.js/issues/3244#issuecomment-41003086, I don't believe
we actually use either of these now that dgeni has replaced the old docs app. These should be
removed if Travis is green.

The i18n scripts still rely on q, so unfortunately it can't be gotten rid of just yet.
2014-04-22 16:32:18 -07:00
nnennajohn 8681276590 docs(tutorial/index): add directions for installing bower 2014-04-22 16:26:19 -07:00
Michał Gołębiowski 92e8289c12 refactor(jqLite): change leftover element.bind/unbind to element.on/off 2014-04-22 16:03:22 +02:00
Amin Ogarrio 219648fada docs(tutorial/step-12): correct file path
Fixed wrong file path:
- angular/angular-animate.js -> angular-animate/angular-animate.js, which meets the code in app/index.html

Closes #7168
2014-04-21 16:20:22 -07:00
Matthew Davies 6ab7adda3c docs(guide/controller): fixed spelling of spiciness
Closes #7185
2014-04-21 16:04:39 -07:00
Brian Ford 7bcaf39437 docs(changelog): release notes for 1.3.0-beta.6 expedient-caffeination 2014-04-21 15:57:08 -07:00
Simon Taranto 2da94a701b docs(tutorial): update step_07.ngdoc
Closes #7169
2014-04-21 15:32:23 -07:00
ttam3d0 5ce17efbbd docs(tutorial/step_03): sync markup with angular-phonecat
Update from Bootstrap 2.3 to 3.1 changed "span" to "col" tags

Closes #7172
2014-04-21 15:22:58 -07:00
Amin Ogarrio b1559be5cf docs(tutorial): fix typos in examples
The phonecatApp and phonecatAnimations modules have a dot where a semicolon should be at the end of the sentence

Closes #7167
2014-04-21 15:09:04 -07:00
Thomas Junghans 8570585692 docs(tutorial): update step_11.ngdoc
Change toEqual to toEqualData in the test 'should create "phones" model with 2 phones fetched from xhr'
to make test more consistent.

Closes #7182
2014-04-21 15:01:55 -07:00
Igor Minar 9178c318e1 docs(guide/forms): fix quotes around updateOn that broke ngModelOptions example 2014-04-21 14:53:04 -07:00
Igor Minar 546cb429d9 perf($interpolate): speed up interpolation by recreating watchGroup approach
This change undoes the use of watchGroup by code that uses $interpolate, by
moving the optimizations into the $interpolate itself. While this is not ideal,
it means that we are making the existing api faster rather than require people
to use $interpolate differently in order to benefit from the speed improvements.
2014-04-21 11:48:38 -07:00
Igor Minar 1db3b8cfb7 refactor($interpolate): attempt to remove hacky code due to $interpolation perf improvements 2014-04-21 11:47:23 -07:00
rodyhaddad 0ebfa0d112 perf($compile): watch interpolated expressions individually 2014-04-21 11:12:35 -07:00
rodyhaddad 88c2193c71 refactor($interpolate): split .parts into .expressions and .separators
BREAKING CHANGE: the function returned by $interpolate
no longer has a `.parts` array set on it.
It has been replaced by two arrays:
* `.expressions`, an array of the expressions in the
  interpolated text. The expressions are parsed with
  $parse, with an extra layer converting them to strings
  when computed
* `.separators`, an array of strings representing the
  separations between interpolations in the text.
  This array is **always** 1 item longer than the
  `.expressions` array for easy merging with it
2014-04-21 11:12:35 -07:00
rodyhaddad 21f9316338 feat(Scope): add $watchGroup method for observing a set of expressions
Given an array of expressions, if any one expression changes then the listener function fires
with an arrays of old and new values.

$scope.watchGroup([expression1, expression2, expression3], function(newVals, oldVals) {
 // newVals and oldVals are arrays of values corresponding to expression1..3
 ...
});

Port of angular/angular.dart@a3c31ce1dd
2014-04-21 11:12:35 -07:00
Igor Minar 8d0cb30688 chore(jshint): add jshint for the test/ folder 2014-04-18 16:33:03 -07:00
Igor Minar 45c356586b chore(Scope): name the $watch deregistration function as 2014-04-18 16:32:07 -07:00
jerryfan 02fcac5e04 docs(tutorial/step_12): fix grammar 2014-04-18 18:38:37 -04:00
Caitlin Potter 64a3f42f14 style(input): fix trailing whitespace
Oops. I blame vim for this little accident.
2014-04-18 18:13:53 -04:00
Caitlin Potter ff428e7283 fix(input): don't dirty model when input event triggered due to placeholder change
Certain versions of IE inexplicably trigger an input event in response to a placeholder
being set.

It is not possible to sniff for this behaviour nicely as the event is not triggered if
the element is not attached to the document, and the event triggers asynchronously so
it is not possible to accomplish this without deferring DOM compilation and slowing down
load times.

Closes #2614
Closes #5960
2014-04-18 17:47:25 -04:00
Janas Page a990078173 docs(form.FormController): fix grammar 2014-04-18 16:42:27 -04:00
Chris Wheatley f0407d2aa0 docs(ngMock): grammar fix
Small grammar fix for mock $httpBackend documentation.
2014-04-18 15:41:26 -04:00
Caitlin Potter 49e7c32bb4 fix($location): fix and test html5Mode url-parsing algorithm for legacy browsers
This CL fixes problems and adds test cases for changes from #6421. Changes
include fixing the algorithm for preprocessing href attribute values, as
well as supporting xlink:href attributes. Credit for the original URL
parsing algorithm still goes to @richardcrichardc.

Good work, champ!
2014-04-17 17:42:34 -04:00
Richard Collins 3f047704c7 fix($location): make legacy browsers behave like modern ones in html5Mode
Previously, LocationHashbangInHtml5Url, which is used when html5Mode is enabled
in browsers which do not support the history API (IE8/9), would behave very
inconsistently WRT relative URLs always being resolved relative to the app root
url.

This fix enables these legacy browsers to behave like history enabled browsers,
by processing href attributes in order to resolve urls correctly.

Closes #6162
Closes #6421
Closes #6899
Closes #6832
Closes #6834
2014-04-17 17:42:20 -04:00
expilo b2e48e61c7 docs(tutorial): specify that ng-repeat goes in markup and not script
Just to make clear that it should not go to test spec.

Closes #7104
2014-04-17 12:43:01 -04:00
Thom Allen d5a2069cd5 docs(tutorial): fix typo in module name
Fixed a typo in step 2

Closes #7138
2014-04-16 16:24:14 -04:00
Caitlin Potter 1192531e9b fix($compile): reference correct directive name in ctreq error
Previously, ctreq would possibly reference the incorrect directive name,
due to relying on a directiveName living outside of the closure which
throws the exception, which can change before the call is ever made.

This change saves the current value of directiveName as a property of
the link function, which prevents this from occurring.

Closes #7062
Closes #7067
2014-04-15 17:16:59 -04:00
Caitlin Potter 5dee9e4a33 fix(limitTo): do not convert Infinity to NaN
parseInt(Infinity, 10) will result in NaN, which becomes undesirable when the expected behaviour is
to return the entire input.

I believe this is possibly useful as a way to toggle input limiting based on certain factors.

Closes #6771
Closes #7118
2014-04-15 17:14:37 -04:00
Caitlin Potter b10a4371a6 docs(ngTouch): define module depending on ngTouch in ngTouch examples
In addition to requiring that the file is loaded, it's also necessary to depend on the ngTouch
module when creating the injector.

Closes #7077
2014-04-14 05:38:14 -04:00
Wojciech Fornal ede9984b36 docs(api): fix broken link to "Services" guide
Nothing big. Simply a broken link to "Services".

> Nothing big.

It IS big, don't you ever forget it!

Closes #7101
2014-04-12 20:46:41 -04:00
Valentin Waeselynck 613a5cc5db docs(guide/e2e-testing): remove inconsistency in first paragraph
Change the "first line of defense" to unit tests, not end-to-end tests.
2014-04-11 19:26:25 +01:00
Caitlin Potter 4b1695ec61 feat(injector): "strict-DI" mode which disables "automatic" function annotation
This modifies the injector to prevent automatic annotation from occurring for a given injector.

This behaviour can be enabled when bootstrapping the application by using the attribute
"ng-strict-di" on the root element (the element containing "ng-app"), or alternatively by passing
an object with the property "strictDi" set to "true" in angular.bootstrap, when bootstrapping
manually.

JS example:

    angular.module("name", ["dependencies", "otherdeps"])
      .provider("$willBreak", function() {
        this.$get = function($rootScope) {
        };
      })
      .run(["$willBreak", function($willBreak) {
        // This block will never run because the noMagic flag was set to true,
        // and the $willBreak '$get' function does not have an explicit
        // annotation.
      }]);

    angular.bootstrap(document, ["name"], {
      strictDi: true
    });

HTML:

    <html ng-app="name" ng-strict-di>
      <!-- ... -->
    </html>

This will only affect functions with an arity greater than 0, and without an $inject property.

Closes #6719
Closes #6717
Closes #4504
Closes #6069
Closes #3611
2014-04-10 17:51:15 -04:00
Caitlin Potter 24a045c3b2 docs(ngTouch): include angular-touch.js in example iframes
Mystical [deps="angular-touch.js"] is enough to get the correct file to be added to the <head> tag.

Closes #7058
Closes #7074
2014-04-10 14:26:12 -04:00
Valentin Waeselynck 61a8e198bd docs(guide/e2e-testing): remove repeated word 'manually'
Fixed repetition in Caveats paragraph

"manually" was repeated. Grammatical correctness is the prime directive, resistance is futile.

Closes #7073
2014-04-10 10:59:02 -04:00
Robert Speicher 66091756b8 docs(tutorial): npm needs "run" keyword for custom script names
Fix "npm run update-webdriver" command

Closes #7071
2014-04-10 08:24:16 -04:00
Steven Benjamin 6743ccf788 docs(guide/directive): directives can be comments, too
While it's not a widely used feature, and likely shouldn't be recommended, it is
possible to use a directive via a comment node.

Closes #7061
2014-04-10 06:46:05 -04:00
Lefteris Paraskevas 2ad7bb9ca9 docs(guide/unit-testing): fix small typo (than instead of then) 2014-04-10 00:49:59 +01:00
Vincent Driessen 3878be52f6 feat($resource): Make stripping of trailing slashes configurable.
First, this now uses a flat object configuration, similar to
`$httpBackend`.  This should make configuring this provider much more
familiar.

This adds a fourth optional argument to the `$resource()` constructor,
supporting overriding global `$resourceProvider` configuration.

Now, both of these ways of configuring this is supported:

    app.config(function($resourceProvider) {
      $resourceProvider.defaults.stripTrailingSlashes = false;
    });

or per instance:

    var CreditCard = $resource('/some/:url/', ..., ..., {
        stripTrailingSlashes: false
    });
2014-04-10 00:09:22 +02:00
Julie acfcbdf906 docs(e2e-testing): deprecate ng-scenario and update E2E testing doc to discuss protractor 2014-04-09 11:05:43 -07:00
Rosseyn 879b0bc9f9 docs(guide/ie): fix spelling mistake
Closes #7050
2014-04-08 18:46:18 -04:00
Peter Bacon Darwin faec99794d docs(ngModelController): add example for $cancelUpdate 2014-04-08 15:19:42 +01:00
Shahar Talmi 66a132847d docs(ngModelOptions): add some docs for $cancelUpdate 2014-04-08 15:19:42 +01:00
Shahar Talmi 2354924a46 refactor(ngModelController): use 'ctrl' instead of 'this' 2014-04-08 15:19:42 +01:00
Shahar Talmi 940fcb4090 fix(ngModelController): introduce $cancelUpdate to cancel pending updates
The `$cancelUpdate()` method on `NgModelController` cancels any pending debounce
action and resets the view value by invoking `$render()`.

This method should be invoked before programmatic update to the model of inputs
that might have pending updates due to `ng-model-options` specifying `updateOn`
or `debounce` properties.

Fixes #6994
Closes #7014
2014-04-08 15:19:42 +01:00
Igor Minar b389cfc49e docs($location): improve $location.search() docs
Closes #7030
2014-04-07 16:10:34 -07:00
Tobias Bosch 1d9ac65d37 docs(bootstrap): add example that actually uses angular.bootstrap.
Closes #7032.
2014-04-07 15:44:10 -07:00
Tyler McGinnis f45241649b docs(ngMock): fix typo
"register" should be "registered"

Closes #7034
2014-04-07 16:33:20 -04:00
Jason Travis a8d42800e7 docs(guide): fix dropped word in controller description
Closes #7026
2014-04-07 07:46:20 -04:00
Thomas Wicker 091eb83301 docs(tutorial): fix typo in tutorial index page
Spelling error, "server" should be "serve"

Closes #7028
2014-04-07 07:39:54 -04:00
Jonathan Sampson 28613f0eae docs(guide/providers): fix grammar in note regarding factory naming conventions
It seems as though this sentence wasn't written the way it was originally planned. I did my best to
approximate the intent of the original author.

Closes #7022
2014-04-06 22:05:34 -04:00
Peter Bacon Darwin 64d404612f docs(tutorial/step-8): fix external link 2014-04-06 18:58:01 +01:00
Yiling Lu 6c7fdd845b docs(tutorial/step-8): link to Protractor rather than ngScenario
Angular test runner API has been deprecated, Protractor is advised:
http://docs.angularjs.org/guide/e2e-testing
Link is updated to direct reader to Protractor API.

Closes #7001
2014-04-06 17:34:36 +01:00
Peter Bacon Darwin b517f49a80 chore(docs/app): remove redundant code directive
Closes #7000
2014-04-06 17:27:53 +01:00
Peter Bacon Darwin abddefd057 docs(guide/di): clarify what can be injected into what
Also do some general housekeeping and tidying of the page.

Closes #6999
2014-04-06 17:22:24 +01:00
Joseph Orbegoso Pea 4f38ba9898 docs(tutorial/step-5): clarify inline annotations
Closes #6998
2014-04-06 15:22:21 +01:00
Peter Bacon Darwin f7cf680d23 docs(tutorial/step-7): clarify the new files & modules
Closes #6996
2014-04-06 14:56:45 +01:00
Peter Bacon Darwin e101c127af docs(ngModelOptions): fix param name and tidy up examples 2014-04-05 21:01:33 +01:00
Thiago Colares 3d31a15cc8 docs(tutorial): added a link to Wiki page on DI in Step 7
This article is fantastic and really helped on understanding how DI works on Angular. It may be
useful to other beginners -- because, at first glance, this topic (DI on Angular) ended a little bit
hazy for me.

Closes #7010
2014-04-05 13:52:02 -04:00
martco 4ae5f7a477 docs(guide/directive): grammatical fixup
The 'to' in 'end-to-end' is directional, not numeric

Closes #6895
2014-04-05 09:48:30 -04:00
Jason Carver d845d8a742 docs(CONTRIBUTING.md): typo fix
"submitting and issue" should be "submitting an issue"

Closes #7002
2014-04-05 09:44:16 -04:00
Yiling Lu 461d6990cf docs(tutorial): fix links to bower in Step 11
Minor change: add missing bower reference link.

Closes #7005
2014-04-05 09:34:37 -04:00
Caitlin Potter ef64169db3 fix($compile): fix regression which affected old jQuery releases
ddb8081 and 4ea57e7 removed the calls which trimmed leading and trailing whitespace from templates
in the HTML compiler. This broke old versions of jQuery (such as 1.9.1), which do not trim
whitespace in their constructors. Naturally, this would not appear in the jQuery tests, as we are
testing against a version which does trim whitespace in the constructor.

This fix re-adds calls to `trim()` when compiling templates in $compile, in order to avoid breaking
old versions of jQuery.
2014-04-05 08:58:16 -04:00
Yiling Lu 8b0b7ca65a docs(tutorial): change regexp so that assertion works
Need to remove this single space for the regex to work here.

Apparently `getText()` is trimming the text content or something, because there is no good reason
why that space should not be there.

Closes #6985
2014-04-04 18:09:32 -04:00
Yiling Lu 20b22f1f7e docs(guide/tutorial): make added assertion one which will actually work.
Update the by.binding usage to make the test case work.

Closes #6987
2014-04-04 16:37:00 -04:00
Matias Niemelä 708f2ba984 fix($animate): ensure class-based animations always perform a domOperation if skipped
Closes #6957
2014-04-04 11:39:08 -04:00
Luis Ramón López dbe381f29f feat(ngModelOptions): custom triggers and debounce of ngModel updates
By default, any change to an input will trigger an immediate model update,
form validation and run a $digest. This is not always desirable, especially
when you have a large number of bindings to update.

This PR implements a new directive `ngModelOptions`, which allow you to
override this default behavior in several ways. It is implemented as an
attribute, to which you pass an Angular expression, which evaluates to an
**options** object.

All inputs, using ngModel, will search for this directive in their ancestors
and use it if found.  This makes it easy to provide options for a whole
form or even the whole page, as well as specifying exceptions for
individual inputs.

* You can specify what events trigger an update to the model by providing
  an `updateOn` property on the **options** object. This property takes a
  string containing a space separated list of events.

  For example, `ng-model-options="{ updateOn: 'blur' }"` will update the
  model only after the input loses focus.

  There is a special pseudo-event, called "default", which maps to the
  default event used by the input box normally. This is useful if you
  want to keep the default behavior and just add new events.

* You can specify a debounce delay, how long to wait after the last triggering
  event before updating the model, by providing a `debounce` property on
  the **options** object.

  This property can be a simple number, the
  debounce delay for all events. For example,
  `ng-model-options="{ debounce: 500 }" will ensure the model is updated
  only when there has been a period 500ms since the last triggering event.

  The property can also be an object, where the keys map to events and
  the values are a corresponding debounce delay for that event.
  This can be useful to force immediate updates on some specific
  circumstances (like blur events). For example,
  `ng-model-options="{ updateOn: 'default blur', debounce: { default: 500, blur: 0} }"`

This commit also brings to an end one of the longest running Pull Requests
in the history of AngularJS (#2129)!  A testament to the patience of @lrlopez.

Closes #1285, #2129, #6945
2014-04-04 14:48:53 +01:00
Choi YoonSung e55c8bcbca docs(tutorial/step-8): module must be loaded in test
Closes #6930
2014-04-04 14:12:57 +01:00
Peter Bacon Darwin 28453015fc docs(tutorial): synchronize with angular-phonecat changes 2014-04-04 14:05:24 +01:00
Peter Bacon Darwin 6b7a1b82bc chore(docs/app/tutorials): improve helper directives 2014-04-04 12:06:40 +01:00
Peter Bacon Darwin 7b0c5b937c docs(tutorial): clean up and elaborate introduction 2014-04-04 10:22:33 +01:00
Peter Bacon Darwin a526ae8f77 docs(tutorial): synch step 0 with angular-seed 2014-04-04 08:30:06 +01:00
Joseph Orbegoso Pea b63fd11800 docs(guide/scope): small fixes 2014-04-03 16:09:10 -07:00
Martin Jezek c369563818 docs(tutorial): fix linked files to bower_components folder
Closes #6960
2014-04-03 16:01:49 -07:00
Brian Ford 8e2c62ae9d chore(CHANGELOG.md): add missing 1.2.15 changelog 2014-04-03 14:46:15 -07:00
Brian Ford cb6b976851 chore(CHANGELOG.md): add missing changes to 1.2.16 release 2014-04-03 14:43:41 -07:00
Brian Ford 7227f1a479 chore(CHANGELOG.md): add changelog for 1.2.16 and 1.3.0-beta.5 2014-04-03 14:39:12 -07:00
Matias Niemelä 1cb8584e84 fix($animate): insert elements at the start of the parent container instead of at the end
With 1.2.x, `$animate.enter` and `$animate.move` both insert the element at the end of the provided
parent container element when only the `parent` element is provided. If an `after` element is provided
then they will place the inserted element after that one. This works fine, but there is no way to
place an item at the top of the provided parent container using these two APIs.

With this change, if the `after` argument is not specified for either `$animate.enter` or `$animate.move`,
the new child element will be inserted into the first position of the parent container element.

Closes #4934
Closes #6275

BREAKING CHANGE: $animate will no longer default the after parameter to the last element of the parent
container. Instead, when after is not specified, the new element will be inserted as the first child of
the parent container.

To update existing code, change all instances of `$animate.enter()` or `$animate.move()` from:

`$animate.enter(element, parent);`

to:

`$animate.enter(element, parent, angular.element(parent[0].lastChild));`
2014-04-03 17:07:43 -04:00
Matias Niemelä c67bd69c58 fix($animate): ensure the CSS driver properly works with SVG elements
The default CSS driver in ngAnimate directly uses node.className when reading
the CSS class string on the given element. While this works fine with standard
HTML DOM elements, SVG elements have their own DOM property. By switching to use
node.getAttribute, ngAnimate can extract the element's className value without
throwing an exception.

When using jQuery over jqLite, ngAnimate will not properly handle SVG elements
for an animation. This is because jQuery doesn't process SVG elements within it's
DOM operation code by default. To get this to work, simply include the jquery.svg.js
JavaScript file into your application.

Closes #6030
2014-04-03 15:47:16 -04:00
Igor Minar 71c11e96c6 fix(Scope): revert the __proto__ cleanup as that could cause regressions
When a async task interacts with a scope that has been destroyed already
and if it interacts with a property that is prototypically inherited from
some parent scope then resetting proto would make these inherited properties
inaccessible and would result in NPEs
2014-04-03 12:40:22 -07:00
Brian Ford c9677920d4 fix(ngClass): handle ngClassOdd/Even affecting the same classes
The basic approach is to introduce a new elt.data() called $classCounts that keeps
track of how many times ngClass, ngClassEven, or ngClassOdd tries to add a given class.
The class is added only when the count goes from 0 to 1, and removed only when the
count hits 0.

To avoid duplicating work, some of the logic for checking which classes
to add/remove move into this directive and the directive calls $animate.

Closes #5271
2014-04-03 11:55:54 -07:00
Andreas Krummsdorf 83e36db85d style(loader.js): correct JSDoc tags of the params of the function module(name, requires, configFn)
This will improve the hints for IDE's which support the Google Closure Compiler (e.g. Webstorm)
2014-04-03 09:37:23 -07:00
Igor Minar d64d41ed99 fix(Scope): more scope clean up on $destroy to minimize leaks
Due to a known V8 memory leak[1] we need to perform extra cleanup to make it easier
for GC to collect this scope object.

V8 leaks are due to strong references from optimized code (fixed in M34) and inline
caches (fix in works). Inline caches are caches that the virtual machine builds on the
fly to speed up property access for javascript objects. These caches contain strong
references to objects so under certain conditions this can create a leak.

The reason why these leaks are extra bad for Scope instances is that scopes hold on
to ton of stuff, so when a single scope leaks, it makes a ton of other stuff leak.

This change removes references to objects that might be holding other big
objects. This means that even if the destroyed scope leaks, the child scopes
should not leak because we are not explicitly holding onto them.

Additionally in  theory we should also help make the current scope eligible for GC
by changing properties of the current Scope object.

I was able to manually verify that this fixes the problem for the following
example app: http://plnkr.co/edit/FrSw6SCEVODk02Ljo8se

Given the nature of the problem I'm not 100% sure that this will work around
the V8 problem in scenarios common for Angular apps, but I guess it's better
than nothing.

This is a second attempt to enhance the cleanup, the first one failed  and was
reverted because it was too aggressive and caused problems for existing apps.
See: #6897

[1] V8 bug: https://code.google.com/p/v8/issues/detail?id=2073

Closes #6794
Closes #6856
Closes #6968
2014-04-03 00:25:01 -07:00
Stephanie Nacios Staub ba48797bb0 docs(tutorial): update instructions for running tests in step 2
Fixing outdated instructions on how to run the test

Closes #6972
2014-04-03 01:01:39 -04:00
Igor Minar f9eb324716 chore(rootScopeSpec): fix a typo in spec description 2014-04-02 21:24:40 -07:00
Yiling Lu 862e587b46 docs(tutorial): remove reference to old webserver script
script/web-server.js is not present anymore. This doc might be referencing a previous version of the
code. Currently the only way to start the server seems to be using "npm start".

Closes #6966
2014-04-02 23:59:25 -04:00
Jonathan Woodard 6cd6ec62fd docs(guide/bootstrap): remove extra call to angular.module()
There was an extra call to angular.module() not being used in 'getter' mode. While this doesn't
break the demo app, it does look kind of weird, so lets toss it.

Closes #6969
2014-04-02 23:55:18 -04:00
Tobias Bosch 549166740b docs(ngForm): clarify the purpose of ngForm
Related to #6704 and #2513.
2014-04-02 17:23:13 -07:00
Alexander Harding 2e3a972b32 test($compile): add tests for <option> or <optgroup> tags as root template nodes 2014-04-02 19:40:16 -04:00
Caitlin Potter ddb8081982 refactor(jqLite): make HTML-parsing constructor more robust
Previously, the jqLite constructor was limited and would be unable to circumvent many of the HTML5
spec's "allowed content" policies for various nodes. This led to complicated and gross hacks around
this in the HTML compiler.

This change refactors these hacks by simplifying them, and placing them in jqLite rather than in
$compile, in order to better support these things, and simplify code.

While the new jqLite constructor is still not even close to as robust as jQuery, it should be more
than suitable enough for the needs of the framework, while adding minimal code.

Closes #6941
Closes #6958
2014-04-02 19:40:16 -04:00
Pascal Precht ccfa72dfa1 docs(guide): fix link in "Complementary libraries" section
the link to `angular-translate` is outdated. this commit fixes it.
2014-04-02 16:07:58 -07:00
Tero Parviainen 7914d3463b fix($parse): mark constant unary minus expressions as constant
Previously, constant numbers with a unary minus sign were not treated as constants. This fix corrects
this behaviour, and may provide a small performance boost for certain applications, due to constant
watches being automatically unregistered after their first listener call.

Closes #6932
2014-04-02 10:05:45 -04:00
Julie 10110bc3f7 docs(tutorial): update tutorial steps to discuss protractor
Closes #6940
2014-04-02 08:30:52 -04:00
b9chris 0ed0207dfb docs($location): fix link to Developer Guide for "Using $location"
Closes #6946
2014-04-02 08:08:00 -04:00
Igor Minar 6621adb6bb revert: fix(Scope): aggressively clean up scope on $destroy to minimize leaks
This reverts commit f552f25171.

The commit is causing regressions.

Closes #6897
2014-04-01 16:39:51 -07:00
Peter Bacon Darwin 789328de9b docs(tutorial): update to match changes to phonecat 2014-04-01 18:21:47 +01:00
Peter Bacon Darwin 14a2142484 docs(css): ensure all type-hints have a background color
If the type of a type-hint was not recognized, say a "Promise", then
the background color was left as white.  Given that the default
foreground color is also white, this meant that such type-hints were
invisible.

Closes #6934
2014-04-01 12:00:37 +01:00
Peter Bacon Darwin 2a4f92ee99 chore(grunt): add jscs task to test task
It is too easy to forget to check jscs for things like trailing whitespace
before pushing commits, such as simple doc changes.  This then breaks the
build and is messy.  Adding jscs to the test task gives people a slightly
better chance of catching these before pushing.
2014-04-01 11:52:23 +01:00
Joseph Orbegoso Pea 47384bc61b docs(guide/bootstrap): add note about ngApp and manual bootstrap 2014-03-31 16:57:49 -07:00
Bobdina 50eb3b2bd6 chore(build): make version-info.js run on windows
Replaced grep with match
Windows operating systems do not have grep by default

Closes #6912.
2014-03-31 16:18:11 -07:00
Matias Niemelä 876df04606 chore(CHANGELOG.md): add changelog for 1.3.0-beta.4 2014-03-28 17:43:17 -04:00
Matias Niemelä 908ab52b8d chore($animate): fix broken IE8 test 2014-03-28 14:13:35 -04:00
Matias Niemelä ff5cf736e5 fix($animate): prevent cancellation timestamp from being too far in the future
Closes #6748
2014-03-28 12:25:50 -04:00
Igor Minar f552f25171 fix(Scope): aggressively clean up scope on $destroy to minimize leaks
Due to a known V8 memory leak[1] we need to perform extra cleanup to make it easier
for GC to collect this scope object.

The theory is that the V8 leaks are due to inline caches which are caches
built on the fly to speed up property access for javascript objects.

By cleaning the scope object and removing all properties, we clean up ICs
as well and so no leaks occur.

I was able to manually verify that this fixes the problem for the following
example app: http://plnkr.co/edit/FrSw6SCEVODk02Ljo8se?p=preview

Given the nature of the problem I'm not 100% sure that this will work around
the V8 problem in scenarios common for Angular apps, but I guess it's better
than nothing.

[1] V8 bug: https://code.google.com/p/v8/issues/detail?id=2073

Closes #6794
Closes #6856
2014-03-27 21:04:25 -07:00
David I. Lehn ec8e3957d2 docs($sce): fix typo.
Closes #6882
2014-03-27 20:27:50 -04:00
tamakisquare d0df8c8946 docs(guide/filter): mention that filters can be used in directives
The doc mentions filters can be used in services and controllers but directives
aren't mentioned. This could lead to confusion for beginners.
2014-03-27 15:36:25 -07:00
Uri Goldshtein b1d1cb6b7a docs(guide): add ngStorage to specific topics 2014-03-27 14:29:16 -07:00
jim lyndon 1d2414ca93 feat($http): add xhr statusText to completeRequest callback
Makes xhr status text accessible is $http success/error callback.
See www.w3.org/TR/XMLHttpRequest/#dom-xmlhttprequest-statustext

Closes #2335
Closes #2665
Closes #6713
2014-03-27 17:08:23 -04:00
ChrisRose 9f62d9d20b docs(filter/orderBy): fixed typo 2014-03-27 13:51:41 -07:00
Alex Sanford 08354ae1f7 docs(ngResource): clarify behaviour of $promise
Closes #6753
2014-03-27 16:31:52 -04:00
winkler1 3f9f1ad502 docs(ngShowHide): fix typo 'hrml' -> 'html'
Typo 'hrml'

Oops!

Closes #6874
2014-03-27 14:38:25 -04:00
Narretz a2aa667777 docs(guide/scope): fix links to $interpolate
Closes #6877
2014-03-27 14:31:14 -04:00
William Bagayoko 0619e6f278 chore(docs): remove unneeded Bootstrap/jQuery files from distribution 2014-03-27 12:12:13 +00:00
wbyoko 545c62ab18 docs(error/index): add header
Closes #6849
2014-03-26 17:21:53 -07:00
wbyoko e584111335 docs(misc/index): add header; general links
Closes #6850
2014-03-26 17:21:32 -07:00
wbyoko 5d9eccc2d4 docs(misc/started): add header
Closes #6851
2014-03-26 17:21:04 -07:00
Narretz 7a7e9f4047 docs($compile): add note about recursive compilation in templates
Closes #3079
Closes #6869
2014-03-26 16:42:39 -07:00
mrmrs 15859cf2c4 chore(docs): remove px declaration from x,y coordinates in header svg 2014-03-26 16:35:18 -07:00
Tobias Bosch 489d0d46d7 chore(release): simplify scripts so that they can be tested locally
The `git fetch --all` resulted in an error if in the local `.gitconfig`
a remote was configured that does not exist in the bower/code.anguarjs.org
repositories (e.g. "remote "upstream-prs"").
2014-03-26 15:55:09 -07:00
Tobias Bosch 33e6e0519c chore(release): remove after CDN script
The homepage (angularjs.org) and the docs now calculate the
current cdn version on every build, so there is no need
for an after-cdn script.
2014-03-26 15:55:09 -07:00
Tobias Bosch aa249ae4a2 chore(release): calculate the cdnVersion on every build
The CDN version of angular is now calculated on every build,
by looking at the tags in angular/angular.js, sorting them
by semver and checking against ajax.googleapis.com which
one is available.
2014-03-26 15:55:09 -07:00
Tobias Bosch d6d7fe4b07 chore(release): don't update phonecat and seed during a release
This is no more needed as phonecat and seed are using bower
now to get the angular version.
2014-03-26 15:55:08 -07:00
sgrebnov 47ba601460 fix(doc-gen): Run Gulp on Windows too
Using node_module/.bin/gulp will enable to gulp command to run
both on Windows and Linux. In its current form, the default action of
executing a Javascript file on Windows does not use node.
Requires quotes around the command to correctly resolve path on Windows

Closes #6346
2014-03-26 18:44:14 +00:00
Matias Niemelä 1bebe36aa9 fix($animate): make CSS blocking optional for class-based animations
$animate attempts places a `transition: none 0s` block on the element when
the first CSS class is applied if a transition animation is underway. This
works fine for structural animations (enter, leave and move), however, for
class-based animations, this poses a big problem. As of this patch, instead
of $animate placing the block, it is now the responsibility of the user to
place `transition: 0s none` into their class-based transition setup CSS class.
This way the animation will avoid all snapping and any will allow $animate to
play nicely with class-based transitions that are defined outside of ngAnimate.

Closes #6674
Closes #6739

BREAKING CHANGE: Any class-based animation code that makes use of transitions
and uses the setup CSS classes (such as class-add and class-remove) must now
provide a empty transition value to ensure that its styling is applied right
away. In other words if your animation code is expecting any styling to be
applied that is defined in the setup class then it will not be applied
"instantly" default unless a `transition:0s none` value is present in the styling
for that CSS class. This situation is only the case if a transition is already
present on the base CSS class once the animation kicks off.
2014-03-26 12:48:53 -04:00
Matias Niemelä 2317af6851 fix($animate): run CSS animations before JS animations to avoid style inheritance
If a JS animation is run before a CSS animation then the JS animation may end up writing style
data to the element. If any transition or animation style data is written then it may end up
being accidentally inherited into the CSS animation hanlder that ngAnimate uses. This may result
in an unexpected outcome due to the tweaks and hacks that the CSS handler places on the element.
If the CSS animation is run before the JS animation then, if there are no transitions on the style
attribute nor within the global CSS on the page then nothing will happen and the JS animation can
work as expected.

Closes #6675
2014-03-26 12:11:25 -04:00
Igor Minar 3bf8d6c612 docs(guide/unit-testing): fix link 2014-03-26 03:50:31 -07:00
Alex Miller 372e31ae84 docs(guide/migration): clarify some confusing points
Closes #6756
2014-03-26 03:28:42 -07:00
Brian Ford 6bf3a12eec chore(travis): do not use npm cache in travis config
This reverts commit e14d1a7988.
2014-03-25 18:05:13 -07:00
Nikita Tovstoles 273e34e3de docs(guide/unit-testing): recommend pre-compiling templates
quite a few folks struggle with how to test directives with external templates.
karma-ng-html2js-preprocessor provides an easy solution but the issues is not
raised in the docs.
2014-03-25 17:52:58 -07:00
Brian Ford 98d5885237 docs(guide/filter): fix example style
* use -Controller suffix
* use array annotations
2014-03-25 17:35:44 -07:00
Brian Ford af042b60db docs(guide/directive): fix example style
* use -Controller suffix
* use array annotations
2014-03-25 17:33:05 -07:00
Brian Ford ca1d126005 docs(error/$injector/unpr): use Controller suffix 2014-03-25 17:10:51 -07:00
Brian Ford f6877ed2d9 docs(guide/controller): use -Controller suffix
Previously, the convention was to end controllers with -Ctrl.
The new convention is to use -Controller
2014-03-25 17:07:47 -07:00
David Pope d6bcbc773c docs($rootScope.Scope): link to angular.equals in Scope.$watch docs 2014-03-25 16:15:53 -07:00
jfortunato 9d07796227 docs(tutorial/step_02): fix typo 2014-03-25 15:57:10 -07:00
Brian Ford 664e680948 docs(ngEventDirs): link to info on $event
Closes #6724
2014-03-25 15:54:40 -07:00
Brian Ford abd6889dca docs(guide/expression): add section on $event 2014-03-25 15:53:27 -07:00
Brian Ford 074648ef57 docs(angular.bootstrap): fix param type to DOMElement 2014-03-25 14:27:40 -07:00
Adam Bradley fa844f64cb docs(css): Add background to .type-hint-domelement
`.type-hint-domelement` does not have a background color assigned to it.
DOM element type hints are now proudly displayed with CadetBlue.
2014-03-25 14:27:40 -07:00
Igor Minar 008fbe53d1 docs(errors/$injector/nomod): add info about forgetting to load the module file
Closes #3752
2014-03-25 14:11:40 -07:00
Caitlin Potter ae342b5ce7 docs($cacheFactory): document cache instance methods
These were apparently entirely undocumented. I'm not sure if they're intended
to be private, but in case they're not, I've written some initial docs for them
2014-03-25 13:32:57 -07:00
Patrice Chalin 562334f5f1 chore(CONTRIBUTING): merge relevant updates from angular.dart
Back port changes to angular.dart `CONTRIBUTING.md`, as suggested by
@vicb.
2014-03-25 13:30:28 -07:00
Trevor Ewen 080ac5a262 docs($cacheFactory): add example 2014-03-25 13:26:34 -07:00
Brian Ford e14d1a7988 chore: use npm cache in travis config 2014-03-25 13:11:55 -07:00
Wesley Cho 726ffdc50f docs($compile): add controllerAs example 2014-03-25 13:08:11 -07:00
Emma Guo b93ca855c0 docs(README): use svg badge 2014-03-25 13:00:22 -07:00
Brian Ford e307e2ab89 docs(guide/migration): add header 2014-03-25 12:52:52 -07:00
Brian Ford 0779b6bfc0 docs(guide/concepts): improve formatting and clarity 2014-03-25 12:46:04 -07:00
Brian Ford 0b02e53ca5 docs(guide/e2e-testing): improve formatting and clarity 2014-03-25 12:38:49 -07:00
Brian Ford eeda289f0e docs(guide/ie): fix header formatting 2014-03-25 12:28:21 -07:00
Brian Ford 6e34da67cd docs(guide/i18n): improve content and formatting 2014-03-25 12:24:05 -07:00
Brian Ford a2acd794b3 docs(guide/ie): note dropping IE8 in 1.3 2014-03-25 11:45:52 -07:00
Uri Goldshtein 3982d9bcb1 docs(guide): add ui-router to complementary libraries 2014-03-25 14:03:03 -04:00
Teddy Wing ccba305ee5 docs(ngAnimate): change "&#64" to "@" symbol
Previously, we had problems with code that contained symbols that looked
like jsdoc directives.  This has now been fixed so we can convert these
HTML character codes back to @ signs.

Closes #6822
Closes #6826
2014-03-25 06:55:03 +00:00
Luke Eller 3f609f9952 docs(guides/directive): add (') to contraction
add apostrophe (') to contraction
2014-03-24 19:05:23 -04:00
cgwyllie 76dbb6e395 docs($http): fix auth default headers example 2014-03-24 18:52:51 -04:00
Renat Yakubov 4a6d4de53e fix(filter.ngdoc): Check if "input" variable is defined
By default, "greeting" textfield in this example is prepopulated with "hello" text, but it's pretty easy to copy just filter code to use it in your app. If your textfield is empty while app loads, you'll get an error: "Error: [$interpolate:interr] Can't interpolate: Reverse: {{greeting|reverse}} TypeError: Cannot read property 'length' of undefined". To prevent this, we should check "input" variable, and proceed only in case it is defined.

Closes #6819.
2014-03-24 15:13:36 -07:00
Caitlin Potter b472d0275f fix(input): don't perform HTML5 validation on updated model-value
Running html5-validation immediately after model-value is updated is incorrect, because the view
has not updated, and HTML5 constraint validation has not adjusted.

Closes #6796
Closes #6806
2014-03-24 10:54:09 -04:00
Diego Algorta 8339c2ebff docs(faq): fix link to Closure Library
The previous link throws a 404.
2014-03-23 20:39:40 +00:00
Peter Bacon Darwin 34a10c6ace chore(npm-shrinkwrap): update dgeni-packages dependency 2014-03-22 22:35:22 +00:00
John K. Paul 635cdaaa9a docs(guide/directive): fix broken link
Fix broken internal link in directive documentation.

Closes #6802
2014-03-22 16:12:26 -04:00
Sekib Omazic c7a49b34c6 docs(error/ng/btstrpd): fix typo in error page
Minimal typo fix

Closes #6803
2014-03-22 16:04:45 -04:00
jenkins 408d9583b8 chore(release): update cdn version 2014-03-21 14:22:33 -07:00
Tobias Bosch 3f2d756532 chore(CHANGELOG.md): add changelog for 1.3.0-beta.3 2014-03-21 11:16:35 -07:00
Caitlin Potter 6011145cfe test($rootScope): add assertion to test ensuring that NaN -> NaN does not throw
https://github.com/angular/angular.js/commit/fb6062fb9d83545730b993e94ac7482ffd43a62c implements a
fix for NaN values causing $watchCollection to throw an infdig error. This change updates the test
by adding an assertion which explains what is actually being tested a bit better, and may also
provide better information in the event that the test ever fails.

Closes #6758
2014-03-20 17:53:03 -04:00
Tobias Bosch b26fc23b06 chore(release): fix angularjs.org cdn script 2014-03-20 14:28:01 -07:00
Chirayu Krishnappa 0c930a1a86 fix(version-info): explicitly specify the remote
`git ls-remote --tags` assumes that you have a remote set up for your
current branch.  That isn't the case, at least for me, when I'm working
on local branches.  `grunt write` doesn't do the right thing in that
case (`git ls-remote --tags` bails out and the silent: true param makes
this a pain to debug.)  Prefer explicit to implicit.

Closes #6678.
2014-03-20 13:55:12 -07:00
alexgarrett 2f61b2f045 docs(tutorial): correct spelling mistake 2014-03-20 09:27:01 -07:00
Trevor Ewen 4d4da556eb docs($document): add a documentation example.
The $document docs are pretty empty, and this fills them out a bit. The example itself may not be
particularly useful, but it could be improved or removed later. Works for me.

Closes #6757
2014-03-19 23:42:21 -04:00
thorn0 58f5da8664 docs($q): add mention of Antroid 2.x browser
The Android 2.x browser is not ES5-compatible in that it does not allow
use of reserved words as property names. This docs fix adds Android to the
note to the `$q` docs which already make it known that string property
notation should be used when using the `finally` method on `$q`.
2014-03-19 12:39:32 -07:00
Sekib Omazic fb6062fb9d fix($rootScope): ng-repeat can't handle NaN values. #4605
$watchCollection checks if oldValue !== newValue which does not work for NaN. This was causing
infinite digest errors, since comparing NaN to NaN in $watchCollection would always return false,
indicating that a change was occuring on each loop.

This fix adds a simple check to see if the current value and previous value are both NaN, and
if so, does not count it as a change.

Closes #4605
2014-03-19 11:34:15 -07:00
Brett Porter 0c65f1ae3e test(ngMock): workaround issue with negative timestamps
In some specific timezones and operating systems, it seems that
getTimezoneOffset() can return an incorrect value for negative timestamps, as
described in #5017. While this isn't something easily fixed in the mock code,
the tests can avoid that particular timeframe by using a positive timestamp.

Closes #5017
Closes #6730
2014-03-18 21:30:29 -07:00
Jeff Balboni f40f54c6da fix(select): avoid checking option element selected properties in render
In Firefox, hovering over an option in an open select menu updates the selected property of option
elements. This means that when a render is triggered by the digest cycle, and the list of options
is being rendered, the selected properties are reset to the values from the model and the option
hovered over changes. This fix changes the code to only use DOM elements' selected properties in a
comparison when a change event has been fired. Otherwise, the internal new and existing option
arrays are used.

Closes #2448
Closes #5994
2014-03-18 23:18:42 -04:00
Caitlin Potter 37bc5ef4d8 fix(orderBy): support string predicates containing non-ident characters
The orderBy filter now allows string predicates passed to the orderBy filter to make use property
name predicates containing non-ident strings, such as spaces or percent signs, or non-latin
characters.

This behaviour requires the predicate string to be double-quoted.

In markup, this might look like so:

```html
<div ng-repeat="item in items | orderBy:'\"Tip %\"'">
...
</div>
```

Or in JS:

```js
var sorted = $filter('orderBy')(array, ['"Tip %"', '-"Subtotal $"'], false);
```

Closes #6143
Closes #6144
2014-03-18 22:54:46 -04:00
Caitlin Potter 3652831084 fix(ngCookie): convert non-string values to string
Previously, non-string values stored in $cookies would be removed, without warning the user, and
causing difficulty debugging. Now, the value is converted to string before being stored, and the
value is not dropped. Serialization may be customized using the toString() method of an object's
prototype.

Closes #6151
Closes #6220
2014-03-18 22:50:17 -04:00
Chris Constantin bc42950b51 fix(ngTouch): update workaround for desktop Webkit quirk
Fix click busting of input click triggered by a label click quickly
following a touch event on a different element, in desktop
and mobile WebKit

To reproduce the issue fixed by this commit set up a page with
 - an element with ng-click
 - a radio button (with hg-model) and associated label
In a quick sequence tap on the element and then on the label.
The radio button will not be checked, unless PREVENT_DURATION has passed

Closes #6302
2014-03-18 22:30:23 -04:00
Caitlin Potter 6680b7b97c fix($httpBackend): don't error when JSONP callback called with no parameter
This change brings Angular's JSONP behaviour closer in line with jQuery's. It will no longer treat
a callback called with no data as an error, and will no longer support IE8 via the onreadystatechanged
event.

BREAKING CHANGE:

Previously, the JSONP backend code would support IE8 by relying on the readystatechanged events. This
is no longer the case, as these events do not provide adequate useful information for deeming whether
or not a response is an error.

Previously, a JSONP response which did not pass data into the callback would be given a status of -2,
and treated as an error. Now, this situation will instead be given a status of 200, despite the lack
of data. This is useful for interaction with certain APIs.

Previously, the onload and onerror callbacks were added to the JSONP script tag. These have been
replaced with jQuery events, in order to gain access to the event object. This means that it is now
difficult to test if the callbacks are registered or not. This is possible with jQuery, using the
$.data("events") method, however it is currently impossible with jqLite. This is not expected to
break applications.

Closes #4987
Closes #6735
2014-03-18 21:49:35 -04:00
Traxmaxx c839f78b8f fix($$RAFProvider): check for webkitCancelRequestAnimationFrame
Android 4.3 only supports webkitCancelRequestAnimationFrame.

Closes #6526
2014-03-18 21:37:04 -04:00
frandroid f7ce415c67 docs(tutorial/step_05): fix services link 2014-03-19 00:24:34 +00:00
frandroid 4cf2adfeda docs(tutorial/step_05): removed stray "a" 2014-03-18 16:10:38 -07:00
Igor Minar 2b84f43a6d style(ngMocks): remove ws 2014-03-18 15:54:17 -07:00
Caio Cunha d6cfcacee1 feat(ngMock.$httpBackend): added support for function as URL matcher
It's now possible to pass a function to match the URL in $httpBackend mocked
expectations. This gives a more sophisticate control over the URL matching
without requiring complex RegExp mantainance or the workaround of creating
an object with a `test` function in order to mimic RegExp interface.

This approach was suggested in [this
thread](https://groups.google.com/d/msg/angular/3QsCUEvvxlM/Q4C4ZIqNIuEJ)

Closes #4580
2014-03-18 15:19:26 -07:00
Caio Cunha 299b220f5e feat($compile): add support for $observer deregistration
In order to make the behavior compatible with $rootScope.$watch and $rootScope.$on methods, and
make it possible to deregister an attribute observer, Attributes.$observe method now returns a
deregistration function instead of the observer itself.

BREAKING CHANGE: calling attr.$observe no longer returns the observer function, but a
    deregistration function instead.

    To migrate the code follow the example below:

    Before:

```
    directive('directiveName', function() {
      return {
        link: function(scope, elm, attr) {
          var observer = attr.$observe('someAttr', function(value) {
            console.log(value);
          });
        }
      };
    });
```

    After:

```
    directive('directiveName', function() {
      return {
        link: function(scope, elm, attr) {
          var observer = function(value) {
            console.log(value);
          };

          attr.$observe('someAttr', observer);
        }
      };
    });
```

Closes #5609
2014-03-18 13:44:16 -07:00
Igor Minar 78057a945e fix(Scope): $watchCollection should call listener with oldValue
Originally we destroyed the oldValue by incrementaly copying over portions of the newValue
into the oldValue during dirty-checking, this resulted in oldValue to be equal to newValue
by the time we called the watchCollection listener.

The fix creates a copy of the newValue each time a change is detected and then uses that
copy *the next time* a change is detected.

To make `$watchCollection` behave the same way as `$watch`, during the first iteration
the listener is called with newValue and oldValue being identical.

Since many of the corner-cases are already covered by existing tests, I refactored the
test logging to include oldValue and made the tests more readable.

Closes #2621
Closes #5661
Closes #5688
Closes #6736
2014-03-18 12:00:50 -07:00
Igor Minar c5e41a0325 chore(log): add log.empty() method to the testing logger
`log.empty()` is the same as `log.reset()`, except thati `empty()`  also returns the current array with messages

instead of:

```
// do work
expect(log).toEqual(['bar']);
log.reset();
```

do:

```
// do work
expect(log.empty()).toEqual(['bar']);
```
2014-03-18 12:00:50 -07:00
Siddique Hameed 748a6c8d9d fix(angular.bootstrap): only allow angular to load once
This is hard to test as a unit-test, since it involves the actual loading
of angular, but it turns out that it is easy to test using a protractor
e2e test.

Closes #5863
Closes #5587
2014-03-18 12:02:19 +00:00
Peter Bacon Darwin ed4cd6c3c6 chore(utils): fix version number processing
The changes to version-info meant that the version being injected into
the code at build time was missing the "dot" (patch) version and the
release code-name.
2014-03-18 10:43:17 +00:00
Peter Bacon Darwin 4cc00e7aed chore(version-info): previousVersions should not return undefined
Closes #6702
2014-03-18 07:11:59 +00:00
Peter Bacon Darwin f7b36844e6 docs(guide/concepts): move ng-app into example text
Closes #6639
2014-03-18 07:07:45 +00:00
Peter Bacon Darwin 959297c38a chore(package.json): update dgeni-packages dependency 2014-03-18 07:07:45 +00:00
Caitlin Potter f8f97f8b61 style($templateCache): remove trailing whitespace
This was introduced by 2ca6d650e8, somewhat inexplicably as I had run
grunt ci-checks locally. But regardless, this should fix this up.
2014-03-17 21:47:25 -04:00
Jesse Palmer 2ca6d650e8 docs($templateCache): use GFM example format rather than <pre> tags
Updated example formatting.

Closes #6068
2014-03-17 20:12:00 -04:00
Edward Brey 547871e779 docs(loader): add annotations to example 2014-03-17 16:38:10 -07:00
Tyler Kellogg a4b70cfd71 docs($cookies): cookies serializer only supports strings
Closes #6705
2014-03-17 16:29:10 -07:00
poshest c9fbb472b7 docs(errors/$compile/nonassing): update nonassign.ngdoc
It might seem obvious that if you don't supply "bind" attribute in this case, you'll get an error,
but I feel this is worth adding to the doc.

Closes #6725
2014-03-17 16:13:34 -07:00
Sekib Omazic df6d34c52b docs(css): RegExp doesn't have .type-hint-regexp class
type-hint-regexp gets a nice color

closes #6596
2014-03-17 16:05:40 -07:00
Gias Kay Lee ed22869e08 docs(module): add link to mentioned resource
Closes #6628
2014-03-17 15:15:51 -07:00
Gias Kay Lee ee07b502a2 docs(css): Fix word breaks issue in <pre>
Closes #6586
2014-03-17 15:08:53 -07:00
wbyoko 63ec18f5c9 docs(migration): note that services can now return functions
This change mostly effects preprocessed javascript.
2014-03-17 15:06:53 -07:00
Takashi Nakagawa e381c4dd09 chore(formatting): removed unnecessary white spaces 2014-03-17 14:57:29 -07:00
unicodesnowman 68e84acec9 docs(ngView): remove global controller definitions
instead use angular modules
also fix formatting
2014-03-17 14:55:00 -07:00
Neil Johnston e118a8be34 docs(tutorial/step_02): add experiment to update controller test
Add an experiment to update the controller unit test after modifying it
with the new model property.
2014-03-17 14:38:51 -07:00
Sekib Omazic 9202767f41 docs(booleanAttrs): fix typo 2014-03-17 14:19:40 -07:00
Jan Hancic 7fb88698dc docs(tutorial/step_12): link to API docs 2014-03-17 14:11:58 -07:00
Peter Bacon Darwin bdcc657c7e chore(angularjs.org/publish.sh): align release script with new website
Closes #6690
2014-03-17 21:09:45 +00:00
David Rogers c995b09b77 docs(ngForm): remove duplicate @param annotation
When the example for `ngAnimate` was added in commit:3344396, the `@param name` annotation was unintentionally duplicated. Remove this duplicate.

Closes #6720
2014-03-17 17:05:57 -04:00
Caitlin Potter 8a96f317e5 fix(jqLite): traverse host property for DocumentFragment in inheritedData()
If dealing with a document fragment node with a host element, and no parent, use the host
element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
to lookup parent controllers.

Closes #6637
2014-03-17 16:52:48 -04:00
Mark Jones d3aa14bc11 docs(ngInclude): make the quote type explicit 2014-03-17 13:20:06 -07:00
linclark cd49876e34 docs($http): update shortcut method description
Update docs to reflect that $http no longer requires passing in an HTTP method, as changed in #6401.
2014-03-17 13:07:15 -07:00
bradwheel 55a0bc453c docs(ngRoute): remove global controller syntax in the example 2014-03-17 12:50:14 -07:00
Igor Minar 2daaf3ea19 docs(triaging): correct information about milestones 2014-03-17 10:35:21 -07:00
Denis Parchenko e1484cdf65 docs(guide/module): remove duplicate word
Closes #6709
2014-03-17 12:44:16 -04:00
Brian Andersen d0781eb1a3 docs(tutorial): fix broken link
On page http://docs.angularjs.org/tutorial/step_05 link is broken.

Should point to http://docs.angularjs.org/guide/services NOT http://docs.angularjs.org/guide/dev_guide.services

Closes #6714
2014-03-17 12:20:22 -04:00
Peter Bacon Darwin d09056d287 docs(runnableExamples): add "edit in Plunker" button
The "runnableExample.template.html" template overrides the one in the
dgeni-packages "examples" package with a similar template that also has
a link to a special Plunker URL that can pull in the example from our
code.angularjs.org website.
2014-03-17 12:43:10 +00:00
Peter Bacon Darwin 849e4472e1 chore(shrinkwrap): grunt-jasmine-node is retrieved from github 2014-03-17 10:01:44 +00:00
Caitlin Potter f55278fa8a docs(misc/contribute): make anchor links work properly
Closes #6706
2014-03-17 00:21:28 -04:00
Brian Andersen df5624147f docs(tutorial): fix broken link
Fixed a minor error in link

Closes #6701
2014-03-16 13:35:21 -04:00
Emile Silvis 91e6d1d22f docs(guide/tutorial): make capitalization of "Angular" consistent
- step_05.ngdoc
- step_06.ngdoc
- step_07.ngdoc
- step_08.ngdoc

Closes #6686
Closes #6687
Closes #6688
Closes #6689
2014-03-15 15:30:25 -04:00
Bruno Baia b8cc71d476 fix($http): allow sending Blob data using $http
Closes #5012
2014-03-15 19:41:07 +01:00
Igor Minar 511422adb0 chore(package.json): update karma to 0.12.0 2014-03-14 23:06:04 -07:00
Peter Bacon Darwin dd3587a8c1 chore(clean-shrinkwrap): add a utility to clean up the shrinkwrap file
This is to deal with https://github.com/npm/npm/issues/3581

See the previous commit for more info.

Closes #6672
2014-03-14 23:01:54 -07:00
Igor Minar e5dd832b20 chore(npm): clean up shrinkwrap file, remove unused properties
from our experiements it appears that the presense or absense of the from and resolved properties
makes no difference on the behavior of  but  updates these properties
with different values depending on different state of the cache and node_modules.

So in order to get clean diffs during updates, we are just going to drop these properties and have
a script to do this automatically.

Long term this should be fixed in npm: https://github.com/npm/npm/issues/3581
2014-03-14 23:01:53 -07:00
jenkins a15d9cb4b6 chore(release): update cdn version 2014-03-14 17:40:25 -07:00
692 changed files with 56736 additions and 19164 deletions
+5
View File
@@ -18,6 +18,11 @@ env:
- LOGS_DIR=/tmp/angular-build/logs
- BROWSER_PROVIDER_READY_FILE=/tmp/sauce-connect-ready
install:
- npm config set registry http://23.251.144.68
# Run npm install twice, because it is flaky.
- npm install || npm install
before_script:
- mkdir -p $LOGS_DIR
- ./lib/sauce/sauce_connect_setup.sh
+872 -17
View File
@@ -1,3 +1,854 @@
<a name="1.3.0-beta.11"></a>
# 1.3.0-beta.11 transclusion-deforestation (2014-06-06)
## Bug Fixes
- **$animate:** remove the need to add `display:block !important` for `ngShow`/`ngHide`
([7c011e79](https://github.com/angular/angular.js/commit/7c011e79d8b3d805755181ace472883800234bf4),
[#3813](https://github.com/angular/angular.js/issues/3813))
- **$compile:**
- bound transclusion to correct scope
([56c60218](https://github.com/angular/angular.js/commit/56c60218d1e70e3a47e37193a4a48714eeda7d44))
- set the iteration state before linking
([0c8a2cd2](https://github.com/angular/angular.js/commit/0c8a2cd2da3a4a9f5d2ee9c25ea8ed56d74a93ab))
- don't pass transcludes to non-transclude templateUrl directives
([2ee29c5d](https://github.com/angular/angular.js/commit/2ee29c5da81ffacdc1cabb438f5d125d5e116cb9))
- don't pass transclude to template of non-transclude directive
([19af0397](https://github.com/angular/angular.js/commit/19af0397456eb8fc06dea47145fdee0e38e62f81))
- fix nested isolated transclude directives
([d414b787](https://github.com/angular/angular.js/commit/d414b787173643362c0c513a1929d8e715ca340e),
[#1809](https://github.com/angular/angular.js/issues/1809), [#7499](https://github.com/angular/angular.js/issues/7499))
- pass transcludeFn down to nested transclude directives
([1fef5fe8](https://github.com/angular/angular.js/commit/1fef5fe8230e8dc53f2c9f3f510a35cf18eeab43),
[#7240](https://github.com/angular/angular.js/issues/7240), [#7387](https://github.com/angular/angular.js/issues/7387))
- **$parse:** fix parsing error with leading space and one time bind
([24c844df](https://github.com/angular/angular.js/commit/24c844df3b6d80103b01e4847b2d55b082757feb),
[#7640](https://github.com/angular/angular.js/issues/7640))
- **angular.copy:** support circular references in the value being copied
([083f496d](https://github.com/angular/angular.js/commit/083f496d46415c01fec6dfa012da63235d0996e4),
[#7618](https://github.com/angular/angular.js/issues/7618))
- **angular.toJson:** only strip properties beginning with `$$`, not `$`
([c054288c](https://github.com/angular/angular.js/commit/c054288c9722875e3595e6e6162193e0fb67a251))
- **ngAnimate:**
- `$animate` methods should accept native DOM elements
([222d4737](https://github.com/angular/angular.js/commit/222d47370e585d9de9fa842310734ba1dd895fab))
- fix property name that is used to calculate cache key
([9f5c4370](https://github.com/angular/angular.js/commit/9f5c4370489043ed953c102340ce203a822c8b42),
[#7566](https://github.com/angular/angular.js/issues/7566))
- **ngClass:** support multiple classes in key
([7eaaca8e](https://github.com/angular/angular.js/commit/7eaaca8ef2b3db76b7c87e98d264d4b16d90a392))
- **ngIf:** ensure that the correct (transcluded) scope is used
([d71df9f8](https://github.com/angular/angular.js/commit/d71df9f83cd3882295ca01b1bb8ad7fb024165b6))
- **ngLocale:** fix i18n code-generation to support `get_vf_`, `decimals_`, and `get_wt_`
([cbab51ca](https://github.com/angular/angular.js/commit/cbab51cac5d6460938e4dfe0035d624df2208d6c))
- **ngRepeat:** ensure that the correct (transcluded) scope is used
([b87e5fc0](https://github.com/angular/angular.js/commit/b87e5fc0920915991122ba5dac87b619847b3568))
- **ngShow:** ensure that the display property is never set to `block`
([1d90744f](https://github.com/angular/angular.js/commit/1d90744f4095ee202616a30f5d6f060fc8e74b20),
[#7707](https://github.com/angular/angular.js/issues/7707))
## Features
- **$resource:** allow props beginning with `$` to be used on resources
([d3c50c84](https://github.com/angular/angular.js/commit/d3c50c845671f0f8bcc3f7842df9e2fb1d1b1c40))
## Breaking Changes
- **$resource:** due to [d3c50c84](https://github.com/angular/angular.js/commit/d3c50c845671f0f8bcc3f7842df9e2fb1d1b1c40),
If you expected `$resource` to strip these types of properties before,
you will have to manually do this yourself now.
- **angular.toJson:** due to [c054288c](https://github.com/angular/angular.js/commit/c054288c9722875e3595e6e6162193e0fb67a251),
If you expected `toJson` to strip these types of properties before,
you will have to manually do this yourself now.
<a name="1.2.17"></a>
# 1.2.17 - quantum disentanglement (2014-06-06)
## Bug Fixes
- **$animate:**
- remove the need to add `display:block!important` for `ngShow`/`ngHide`
([55b2f0e8](https://github.com/angular/angular.js/commit/55b2f0e8620465559016b424967d90a86af597c0),
[#3813](https://github.com/angular/angular.js/issues/3813))
- retain inline styles for property-specific transitions
([ad08638c](https://github.com/angular/angular.js/commit/ad08638c0ae61a22ce43d0b40e1220065b867672),
[#7503](https://github.com/angular/angular.js/issues/7503))
- ensure class-based animations always perform a DOM operation if skipped
([34d07403](https://github.com/angular/angular.js/commit/34d0740350a50ff2c3a076eaad1e8122283448c3),
[#6957](https://github.com/angular/angular.js/issues/6957))
- **$compile:**
- do not merge attrs that are the same for replace directives
([b635903e](https://github.com/angular/angular.js/commit/b635903ec435ea355b0f3688c7372627d01e23e2),
[#7463](https://github.com/angular/angular.js/issues/7463))
- pass `transcludeFn` down to nested transclude directives
([11385060](https://github.com/angular/angular.js/commit/113850602de2f8bc396df4ffd54bb0f1be565b17),
[#7240](https://github.com/angular/angular.js/issues/7240), [#7387](https://github.com/angular/angular.js/issues/7387))
- set `$isolateScope` correctly for sync template directives
([5319621a](https://github.com/angular/angular.js/commit/5319621afd0edf60aef177a0e98dbb7c282cc418),
[#6942](https://github.com/angular/angular.js/issues/6942))
- reference correct directive name in `ctreq` error
([6bea0591](https://github.com/angular/angular.js/commit/6bea0591095c19f747c08ef24cc60b34d28b2824),
[#7062](https://github.com/angular/angular.js/issues/7062), [#7067](https://github.com/angular/angular.js/issues/7067))
- fix regression which affected old jQuery releases
([a97a172e](https://github.com/angular/angular.js/commit/a97a172ee9f9bcff4d4d84854ded0c72fa0f7e9a))
- **$httpBackend:** don't error when JSONP callback is called with no parameter
([a7ccb753](https://github.com/angular/angular.js/commit/a7ccb7531c92fb976c6058aef2bb18316075efb2),
[#7031](https://github.com/angular/angular.js/issues/7031))
- **$location:**
- don't clobber path during parsing of path
([02058bfb](https://github.com/angular/angular.js/commit/02058bfbe27296c5441fc247e5a451da83c74134),
[#7199](https://github.com/angular/angular.js/issues/7199))
- fix and test html5Mode url-parsing algorithm for legacy browsers
([24f7999b](https://github.com/angular/angular.js/commit/24f7999bc16e347208aa18c418da85489286674b))
- make legacy browsers behave like modern ones in html5Mode
([e0203660](https://github.com/angular/angular.js/commit/e0203660d3af56c5a94e0a9b69c10fd5dabcf577),
[#6162](https://github.com/angular/angular.js/issues/6162), [#6421](https://github.com/angular/angular.js/issues/6421), [#6899](https://github.com/angular/angular.js/issues/6899), [#6832](https://github.com/angular/angular.js/issues/6832), [#6834](https://github.com/angular/angular.js/issues/6834))
- **angular.copy:** support circular references in the value being copied
([5c997209](https://github.com/angular/angular.js/commit/5c99720934edc35dd462b1ad02c4d0205683d917),
[#7618](https://github.com/angular/angular.js/issues/7618))
- **grunt-utils:** ensure special inline CSS works when `angular` is not a global
([d4231171](https://github.com/angular/angular.js/commit/d4231171582eb41d37bbb908eed23f074ab12f3f),
[#7176](https://github.com/angular/angular.js/issues/7176))
- **input:**
- fix `ReferenceError` in event listener
([2d7cb14a](https://github.com/angular/angular.js/commit/2d7cb14a167560edc1356dcec6f9e100ed7ac691))
- don't dirty model when input event is triggered due to a placeholder change
([109e5d1d](https://github.com/angular/angular.js/commit/109e5d1d39015af8ade1dc2aff31a2355fbab0a6),
[#2614](https://github.com/angular/angular.js/issues/2614), [#5960](https://github.com/angular/angular.js/issues/5960))
- **jqLite:** use jQuery only if `jQuery.fn.on` is present
([fafcd628](https://github.com/angular/angular.js/commit/fafcd6285a6799c4e377ea33011ae3a01aac49a6))
- **limitTo:** do not convert `Infinity` to `NaN`
([fcdac65a](https://github.com/angular/angular.js/commit/fcdac65aedfdf48dd2e11d6e5850e03ec188f068),
[#6771](https://github.com/angular/angular.js/issues/6771), [#7118](https://github.com/angular/angular.js/issues/7118))
- **ngAnimate:** `$animate` methods should accept native DOM elements
([9227a5db](https://github.com/angular/angular.js/commit/9227a5db947a78e3dbe8b91d5dac5d67444c855c))
- **ngClass:**
- support multiple classes in key
([85ce5d0d](https://github.com/angular/angular.js/commit/85ce5d0db9fc4ee5636015fc042224785f9aa997))
- handle index changes when an item is unshifted
([a4cc9e19](https://github.com/angular/angular.js/commit/a4cc9e194468573bae5232f63044459d0de6638f),
[#7256](https://github.com/angular/angular.js/issues/7256))
- **ngLocale:** fix i18n code-generation to support `get_vf_`, `decimals_`, and `get_wt_`
([96a31476](https://github.com/angular/angular.js/commit/96a314766c41bbb18bcddeddd25c8e566ab76acd))
- **ngSanitize:** encode surrogate pair properly
([3d0b49c0](https://github.com/angular/angular.js/commit/3d0b49c07f10c0a723c91629c63705647b690d81),
[#5088](https://github.com/angular/angular.js/issues/5088), [#6911](https://github.com/angular/angular.js/issues/6911))
- **ngSwitch:** properly support case labels with different numbers of transclude fns
([32aa4915](https://github.com/angular/angular.js/commit/32aa491588fe4982d4056e89a5d0dd19cf835e72))
- **numberFilter:** fix rounding error edge case
([0388eed7](https://github.com/angular/angular.js/commit/0388eed7e52fdbb832a5b4ef466420a128a43800),
[#7453](https://github.com/angular/angular.js/issues/7453), [#7478](https://github.com/angular/angular.js/issues/7478))
## Features
- **ngMock:** add support of mocha tdd interface
([6d1c6772](https://github.com/angular/angular.js/commit/6d1c67727ab872c44addc783ef1406952142d89e),
[#7489](https://github.com/angular/angular.js/issues/7489))
## Performance Improvements
- **$interpolate:** optimize value stringification
([9d4fa33e](https://github.com/angular/angular.js/commit/9d4fa33e35d73ab28a8a187e20dfbe1f77055825),
[#7501](https://github.com/angular/angular.js/issues/7501))
- **scope:** 10x. Share the child scope class.
([9ab9bf6b](https://github.com/angular/angular.js/commit/9ab9bf6b415aa216cfbfda040286e5ec99f56ee0))
<a name="1.3.0-beta.10"></a>
# 1.3.0-beta.10 excessive-clarification (2014-05-23)
## Bug Fixes
- **$animate:** retain inline styles for property-specific transitions
([98b9d68e](https://github.com/angular/angular.js/commit/98b9d68ea3ecfb521e9279c9cbfe93f8ba7d626e),
[#7503](https://github.com/angular/angular.js/issues/7503))
- **$compile:** do not merge attrs that are the same for replace directives
([1ab6e908](https://github.com/angular/angular.js/commit/1ab6e908b15470d59b52eb0ead20c755c66ec3b8),
[#7463](https://github.com/angular/angular.js/issues/7463))
- **$parse:** remove deprecated promise unwrapping
([fa6e411d](https://github.com/angular/angular.js/commit/fa6e411da26824a5bae55f37ce7dbb859653276d))
- **Scope:** $broadcast and $emit should set event.currentScope to null
([82f45aee](https://github.com/angular/angular.js/commit/82f45aee5bd84d1cc53fb2e8f645d2263cdaacbc),
[#7445](https://github.com/angular/angular.js/issues/7445), [#7523](https://github.com/angular/angular.js/issues/7523))
- **ngModel:** do not dirty the input on $commitViewValue if nothing was changed
([facd904a](https://github.com/angular/angular.js/commit/facd904a613e716151a13ab7460b5e6206e0442b),
[#7457](https://github.com/angular/angular.js/issues/7457), [#7495](https://github.com/angular/angular.js/issues/7495))
## Features
- **$interpolate:** escaped interpolation expressions
([e3f78c17](https://github.com/angular/angular.js/commit/e3f78c17d3b5d3a714402d7314094aabe7f6512a),
[#5601](https://github.com/angular/angular.js/issues/5601), [#7517](https://github.com/angular/angular.js/issues/7517))
- **{{ bindings }}:** lazy one-time binding support
([cee429f0](https://github.com/angular/angular.js/commit/cee429f0aaebf32ef1c9aedd8447a48f163dd0a4),
[#7486](https://github.com/angular/angular.js/issues/7486), [#5408](https://github.com/angular/angular.js/issues/5408))
- **ngMock:** add support of mocha tdd interface
([854bf5b7](https://github.com/angular/angular.js/commit/854bf5b74d0395f4d2e30382102d3f5d1614ea11),
[#7489](https://github.com/angular/angular.js/issues/7489))
## Performance Improvements
- **$interpolate:** optimize value stringification
([e927193d](https://github.com/angular/angular.js/commit/e927193de06500f01a2f893934250911cf1905e6),
[#7501](https://github.com/angular/angular.js/issues/7501))
## Breaking Changes
- **$compile:** due to [eec6394a](https://github.com/angular/angular.js/commit/eec6394a342fb92fba5270eee11c83f1d895e9fb), The `replace` flag for defining directives that
replace the element that they are on will be removed in the next major angular version.
This feature has difficult semantics (e.g. how attributes are merged) and leads to more
problems compared to what it solves. Also, with Web Components it is normal to have
custom elements in the DOM.
- **$parse:** due to [fa6e411d](https://github.com/angular/angular.js/commit/fa6e411da26824a5bae55f37ce7dbb859653276d),
promise unwrapping has been removed. It has been deprecated since 1.2.0-rc.3.
It can no longer be turned on.
Two methods have been removed:
* `$parseProvider.unwrapPromises`
* `$parseProvider.logPromiseWarnings`
- **Scope:** due to [82f45aee](https://github.com/angular/angular.js/commit/82f45aee5bd84d1cc53fb2e8f645d2263cdaacbc),
[#7445](https://github.com/angular/angular.js/issues/7445),
[#7523](https://github.com/angular/angular.js/issues/7523)
`$broadcast` and `$emit` will now reset the `currentScope` property of the event to
null once the event finished propagating. If any code depends on asynchronously accessing their
`currentScope` property, it should be migrated to use `targetScope` instead. All of these cases
should be considered programming bugs.
<a name="1.3.0-beta.9"></a>
# 1.3.0-beta.9 release-naming (2014-05-16)
## Bug Fixes
- **$compile:** pass `transcludeFn` down to nested transclude directives
([4f03dc5a](https://github.com/angular/angular.js/commit/4f03dc5a9650f3f22f78b438474322b4b8871dec),
[#7240](https://github.com/angular/angular.js/issues/7240), [#7387](https://github.com/angular/angular.js/issues/7387))
- **jqLite:** use jQuery only if jQuery.fn.on present
([e9bc51cb](https://github.com/angular/angular.js/commit/e9bc51cb0964ea682c1654919174dacebd09fcf6))
- **ngClass:** handle index changes when an item is unshifted
([5fbd618c](https://github.com/angular/angular.js/commit/5fbd618c2ff0dbaa4e19d0fd0e55921ce7d89478),
[#7256](https://github.com/angular/angular.js/issues/7256))
- **ngMessages:** annotate ngMessages controller for minification
([0282ca97](https://github.com/angular/angular.js/commit/0282ca971df7923c8f3dba0eb0df544e244e5b93))
- **numberFilter:** fix rounding error edge case
([81d427b5](https://github.com/angular/angular.js/commit/81d427b5f0d3502f65e8db5beaa5ad837c9ede17),
[#7453](https://github.com/angular/angular.js/issues/7453), [#7478](https://github.com/angular/angular.js/issues/7478))
## Features
- **ngTouch:** add optional `ngSwipeDisableMouse` attribute to `ngSwipe` directives to ignore mouse events.
([5a568b4f](https://github.com/angular/angular.js/commit/5a568b4f960cc5381b3911e3a6423aff2ff7f7f9),
[#6627](https://github.com/angular/angular.js/issues/6627), [#6626](https://github.com/angular/angular.js/issues/6626))
## Breaking Changes
- **jqLite:** due to [d71dbb1a](https://github.com/angular/angular.js/commit/d71dbb1ae50f174680533492ce4c7db3ff74df00),
the jQuery `detach()` method does not trigger the `$destroy` event.
If you want to destroy Angular data attached to the element, use `remove()`.
<a name="1.3.0-beta.8"></a>
# 1.3.0-beta.8 accidental-haiku (2014-05-09)
## Bug Fixes
- **$compile:** set $isolateScope correctly for sync template directives
([562c4e42](https://github.com/angular/angular.js/commit/562c4e424b0ed5f8d4bffba0cd18e66db2059043),
[#6942](https://github.com/angular/angular.js/issues/6942))
- **$httpBackend:** Add missing expectHEAD() method
([e1d61784](https://github.com/angular/angular.js/commit/e1d6178457045e721872022f71227b277cb88726),
[#7320](https://github.com/angular/angular.js/issues/7320))
- **$interpolate:** don't ReferenceError when context is undefined
([924ee6db](https://github.com/angular/angular.js/commit/924ee6db06a2518224caada86769efedd21c0710),
[#7230](https://github.com/angular/angular.js/issues/7230), [#7237](https://github.com/angular/angular.js/issues/7237))
- **grunt-utils:** ensure special inline CSS works when `angular` is not a global
([af72f40a](https://github.com/angular/angular.js/commit/af72f40a5512daa97c1f175a59b547c33cff1dc0),
[#7176](https://github.com/angular/angular.js/issues/7176))
- **injector:** invoke config blocks for module after all providers
([c0b4e2db](https://github.com/angular/angular.js/commit/c0b4e2db9cbc8bc3164cedc4646145d3ab72536e),
[#7139](https://github.com/angular/angular.js/issues/7139), [#7147](https://github.com/angular/angular.js/issues/7147))
- **ngModelOptions:**
- enable overriding the default with a debounce of zero
([c56e32a7](https://github.com/angular/angular.js/commit/c56e32a7fa44e2edd2c70f663906720c7c9ad898),
[#7205](https://github.com/angular/angular.js/issues/7205))
- initialize ngModelOptions in prelink
([fbf5ab8f](https://github.com/angular/angular.js/commit/fbf5ab8f17d28efeadb492c5a252f0778643f072),
[#7281](https://github.com/angular/angular.js/issues/7281), [#7292](https://github.com/angular/angular.js/issues/7292))
- **ngSanitize:** encode surrogate pair properly
([627b0354](https://github.com/angular/angular.js/commit/627b0354ec35bef5c6dbfab6469168c2fadcbee5),
[#5088](https://github.com/angular/angular.js/issues/5088), [#6911](https://github.com/angular/angular.js/issues/6911))
- **ngSrc, ngSrcset:** only interpolate if all expressions are defined
([8d180383](https://github.com/angular/angular.js/commit/8d180383014cbe38d58ff3eab083f51cfcfb8dde),
[#6984](https://github.com/angular/angular.js/issues/6984))
- **ngSwitch:** properly support case labels with different numbers of transclude fns
([ac37915e](https://github.com/angular/angular.js/commit/ac37915ef64c60ec8f8d4e49e4d61d7baeb96ba0),
[#7372](https://github.com/angular/angular.js/issues/7372), [#7373](https://github.com/angular/angular.js/issues/7373))
## Features
- **$compile:** allow SVG and MathML templates via special `type` property
([f0e12ea7](https://github.com/angular/angular.js/commit/f0e12ea7fea853192e4eead00b40d6041c5f914a),
[#7265](https://github.com/angular/angular.js/issues/7265))
- **$interpolate:** add optional allOrNothing param
([c2362e3f](https://github.com/angular/angular.js/commit/c2362e3f45e732a9defdb0ea59ce4ec5236fcd3a))
- **FormController:** commit `$viewValue` of all child controls when form is submitted
([a0ae07bd](https://github.com/angular/angular.js/commit/a0ae07bd4ee8d98654df4eb261d16ca55884e374),
[#7017](https://github.com/angular/angular.js/issues/7017))
- **NgMessages:** introduce the NgMessages module and directives
([0f4016c8](https://github.com/angular/angular.js/commit/0f4016c84a47e01a0fb993867dfd0a64828c089c))
## Breaking Changes
- **$http:** due to [ad4336f9](https://github.com/angular/angular.js/commit/ad4336f9359a073e272930f8f9bcd36587a8648f),
Previously, it was possible to register a response interceptor like so:
```js
// register the interceptor as a service
$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
return function(promise) {
return promise.then(function(response) {
// do something on success
return response;
}, function(response) {
// do something on error
if (canRecover(response)) {
return responseOrNewPromise
}
return $q.reject(response);
});
}
});
$httpProvider.responseInterceptors.push('myHttpInterceptor');
```
Now, one must use the newer API introduced in v1.1.4 (4ae46814), like so:
```js
$provide.factory('myHttpInterceptor', function($q) {
return {
response: function(response) {
// do something on success
return response;
},
responseError: function(response) {
// do something on error
if (canRecover(response)) {
return responseOrNewPromise
}
return $q.reject(response);
}
};
});
$httpProvider.interceptors.push('myHttpInterceptor');
```
More details on the new interceptors API (which has been around as of v1.1.4) can be found at
https://docs.angularjs.org/api/ng/service/$http#interceptors
- **injector:** due to [c0b4e2db](https://github.com/angular/angular.js/commit/c0b4e2db9cbc8bc3164cedc4646145d3ab72536e),
Previously, config blocks would be able to control behaviour of provider registration, due to being
invoked prior to provider registration. Now, provider registration always occurs prior to configuration
for a given module, and therefore config blocks are not able to have any control over a providers
registration.
**Example**:
Previously, the following:
```js
angular.module('foo', [])
.provider('$rootProvider', function() {
this.$get = function() { ... }
})
.config(function($rootProvider) {
$rootProvider.dependentMode = "B";
})
.provider('$dependentProvider', function($rootProvider) {
if ($rootProvider.dependentMode === "A") {
this.$get = function() {
// Special mode!
}
} else {
this.$get = function() {
// something else
}
}
});
```
would have "worked", meaning behaviour of the config block between the registration of "$rootProvider"
and "$dependentProvider" would have actually accomplished something and changed the behaviour of the
app. This is no longer possible within a single module.
- **ngModelOptions:** due to [adfc322b](https://github.com/angular/angular.js/commit/adfc322b04a58158fb9697e5b99aab9ca63c80bb),
This commit changes the API on `NgModelController`, both semantically and
in terms of adding and renaming methods.
* `$setViewValue(value)` -
This method still changes the `$viewValue` but does not immediately commit this
change through to the `$modelValue` as it did previously.
Now the value is committed only when a trigger specified in an associated
`ngModelOptions` directive occurs. If `ngModelOptions` also has a `debounce` delay
specified for the trigger then the change will also be debounced before being
committed.
In most cases this should not have a significant impact on how `NgModelController`
is used: If `updateOn` includes `default` then `$setViewValue` will trigger
a (potentially debounced) commit immediately.
* `$cancelUpdate()` - is renamed to `$rollbackViewValue()` and has the same meaning,
which is to revert the current `$viewValue` back to the `$lastCommittedViewValue`,
to cancel any pending debounced updates and to re-render the input.
To migrate code that used `$cancelUpdate()` follow the example below:
Before:
```js
$scope.resetWithCancel = function (e) {
if (e.keyCode == 27) {
$scope.myForm.myInput1.$cancelUpdate();
$scope.myValue = '';
}
};
```
After:
```js
$scope.resetWithCancel = function (e) {
if (e.keyCode == 27) {
$scope.myForm.myInput1.$rollbackViewValue();
$scope.myValue = '';
}
}
```
<a name="v1.3.0-beta.7"></a>
# v1.3.0-beta.7 proper-attribution (2014-04-25)
## Bug Fixes
- **$location:** don't clobber path during parsing of path
([498835a1](https://github.com/angular/angular.js/commit/498835a1c4d0dc6397df4dd667796b09565fedf4),
[#7199](https://github.com/angular/angular.js/issues/7199))
## Performance Improvements
- **scope:** ~10x speedup from sharing the child scope class.
([8377e818](https://github.com/angular/angular.js/commit/8377e81827a840b9eb64f119de4bcbaba0ceb3be))
<a name="v1.3.0-beta.6"></a>
# v1.3.0-beta.6 expedient-caffeination (2014-04-21)
## Bug Fixes
- **$animate:** ensure class-based animations always perform a domOperation if skipped
([708f2ba9](https://github.com/angular/angular.js/commit/708f2ba9843b665e417b93c7df907194565db991),
[#6957](https://github.com/angular/angular.js/issues/6957))
- **$compile:**
- reference correct directive name in ctreq error
([1192531e](https://github.com/angular/angular.js/commit/1192531e9b48cd90cbb601b0c0fdeb12340c1885),
[#7062](https://github.com/angular/angular.js/issues/7062), [#7067](https://github.com/angular/angular.js/issues/7067))
- fix regression which affected old jQuery releases
([ef64169d](https://github.com/angular/angular.js/commit/ef64169db32ffdf5e0e3ae2154ac434c6a55378b))
- **$location:**
- fix and test html5Mode url-parsing algorithm for legacy browsers
([49e7c32b](https://github.com/angular/angular.js/commit/49e7c32bb45ce3984df6768ba7b2f6a723a4ebe7))
- make legacy browsers behave like modern ones in html5Mode
([3f047704](https://github.com/angular/angular.js/commit/3f047704c70a957596371fec554d3e1fb066a29d),
[#6162](https://github.com/angular/angular.js/issues/6162), [#6421](https://github.com/angular/angular.js/issues/6421), [#6899](https://github.com/angular/angular.js/issues/6899), [#6832](https://github.com/angular/angular.js/issues/6832), [#6834](https://github.com/angular/angular.js/issues/6834))
- **input:** don't dirty model when input event triggered due to placeholder change
([ff428e72](https://github.com/angular/angular.js/commit/ff428e72837c85b9540ee9e5a3daa2c9477c90bb),
[#2614](https://github.com/angular/angular.js/issues/2614), [#5960](https://github.com/angular/angular.js/issues/5960))
- **limitTo:** do not convert Infinity to NaN
([5dee9e4a](https://github.com/angular/angular.js/commit/5dee9e4a33ab2a0be6d8a8099297be3028771e0b),
[#6771](https://github.com/angular/angular.js/issues/6771), [#7118](https://github.com/angular/angular.js/issues/7118))
- **ngModelController:** introduce $cancelUpdate to cancel pending updates
([940fcb40](https://github.com/angular/angular.js/commit/940fcb4090e96824a4abc50252aa36aaf239e937),
[#6994](https://github.com/angular/angular.js/issues/6994), [#7014](https://github.com/angular/angular.js/issues/7014))
## Features
- **$resource:** Make stripping of trailing slashes configurable.
([3878be52](https://github.com/angular/angular.js/commit/3878be52f6d95fca4c386d4a5523f3c8fcb04270))
- **Scope:** add `$watchGroup` method for observing a set of expressions
([21f93163](https://github.com/angular/angular.js/commit/21f93163384f36fc4ae0934387339380e3dc3e9c))
- **injector:** "strict-DI" mode which disables "automatic" function annotation
([4b1695ec](https://github.com/angular/angular.js/commit/4b1695ec61aac8de7fcac1dfe8b4b420f9842c38),
[#6719](https://github.com/angular/angular.js/issues/6719), [#6717](https://github.com/angular/angular.js/issues/6717), [#4504](https://github.com/angular/angular.js/issues/4504), [#6069](https://github.com/angular/angular.js/issues/6069), [#3611](https://github.com/angular/angular.js/issues/3611))
- **ngModelOptions:** custom triggers and debounce of ngModel updates
([dbe381f2](https://github.com/angular/angular.js/commit/dbe381f29fc72490f8e3a5328d5c487b185fe652),
[#1285](https://github.com/angular/angular.js/issues/1285))
## Performance Improvements
- **$compile:** watch interpolated expressions individually
([0ebfa0d1](https://github.com/angular/angular.js/commit/0ebfa0d112c8ba42242cb8353db91e93eb42b463))
- **$interpolate:** speed up interpolation by recreating watchGroup approach
([546cb429](https://github.com/angular/angular.js/commit/546cb429d9cea25a9bdadbb87dfd401366b0b908))
## Breaking Changes
- **$interpolate:** due to [88c2193c](https://github.com/angular/angular.js/commit/88c2193c71954b9e7e7e4bdf636a2b168d36300d),
the function returned by `$interpolate`
no longer has a `.parts` array set on it.
Instead it has two arrays:
* `.expressions`, an array of the expressions in the
interpolated text. The expressions are parsed with
`$parse`, with an extra layer converting them to strings
when computed
* `.separators`, an array of strings representing the
separations between interpolations in the text.
This array is **always** 1 item longer than the
`.expressions` array for easy merging with it
<a name="1.3.0-beta.5"></a>
# 1.3.0-beta.5 chimeric-glitterfication (2014-04-03)
## Bug Fixes
- **$animate:**
- insert elements at the start of the parent container instead of at the end
([1cb8584e](https://github.com/angular/angular.js/commit/1cb8584e8490ecdb1b410a8846c4478c6c2c0e53),
[#4934](https://github.com/angular/angular.js/issues/4934), [#6275](https://github.com/angular/angular.js/issues/6275))
- ensure the CSS driver properly works with SVG elements
([c67bd69c](https://github.com/angular/angular.js/commit/c67bd69c58812da82b1a3a31d430df7aad8a50a8),
[#6030](https://github.com/angular/angular.js/issues/6030))
- **$parse:** mark constant unary minus expressions as constant
([7914d346](https://github.com/angular/angular.js/commit/7914d3463b5ec560c616a0c9fd008bc0e3f7c786),
[#6932](https://github.com/angular/angular.js/issues/6932))
- **Scope:**
- revert the `__proto__` cleanup as that could cause regressions
([71c11e96](https://github.com/angular/angular.js/commit/71c11e96c64d5d4eb71f48c1eb778c2ba5c63377))
- more scope clean up on $destroy to minimize leaks
([d64d41ed](https://github.com/angular/angular.js/commit/d64d41ed992430a4fc89cd415c03acf8d56022e6),
[#6794](https://github.com/angular/angular.js/issues/6794), [#6856](https://github.com/angular/angular.js/issues/6856), [#6968](https://github.com/angular/angular.js/issues/6968))
- **ngClass:** handle ngClassOdd/Even affecting the same classes
([c9677920](https://github.com/angular/angular.js/commit/c9677920d462046710fc72ca422ab7400f551d2e),
[#5271](https://github.com/angular/angular.js/issues/5271))
## Breaking Changes
- **$animate:** due to [1cb8584e](https://github.com/angular/angular.js/commit/1cb8584e8490ecdb1b410a8846c4478c6c2c0e53),
`$animate` will no longer default the after parameter to the last element of the parent
container. Instead, when after is not specified, the new element will be inserted as the
first child of the parent container.
To update existing code, change all instances of `$animate.enter()` or `$animate.move()` from:
`$animate.enter(element, parent);`
to:
`$animate.enter(element, parent, angular.element(parent[0].lastChild));`
<a name="1.2.16"></a>
# 1.2.16 badger-enumeration (2014-04-03)
## Bug Fixes
- **$animate:**
- ensure the CSS driver properly works with SVG elements
([38ea5426](https://github.com/angular/angular.js/commit/38ea542662b2b74703d583e3a637d65369fc26eb),
[#6030](https://github.com/angular/angular.js/issues/6030))
- prevent cancellation timestamp from being too far in the future
([35d635cb](https://github.com/angular/angular.js/commit/35d635cbcbdc20f304781655f3563111afa6567f),
[#6748](https://github.com/angular/angular.js/issues/6748))
- run CSS animations before JS animations to avoid style inheritance
([0e5106ec](https://github.com/angular/angular.js/commit/0e5106ec2ccc8596c589b89074d3b27d27bf395a),
[#6675](https://github.com/angular/angular.js/issues/6675))
- **$parse:** mark constant unary minus expressions as constant
([6e420ff2](https://github.com/angular/angular.js/commit/6e420ff28d9b3e76ac2c3598bf3797540ef8a1d3),
[#6932](https://github.com/angular/angular.js/issues/6932))
- **Scope:**
- revert the __proto__ cleanup as that could cause regressions
([2db66f5b](https://github.com/angular/angular.js/commit/2db66f5b695a06cff62a52e55e55d1a0a25eec2f))
- more scope clean up on $destroy to minimize leaks
([7e4e696e](https://github.com/angular/angular.js/commit/7e4e696ec3adf9d6fc77a7aa7e0909a9675fd43a),
[#6794](https://github.com/angular/angular.js/issues/6794), [#6856](https://github.com/angular/angular.js/issues/6856), [#6968](https://github.com/angular/angular.js/issues/6968))
- aggressively clean up scope on $destroy to minimize leaks
([8d4d437e](https://github.com/angular/angular.js/commit/8d4d437e8cd8d7cebab5d9ae5c8bcfeef2118ce9),
[#6794](https://github.com/angular/angular.js/issues/6794), [#6856](https://github.com/angular/angular.js/issues/6856))
- **filter.ngdoc:** Check if "input" variable is defined
([a275d539](https://github.com/angular/angular.js/commit/a275d539f9631d6ec64d03814b3b09420e6cf1ee),
[#6819](https://github.com/angular/angular.js/issues/6819))
- **input:** don't perform HTML5 validation on updated model-value
([b2363e31](https://github.com/angular/angular.js/commit/b2363e31023df8240113f68b4e01d942f8009b60),
[#6796](https://github.com/angular/angular.js/issues/6796), [#6806](https://github.com/angular/angular.js/issues/6806))
- **ngClass:** handle ngClassOdd/Even affecting the same classes
([55fe6d63](https://github.com/angular/angular.js/commit/55fe6d6331e501325c2658df8995dcc083fc4ffb),
[#5271](https://github.com/angular/angular.js/issues/5271))
## Features
- **$http:** add xhr statusText to completeRequest callback
([32c09c1d](https://github.com/angular/angular.js/commit/32c09c1d195fcb98f6e29fc7e554a867f4762301),
[#2335](https://github.com/angular/angular.js/issues/2335), [#2665](https://github.com/angular/angular.js/issues/2665), [#6713](https://github.com/angular/angular.js/issues/6713))
<a name="1.3.0-beta.4"></a>
# 1.3.0-beta.4 inconspicuous-deception (2014-03-28)
## Bug Fixes
- **$animate:**
- prevent cancellation timestamp from being too far in the future
([ff5cf736](https://github.com/angular/angular.js/commit/ff5cf736e5b8073c8121295743873ccd04cc7d6b),
[#6748](https://github.com/angular/angular.js/issues/6748))
- make CSS blocking optional for class-based animations
([1bebe36a](https://github.com/angular/angular.js/commit/1bebe36aa938890d61188762ed618b1b5e193634),
[#6674](https://github.com/angular/angular.js/issues/6674), [#6739](https://github.com/angular/angular.js/issues/6739))
- run CSS animations before JS animations to avoid style inheritance
([2317af68](https://github.com/angular/angular.js/commit/2317af68510fe3b67526282dad697ad4dc621a19),
[#6675](https://github.com/angular/angular.js/issues/6675))
- **Scope:** aggressively clean up scope on $destroy to minimize leaks
([f552f251](https://github.com/angular/angular.js/commit/f552f25171390e726ad7246ed18b994970bcf764),
[#6794](https://github.com/angular/angular.js/issues/6794), [#6856](https://github.com/angular/angular.js/issues/6856))
- **doc-gen:** Run Gulp on Windows too
([47ba6014](https://github.com/angular/angular.js/commit/47ba60146032c0bfadeaa9f3816644b31fc33315),
[#6346](https://github.com/angular/angular.js/issues/6346))
- **filter.ngdoc:** Check if "input" variable is defined
([4a6d4de5](https://github.com/angular/angular.js/commit/4a6d4de53ed1472c0cb2323292127495619d7ed9),
[#6819](https://github.com/angular/angular.js/issues/6819))
- **input:** don't perform HTML5 validation on updated model-value
([b472d027](https://github.com/angular/angular.js/commit/b472d0275f2900beba3b1f2fcee821369f8c15c1),
[#6796](https://github.com/angular/angular.js/issues/6796), [#6806](https://github.com/angular/angular.js/issues/6806))
## Features
- **$http:** add xhr statusText to completeRequest callback
([1d2414ca](https://github.com/angular/angular.js/commit/1d2414ca93a0340840ea1e80c48edb51ec55cd48),
[#2335](https://github.com/angular/angular.js/issues/2335), [#2665](https://github.com/angular/angular.js/issues/2665), [#6713](https://github.com/angular/angular.js/issues/6713))
## Breaking Changes
- **$animate:** due to [1bebe36a](https://github.com/angular/angular.js/commit/1bebe36aa938890d61188762ed618b1b5e193634),
Any class-based animation code that makes use of transitions
and uses the setup CSS classes (such as class-add and class-remove) must now
provide a empty transition value to ensure that its styling is applied right
away. In other words if your animation code is expecting any styling to be
applied that is defined in the setup class then it will not be applied
"instantly" unless a `transition:0s none` value is present in the styling
for that CSS class. This situation is only the case if a transition is already
present on the base CSS class once the animation kicks off.
Before:
.animated.my-class-add {
opacity:0;
transition:0.5s linear all;
}
.animated.my-class-add.my-class-add-active {
opacity:1;
}
After:
.animated.my-class-add {
transition:0s linear all;
opacity:0;
}
.animated.my-class-add.my-class-add-active {
transition:0.5s linear all;
opacity:1;
}
Please view the documentation for ngAnimate for more info.
<a name="1.3.0-beta.3"></a>
# 1.3.0-beta.3 emotional-waffles (2014-03-21)
## Bug Fixes
- **ngAnimate:** support `webkitCancelRequestAnimationFrame` in addition to `webkitCancelAnimationFrame`
([c839f78b](https://github.com/angular/angular.js/commit/c839f78b8f2d8d910bc2bfc9e41b3e3b67090ec1),
[#6526](https://github.com/angular/angular.js/issues/6526))
- **$http:** allow sending Blob data using `$http`
([b8cc71d4](https://github.com/angular/angular.js/commit/b8cc71d476f76ff51e719fb76fb2348027c858ce),
[#5012](https://github.com/angular/angular.js/issues/5012))
- **$httpBackend:** don't error when JSONP callback is called with no parameter
([6680b7b9](https://github.com/angular/angular.js/commit/6680b7b97c0326a80bdccaf0a35031e4af641e0e),
[#4987](https://github.com/angular/angular.js/issues/4987), [#6735](https://github.com/angular/angular.js/issues/6735))
- **$rootScope:** ng-repeat can't handle `NaN` values. #4605
([fb6062fb](https://github.com/angular/angular.js/commit/fb6062fb9d83545730b993e94ac7482ffd43a62c),
[#4605](https://github.com/angular/angular.js/issues/4605))
- **$rootScope:** `$watchCollection` should call listener with old value
([78057a94](https://github.com/angular/angular.js/commit/78057a945ef84cbb05f9417fe884cb8c28e67b44),
[#2621](https://github.com/angular/angular.js/issues/2621), [#5661](https://github.com/angular/angular.js/issues/5661), [#5688](https://github.com/angular/angular.js/issues/5688), [#6736](https://github.com/angular/angular.js/issues/6736))
- **angular.bootstrap:** allow angular to load only once
([748a6c8d](https://github.com/angular/angular.js/commit/748a6c8d9d8d61c3ee18eec462abe8ff245d6a98),
[#5863](https://github.com/angular/angular.js/issues/5863), [#5587](https://github.com/angular/angular.js/issues/5587))
- **jqLite:** `inheritedData()` now traverses Shadow DOM boundaries via the `host` property of `DocumentFragment`
([8a96f317](https://github.com/angular/angular.js/commit/8a96f317e594a5096d4fa56ceae4c685eec8ac8b),
[#6637](https://github.com/angular/angular.js/issues/6637))
- **ngCookie:** convert non-string values to string
([36528310](https://github.com/angular/angular.js/commit/3652831084c3788f786046b907a7361d2e89c520),
[#6151](https://github.com/angular/angular.js/issues/6151), [#6220](https://github.com/angular/angular.js/issues/6220))
- **ngTouch:** update workaround for Webkit quirk
([bc42950b](https://github.com/angular/angular.js/commit/bc42950b514b60f319812eeb87aae2915e394237),
[#6302](https://github.com/angular/angular.js/issues/6302))
- **orderBy:** support string predicates containing non-ident characters
([37bc5ef4](https://github.com/angular/angular.js/commit/37bc5ef4d87f19da47d3ab454c43d1e532c4f924),
[#6143](https://github.com/angular/angular.js/issues/6143), [#6144](https://github.com/angular/angular.js/issues/6144))
- **select:** avoid checking option element's `selected` property in render
([f40f54c6](https://github.com/angular/angular.js/commit/f40f54c6da4a5399fe18a89d068634bb491e9f1a),
[#2448](https://github.com/angular/angular.js/issues/2448), [#5994](https://github.com/angular/angular.js/issues/5994))
## Features
- **$compile:** add support for `$observer` deregistration
([299b220f](https://github.com/angular/angular.js/commit/299b220f5e05e1d4e26bfd58d0b2fd7329ca76b1),
[#5609](https://github.com/angular/angular.js/issues/5609))
- **ngMock.$httpBackend:** added support for function as URL matcher
([d6cfcace](https://github.com/angular/angular.js/commit/d6cfcacee101f2738e0a224a3377232ff85f78a4),
[#4580](https://github.com/angular/angular.js/issues/4580))
## Breaking Changes
- **$compile:** due to [299b220f](https://github.com/angular/angular.js/commit/299b220f5e05e1d4e26bfd58d0b2fd7329ca76b1),
calling `attr.$observe` no longer returns the observer function, but a
deregistration function instead. To migrate the code follow the example below:
Before:
directive('directiveName', function() {
return {
link: function(scope, elm, attr) {
var observer = attr.$observe('someAttr', function(value) {
console.log(value);
});
}
};
});
After:
directive('directiveName', function() {
return {
link: function(scope, elm, attr) {
var observer = function(value) {
console.log(value);
};
attr.$observe('someAttr', observer);
}
};
});
- **$httpBackend:** due to [6680b7b9](https://github.com/angular/angular.js/commit/6680b7b97c0326a80bdccaf0a35031e4af641e0e), the JSONP behavior for erroneous and empty responses changed:
Previously, a JSONP response was regarded as erroneous if it was empty. Now Angular is listening to the
correct events to detect errors, i.e. even empty responses can be successful.
<a name="v1.2.15"></a>
# v1.2.15 beer-underestimating (2014-03-21)
## Bug Fixes
- **$$RAFProvider:** check for webkitCancelRequestAnimationFrame
([e84da228](https://github.com/angular/angular.js/commit/e84da2283c4e195be557f7b06c8783fe502acbbb),
[#6526](https://github.com/angular/angular.js/issues/6526))
- **$$rAF:** always fallback to a $timeout incase native rAF isn't supported
([ee8e4a94](https://github.com/angular/angular.js/commit/ee8e4a946ed8f943e00846b88d8d51c0b2cd1fab),
[#6654](https://github.com/angular/angular.js/issues/6654))
- **$compile:** support templates with thead and tfoot root elements
([ca0ac649](https://github.com/angular/angular.js/commit/ca0ac649971ae4fb50419b38f92a98d2226eb696),
[#6289](https://github.com/angular/angular.js/issues/6289))
- **$http:**
- allow sending Blob data using $http
([fbb125a3](https://github.com/angular/angular.js/commit/fbb125a3af164e52af2f8119175b04cbbed2f331),
[#5012](https://github.com/angular/angular.js/issues/5012))
- don't covert 0 status codes to 404 for non-file protocols
([f108a2a9](https://github.com/angular/angular.js/commit/f108a2a994149ecc011e29f327bcb8e11adf72d9),
[#6074](https://github.com/angular/angular.js/issues/6074), [#6155](https://github.com/angular/angular.js/issues/6155))
- **$rootScope:**
- ng-repeat can't handle NaN values. #4605
([e48c28fe](https://github.com/angular/angular.js/commit/e48c28fe9292efe7af6205b2be116d2350990c73),
[#4605](https://github.com/angular/angular.js/issues/4605))
- $watchCollection should call listener with oldValue
([3dd95727](https://github.com/angular/angular.js/commit/3dd9572754c7bafec30dd625f5c611346959c969),
[#2621](https://github.com/angular/angular.js/issues/2621), [#5661](https://github.com/angular/angular.js/issues/5661), [#5688](https://github.com/angular/angular.js/issues/5688), [#6736](https://github.com/angular/angular.js/issues/6736))
- **angular.bootstrap:** only allow angular to load once
([0d60f8d3](https://github.com/angular/angular.js/commit/0d60f8d367e38224696749b0f7de04bd60649815),
[#5863](https://github.com/angular/angular.js/issues/5863), [#5587](https://github.com/angular/angular.js/issues/5587))
- **jqLite:** traverse `host` property for DocumentFragment in inheritedData()
([98d825e1](https://github.com/angular/angular.js/commit/98d825e10d3bf76f47e69abba857a8933c8cb7d9),
[#6637](https://github.com/angular/angular.js/issues/6637))
- **ngAnimate:** setting classNameFilter disables animation inside ng-if
([a41a2a1d](https://github.com/angular/angular.js/commit/a41a2a1d2ce20f86ac2709592e4ada527160e580),
[#6539](https://github.com/angular/angular.js/issues/6539))
- **ngCookie:** convert non-string values to string
([93d1c95c](https://github.com/angular/angular.js/commit/93d1c95c61dbfa565333bb64527a103242175af7),
[#6151](https://github.com/angular/angular.js/issues/6151), [#6220](https://github.com/angular/angular.js/issues/6220))
- **ngTouch:** update workaround for desktop Webkit quirk
([01a34f51](https://github.com/angular/angular.js/commit/01a34f513bb567ed6d4c81d00d7c2a777c0dae01),
[#6302](https://github.com/angular/angular.js/issues/6302))
- **orderBy:** support string predicates containing non-ident characters
([10d3e1e4](https://github.com/angular/angular.js/commit/10d3e1e4472ab9f5cf4418b6438ec2e0f2b0b288),
[#6143](https://github.com/angular/angular.js/issues/6143), [#6144](https://github.com/angular/angular.js/issues/6144))
- **select:** avoid checking option element selected properties in render
([dc149de9](https://github.com/angular/angular.js/commit/dc149de9364c66b988f169f67cad39577ba43434),
[#2448](https://github.com/angular/angular.js/issues/2448), [#5994](https://github.com/angular/angular.js/issues/5994), [#6769](https://github.com/angular/angular.js/issues/6769))
<a name="1.3.0-beta.2"></a>
# 1.3.0-beta.2 silent-ventriloquism (2014-03-14)
@@ -333,26 +1184,30 @@ The animation mock module has been renamed from `mock.animate` to `ngAnimateMock
## Breaking Changes
- **$http:** due to [e1cfb195](https://github.com/angular/angular.js/commit/e1cfb1957feaf89408bccf48fae6f529e57a82fe),
it is now necessary to separately specify default HTTP headers for PUT, POST and PATCH requests, as these no longer share a single object.
it is now necessary to seperately specify default HTTP headers for PUT, POST and PATCH requests, as these no longer share a single object.
To migrate your code, follow the example below:
To migrate your code, follow the example below:
Before:
Before:
// Will apply to POST, PUT and PATCH methods
$httpProvider.defaults.headers.post = {
"X-MY-CSRF-HEADER": "..."
};
```
// Will apply to POST, PUT and PATCH methods
$httpProvider.defaults.headers.post = {
"X-MY-CSRF-HEADER": "..."
};
```
After:
After:
// POST, PUT and PATCH default headers must be specified separately,
// as they do not share data.
$httpProvider.defaults.headers.post =
$httpProvider.defaults.headers.put =
$httpProviders.defaults.headers.patch = {
"X-MY-CSRF-HEADER": "..."
};
```
// POST, PUT and PATCH default headers must be specified seperately,
// as they do not share data.
$httpProvider.defaults.headers.post =
$httpProvider.defaults.headers.put =
$httpProviders.defaults.headers.patch = {
"X-MY-CSRF-HEADER": "..."
};
```
<a name="1.2.8"></a>
# 1.2.8 interdimensional-cartography (2014-01-10)
@@ -3667,7 +4522,7 @@ behavior and migrate your controllers one at a time: <https://gist.github.com/16
([commit](https://github.com/angular/angular.js/commit/78656fe0dfc99c341ce02d71e7006e9c05b1fe3f))
- fn signature change for change listener functions registered via `scope.$watch` - this means that
the scope object can be listed in the arguments list only if its needed and skipped otherwise.
the scope object can be listed in the arguments list only if it's needed and skipped otherwise.
([commit](https://github.com/angular/angular.js/commit/0196411dbe179afe24f4faa6d6503ff3f69472da))
- before: `scope.$watch('someModel', function(scope, newVal, oldVal) {})`
@@ -4601,7 +5456,7 @@ with the `$route` service
- docs app UI polishing with dual scrolling and other improvements
### Bug Fixes
- `select` widget now behaves correctly when it's `option` items are created via `ng:repeat`
- `select` widget now behaves correctly when its `option` items are created via `ng:repeat`
(issue #170)
- fix for async xhr cache issue #152 by adding `$browser.defer` and `$defer` service
+43 -35
View File
@@ -23,7 +23,7 @@ discussion list or [StackOverflow][stackoverflow]. We are also available on [IRC
## <a name="issue"></a> Found an Issue?
If you find a bug in the source code or a mistake in the documentation, you can help us by
submitting and issue to our [GitHub Repository][github]. Even better you can submit a Pull Request
submitting an issue to our [GitHub Repository][github]. Even better you can submit a Pull Request
with a fix.
***Localization Issue:*** *Angular.js uses the [Google Closure I18N library], to generate its own I18N files. This means that
@@ -40,17 +40,18 @@ would like to implement a new feature then consider what kind of change it is:
[dev mailing list][angular-dev] or [IRC][irc] so that we can better coordinate our efforts, prevent
duplication of work, and help you to craft the change so that it is successfully accepted into the
project.
* **Small Changes** can be crafted and submitted to [GitHub Repository][github] as a Pull Request.
* **Small Changes** can be crafted and submitted to the [GitHub Repository][github] as a Pull Request.
## <a name="docs"></a> Want a Doc Fix?
If you want to help improve the docs, it's a good idea to let others know what you're working on to
minimize duplication of effort. Before starting, check out the issue queue for [Milestone:Docs Only](https://github.com/angular/angular.js/issues?milestone=24&state=open).
If you want to help improve the docs, it's a good idea to let others know what you're working on to
minimize duplication of effort. Before starting, check out the issue queue for
[Milestone:Docs Only](https://github.com/angular/angular.js/issues?milestone=24&state=open).
Comment on an issue to let others know what you're working on, or create a new issue if your work
doesn't fit within the scope of any of the existing doc fix projects.
For large fixes, please build and test the documentation before submitting the PR to be sure you haven't
accidentally introduced any layout or formatting issues.You should also make sure that your commit message
accidentally introduced any layout or formatting issues. You should also make sure that your commit message
is labeled "docs:" and follows the **Git Commit Guidelines** outlined below.
If you're just making a small change, don't worry about filing an issue first. Use the friendly blue "Improve this doc" button at the top right of the doc page to fork the repository in-place and make a quick change on the fly.
@@ -84,7 +85,7 @@ Before you submit your pull request consider the following guidelines:
* Search [GitHub](https://github.com/angular/angular.js/pulls) for an open or closed Pull Request
that relates to your submission. You don't want to duplicate effort.
* Please sign our [Contributor License Agreement (CLA)](#signing-the-cla) before sending pull
* Please sign our [Contributor License Agreement (CLA)](#cla) before sending pull
requests. We cannot accept code without this.
* Make your changes in a new git branch
@@ -92,16 +93,19 @@ Before you submit your pull request consider the following guidelines:
git checkout -b my-fix-branch master
```
* Create your patch, including appropriate test cases.
* Follow our [Coding Rules](#coding-rules)
* Commit your changes and create a descriptive commit message (the
commit message is used to generate release notes, please check out our
[commit message conventions](#commit-message-format) and our commit message presubmit hook
`validate-commit-msg.js`):
* Create your patch, **including appropriate test cases**.
* Follow our [Coding Rules](#rules).
* Run the full Angular test suite, as described in the [developer documentation][dev-doc],
and ensure that all tests pass.
* Commit your changes using a descriptive commit message that follows our
[commit message conventions](#commit-message-format) and passes our commit message presubmit hook
`validate-commit-msg.js`. Adherence to the [commit message conventions](#commit-message-format)
is required because release notes are automatically generated from these messages.
```shell
git commit -a
```
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
* Build your changes locally to ensure all the tests pass
@@ -109,15 +113,17 @@ Before you submit your pull request consider the following guidelines:
grunt test
```
* Push your branch to Github:
* Push your branch to GitHub:
```shell
git push origin my-fix-branch
```
* In Github, send a pull request to `angular:master`.
* If we suggest changes then you can modify your branch, rebase and force a new push to your GitHub
repository to update the Pull Request:
* In GitHub, send a pull request to `angular:master`.
* If we suggest changes then
* Make the required updates.
* Re-run the Angular test suite to ensure tests are still passing.
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
```shell
git rebase master -i
@@ -126,10 +132,12 @@ Before you submit your pull request consider the following guidelines:
That's it! Thank you for your contribution!
When the patch is reviewed and merged, you can safely delete your branch and pull the changes
#### After your pull request is merged
After your pull request is merged, you can safely delete your branch and pull the changes
from the main (upstream) repository:
* Delete the remote branch on Github:
* Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:
```shell
git push origin --delete my-fix-branch
@@ -245,24 +253,24 @@ You can find out more detailed information about contributing in the
[github]: https://github.com/angular/angular.js
[Google Closure I18N library]: https://code.google.com/p/closure-library/source/browse/closure/goog/i18n/
[list]: https://groups.google.com/forum/?fromgroups#!forum/angular
[contribute]: http://docs.angularjs.org/misc/contribute
[stackoverflow]: http://stackoverflow.com/questions/tagged/angularjs
[groups]: https://groups.google.com/forum/?fromgroups#!forum/angular
[Google Closure I18N library]: https://github.com/google/closure-library/tree/master/closure/goog/i18n
[angular-dev]: https://groups.google.com/forum/?fromgroups#!forum/angular-dev
[irc]: http://webchat.freenode.net/?channels=angularjs&uio=d4
[plunker]: http://plnkr.co/edit
[jsfiddle]: http://jsfiddle.net/
[ngDocs]: https://github.com/angular/angular.js/wiki/Writing-AngularJS-Documentation
[unit-testing]: http://docs.angularjs.org/guide/dev_guide.unit-testing
[js-style-guide]: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
[contributing]: http://docs.angularjs.org/misc/contribute
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
[github-pr-helper]: https://chrome.google.com/webstore/detail/github-pr-helper/mokbklfnaddkkbolfldepnkfmanfhpen
[coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
[contribute]: http://docs.angularjs.org/misc/contribute
[contributing]: http://docs.angularjs.org/misc/contribute
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
[dev-doc]: https://docs.angularjs.org/guide
[github]: https://github.com/angular/angular.js
[groups]: https://groups.google.com/forum/?fromgroups#!forum/angular
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
[irc]: http://webchat.freenode.net/?channels=angularjs&uio=d4
[js-style-guide]: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
[jsfiddle]: http://jsfiddle.net/
[list]: https://groups.google.com/forum/?fromgroups#!forum/angular
[ngDocs]: https://github.com/angular/angular.js/wiki/Writing-AngularJS-Documentation
[plunker]: http://plnkr.co/edit
[stackoverflow]: http://stackoverflow.com/questions/tagged/angularjs
[unit-testing]: https://docs.angularjs.org/guide/unit-testing
[![Analytics](https://ga-beacon.appspot.com/UA-8594346-11/angular.js/CONTRIBUTING.md?pixel)](https://github.com/igrigorik/ga-beacon)
+17 -3
View File
@@ -10,7 +10,7 @@ module.exports = function(grunt) {
grunt.loadTasks('lib/grunt');
var NG_VERSION = versionInfo.currentVersion;
NG_VERSION.cdn = versionInfo.currentPackage.cdnVersion;
NG_VERSION.cdn = versionInfo.cdnVersion;
var dist = 'angular-'+ NG_VERSION.full;
//global beforeEach
@@ -107,6 +107,9 @@ module.exports = function(grunt) {
options: {
jshintrc: true,
},
tests: {
files: { src: 'test/**/*.js' },
},
ng: {
files: { src: files['angularSrc'] },
},
@@ -119,6 +122,9 @@ module.exports = function(grunt) {
ngLocale: {
files: { src: 'src/ngLocale/**/*.js' },
},
ngMessages: {
files: { src: 'src/ngMessages/**/*.js' },
},
ngMock: {
files: { src: 'src/ngMock/**/*.js' },
},
@@ -187,6 +193,10 @@ module.exports = function(grunt) {
dest: 'build/angular-resource.js',
src: util.wrap(files['angularModules']['ngResource'], 'module')
},
messages: {
dest: 'build/angular-messages.js',
src: util.wrap(files['angularModules']['ngMessages'], 'module')
},
animate: {
dest: 'build/angular-animate.js',
src: util.wrap(files['angularModules']['ngAnimate'], 'module')
@@ -211,6 +221,7 @@ module.exports = function(grunt) {
animate: 'build/angular-animate.js',
cookies: 'build/angular-cookies.js',
loader: 'build/angular-loader.js',
messages: 'build/angular-messages.js',
touch: 'build/angular-touch.js',
resource: 'build/angular-resource.js',
route: 'build/angular-route.js',
@@ -220,8 +231,11 @@ module.exports = function(grunt) {
"ddescribe-iit": {
files: [
'src/**/*.js',
'test/**/*.js',
'!test/ngScenario/DescribeSpec.js'
'!test/ngScenario/DescribeSpec.js',
'!src/ng/directive/attrs.js', // legitimate xit here
'!src/ngScenario/**/*.js'
]
},
@@ -279,7 +293,7 @@ module.exports = function(grunt) {
//alias tasks
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'package','test:unit','test:promises-aplus', 'tests:docs', 'test:protractor']);
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'jscs', 'package','test:unit','test:promises-aplus', 'tests:docs', 'test:protractor']);
grunt.registerTask('test:jqlite', 'Run the unit tests with Karma' , ['tests:jqlite']);
grunt.registerTask('test:jquery', 'Run the jQuery unit tests with Karma', ['tests:jquery']);
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', ['tests:modules']);
+1 -1
View File
@@ -1,4 +1,4 @@
AngularJS [![Build Status](https://travis-ci.org/angular/angular.js.png?branch=master)](https://travis-ci.org/angular/angular.js)
AngularJS [![Build Status](https://travis-ci.org/angular/angular.js.svg?branch=master)](https://travis-ci.org/angular/angular.js)
=========
AngularJS lets you write client-side web applications as if you had a smarter browser. It lets you
+4 -4
View File
@@ -34,7 +34,7 @@ This process based on the idea of minimizing user pain
* Check if there are comments that link to a dupe. If so verify that this is indeed a dupe, [close it][], and go to the last step.
1. Bugs:
* Label `Type: Bug`
* Reproducible? - Steps to reproduce the bug are clear. If they are not,
* Reproducible? - Steps to reproduce the bug are clear. If they are not, ask for a clarification. If there's no reply after a week, [close it][].
* Reproducible on master? - <http://code.angularjs.org/snapshot/>
1. Non bugs:
@@ -61,9 +61,9 @@ This process based on the idea of minimizing user pain
1. Label `origin: google` for issues from Google
1. Assign a milestone:
* Current 1.x.y milestone - regressions and urgent bugs only
* Backlog - fixes; changes that should go into a patch release
* Ice Box - new features; changes that belong inß a major/minor release
* Backlog - triaged fixes and features, should be the default choice
* Current 1.x.y milestone (e.g. 1.3.0-beta-2) - regressions and urgent bugs only
1. Unassign yourself from the issue
+6 -1
View File
@@ -44,7 +44,7 @@ angularFiles = {
'src/ng/directive/directives.js',
'src/ng/directive/a.js',
'src/ng/directive/booleanAttrs.js',
'src/ng/directive/attrs.js',
'src/ng/directive/form.js',
'src/ng/directive/input.js',
'src/ng/directive/ngBind.js',
@@ -80,6 +80,9 @@ angularFiles = {
'ngCookies': [
'src/ngCookies/cookies.js'
],
'ngMessages': [
'src/ngMessages/messages.js'
],
'ngResource': [
'src/ngResource/resource.js'
],
@@ -128,6 +131,7 @@ angularFiles = {
'test/auto/*.js',
'test/ng/**/*.js',
'test/ngAnimate/*.js',
'test/ngMessages/*.js',
'test/ngCookies/*.js',
'test/ngResource/*.js',
'test/ngRoute/**/*.js',
@@ -189,6 +193,7 @@ angularFiles = {
angularFiles['angularSrcModules'] = [].concat(
angularFiles['angularModules']['ngAnimate'],
angularFiles['angularModules']['ngMessages'],
angularFiles['angularModules']['ngCookies'],
angularFiles['angularModules']['ngResource'],
angularFiles['angularModules']['ngRoute'],
+1 -6
View File
@@ -2,15 +2,10 @@
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],
.ng-cloak, .x-ng-cloak,
.ng-hide {
.ng-hide:not(.ng-animate) {
display: none !important;
}
ng\:form {
display: block;
}
.ng-animate-block-transitions {
transition:0s all!important;
-webkit-transition:0s all!important;
}
+24 -5
View File
@@ -184,10 +184,8 @@ h1,h2,h3,h4,h5,h6 {
}
pre {
padding:15px;
border:1px solid #ddd;
display:block;
border-radius:5px;
white-space: pre-wrap;
word-break: normal;
}
.aside-nav a,
@@ -255,6 +253,9 @@ code.highlighted {
z-index: 99;
cursor: pointer;
font-size: 16px;
-moz-appearance: none;
text-indent: 0.01px;
text-overflow: '';
}
.picker:after {
@@ -420,6 +421,7 @@ iframe.example {
.type-hint {
display:inline-block;
background: gray;
}
.variables-matrix .type-hint {
@@ -464,6 +466,14 @@ iframe.example {
background:rgb(189, 63, 66);
}
.type-hint-regexp {
background: rgb(90, 84, 189);
}
.type-hint-domelement {
background: rgb(95, 158, 160);
}
.runnable-example-frame {
width:100%;
height:300px;
@@ -557,7 +567,7 @@ h4 {
}
.return-arguments td:first-child {
width:100px;
width:100px;
}
ul.methods > li,
@@ -565,6 +575,15 @@ ul.events > li {
margin-bottom:40px;
}
@media only screen and (min-width: 769px) and (max-width: 991px) {
.main-body-grid {
margin-top: 160px;
}
.main-body-grid > .grid-left {
top: 160px;
}
}
@media only screen and (max-width : 768px) {
.picker, .picker select {
width:auto;
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

+1 -1
View File
@@ -215,7 +215,7 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
}
});
element.bind('$destroy', function() {
element.on('$destroy', function() {
deregisterEmbedRootScope();
embedRootScope.$destroy();
});
+4 -4
View File
@@ -274,13 +274,13 @@ var popoverElement = function() {
this.contentElement = angular.element(inner.childNodes[1]);
//stop the click on the tooltip
this.element.bind('click', function(event) {
this.element.on('click', function(event) {
event.preventDefault();
event.stopPropagation();
});
var self = this;
angular.element(document.body).bind('click',function(event) {
angular.element(document.body).on('click',function(event) {
if(self.visible()) self.hide();
});
},
@@ -359,7 +359,7 @@ directive.popover = ['popoverElement', function(popover) {
restrict: 'A',
priority : 500,
link: function(scope, element, attrs) {
element.bind('click',function(event) {
element.on('click',function(event) {
event.preventDefault();
event.stopPropagation();
if(popover.isSituatedAt(element) && popover.visible()) {
@@ -396,7 +396,7 @@ directive.foldout = ['$http', '$animate','$window', function($http, $animate, $w
if(/\/build\//.test($window.location.href)) {
url = '/build/docs' + url;
}
element.bind('click',function() {
element.on('click',function() {
scope.$apply(function() {
if(!container) {
if(loading) return;
+5 -5
View File
@@ -35,8 +35,8 @@ angular.module('ui.bootstrap.dropdown', [])
this.open = function( dropdownScope ) {
if ( !openScope ) {
$document.bind('click', closeDropdown);
$document.bind('keydown', escapeKeyBind);
$document.on('click', closeDropdown);
$document.on('keydown', escapeKeyBind);
}
if ( openScope && openScope !== dropdownScope ) {
@@ -49,8 +49,8 @@ angular.module('ui.bootstrap.dropdown', [])
this.close = function( dropdownScope ) {
if ( openScope === dropdownScope ) {
openScope = null;
$document.unbind('click', closeDropdown);
$document.unbind('keydown', escapeKeyBind);
$document.off('click', closeDropdown);
$document.off('keydown', escapeKeyBind);
}
};
@@ -124,7 +124,7 @@ angular.module('ui.bootstrap.dropdown', [])
return;
}
element.bind('click', function(event) {
element.on('click', function(event) {
event.preventDefault();
event.stopPropagation();
+2 -6
View File
@@ -1,9 +1,5 @@
angular.module('directives', [])
.directive('code', function() {
return { restrict:'E', terminal: true };
})
/**
* backToTop Directive
* @param {Function} $anchorScroll
@@ -25,8 +21,8 @@ angular.module('directives', [])
restrict: 'E',
terminal: true,
compile: function(element) {
var linenums = element.hasClass('linenum') || element.parent()[0].nodeName === 'PRE';
var match = /lang-(\S)+/.exec(element.className);
var linenums = element.hasClass('linenum');// || element.parent()[0].nodeName === 'PRE';
var match = /lang-(\S+)/.exec(element[0].className);
var lang = match && match[1];
var html = element.html();
element.html(window.prettyPrintOne(html, lang, linenums));
+11 -2
View File
@@ -1,6 +1,15 @@
angular.module('DocsController', [])
.controller('DocsController', function($scope, $rootScope, $location, $window, $cookies, NG_PAGES, NG_NAVIGATION, NG_VERSION) {
.controller('DocsController', [
'$scope', '$rootScope', '$location', '$window', '$cookies', 'openPlunkr',
'NG_PAGES', 'NG_NAVIGATION', 'NG_VERSION',
function($scope, $rootScope, $location, $window, $cookies, openPlunkr,
NG_PAGES, NG_NAVIGATION, NG_VERSION) {
$scope.openPlunkr = openPlunkr;
$scope.docsVersion = NG_VERSION.isSnapshot ? 'snapshot' : NG_VERSION.version;
$scope.fold = function(url) {
if(url) {
@@ -118,4 +127,4 @@ angular.module('DocsController', [])
});
}
});
});
}]);
+47 -241
View File
@@ -1,65 +1,6 @@
angular.module('examples', [])
.directive('sourceEdit', function(getEmbeddedTemplate) {
return {
template: '<div class="btn-group pull-right">' +
'<a class="btn dropdown-toggle btn-primary" data-toggle="dropdown" href>' +
' <i class="icon-pencil icon-white"></i> Edit<span class="caret"></span>' +
'</a>' +
'<ul class="dropdown-menu">' +
' <li><a ng-click="plunkr($event)" href="">In Plunkr</a></li>' +
' <li><a ng-click="fiddle($event)" href="">In JsFiddle</a></li>' +
'</ul>' +
'</div>',
scope: true,
controller: function($scope, $attrs, openJsFiddle, openPlunkr) {
var sources = {
module: $attrs.sourceEdit,
deps: read($attrs.sourceEditDeps),
html: read($attrs.sourceEditHtml),
css: read($attrs.sourceEditCss),
js: read($attrs.sourceEditJs),
json: read($attrs.sourceEditJson),
unit: read($attrs.sourceEditUnit),
scenario: read($attrs.sourceEditScenario)
};
$scope.fiddle = function(e) {
e.stopPropagation();
openJsFiddle(sources);
};
$scope.plunkr = function(e) {
e.stopPropagation();
openPlunkr(sources);
};
}
};
function read(text) {
var files = [];
angular.forEach(text ? text.split(' ') : [], function(refId) {
// refId is index.html-343, so we need to strip the unique ID when exporting the name
files.push({name: refId.replace(/-\d+$/, ''), content: getEmbeddedTemplate(refId)});
});
return files;
}
})
.factory('angularUrls', function($document) {
var urls = {};
angular.forEach($document.find('script'), function(script) {
var match = script.src.match(/^.*\/(angular[^\/]*\.js)$/);
if (match) {
urls[match[1].replace(/(\-\d.*)?(\.min)?\.js$/, '.js')] = match[0];
}
});
return urls;
})
.factory('formPostData', function($document) {
.factory('formPostData', ['$document', function($document) {
return function(url, fields) {
var form = angular.element('<form style="display: none;" method="post" action="' + url + '" target="_blank"></form>');
angular.forEach(fields, function(value, name) {
@@ -71,196 +12,61 @@ angular.module('examples', [])
form[0].submit();
form.remove();
};
})
}])
.factory('prepareDefaultAppModule', function() {
return function(content) {
var deps = [];
angular.forEach(content.deps, function(file) {
if(file.name == 'angular-animate.js') {
deps.push('ngAnimate');
}
});
.factory('openPlunkr', ['formPostData', '$http', '$q', function(formPostData, $http, $q) {
return function(exampleFolder) {
var moduleName = 'App';
return {
module : moduleName,
script : "angular.module('" + moduleName + "', [" +
(deps.length ? "'" + deps.join("','") + "'" : "") + "]);\n\n"
};
};
})
var exampleName = 'AngularJS Example';
.factory('prepareEditorAssetTags', function(angularUrls) {
return function(content, options) {
options = options || {};
var includeLocalFiles = options.includeLocalFiles;
var html = makeScriptTag(angularUrls['angular.js']);
// Load the manifest for the example
$http.get(exampleFolder + '/manifest.json')
.then(function(response) {
return response.data;
})
.then(function(manifest) {
var filePromises = [];
var allFiles = [].concat(content.js, content.css, content.html, content.json);
angular.forEach(content.deps, function(file) {
if (file.name !== 'angular.js') {
var isLocal = false;
for(var i=0;i<allFiles.length;i++) {
if(allFiles[i].name == file.name) {
isLocal = true;
break;
}
}
if(!(isLocal && !includeLocalFiles)) {
var assetUrl = angularUrls[file.name] || file.name;
html += makeScriptTag(assetUrl);
}
}
});
if(includeLocalFiles) {
angular.forEach(content.css, function(file, index) {
html += makeCssLinkTag(file.name);
});
}
return html;
function makeScriptTag(src) {
return '<script type="text/javascript" src="' + src + '"></script>\n';
}
function makeCssLinkTag(src) {
return '<link rel="stylesheet" type="text/css" href="' + src + '" />\n';
}
};
})
.factory('openPlunkr', function(templateMerge, formPostData, prepareEditorAssetTags, prepareDefaultAppModule) {
return function(content) {
var hasRouting = false;
angular.forEach(content.deps, function(file) {
hasRouting = hasRouting || file.name == 'angular-route.js';
});
var indexHtmlContent = '<!doctype html>\n' +
'<html ng-app="{{module}}">\n' +
' <head>\n' +
'{{scriptDeps}}';
if(hasRouting) {
indexHtmlContent += '<script type="text/javascript">\n' +
'//this is here to make plunkr work with AngularJS routing\n' +
'angular.element(document.getElementsByTagName(\'head\')).append(' +
'angular.element(\'<base href="\' + window.location.pathname + \'" />\')' +
');\n' +
'</script>\n';
}
indexHtmlContent += '</head>\n' +
' <body>\n\n' +
'{{indexContents}}\n\n' +
' </body>\n' +
'</html>\n';
indexProp = {
module: content.module,
scriptDeps: prepareEditorAssetTags(content, { includeLocalFiles : true }),
indexContents: content.html[0].content
};
var allFiles = [].concat(content.js, content.css, content.html, content.json);
if(!content.module) {
var moduleData = prepareDefaultAppModule(content);
indexProp.module = moduleData.module;
var found = false;
angular.forEach(content.js, function(file) {
if(file.name == 'script.js') {
file.content = moduleData.script + file.content;
found = true;
}
});
if(!found) {
indexProp.scriptDeps += '<script type="text/javascript" src="script.js"></script>\n';
allFiles.push({
name : 'script.js',
content : moduleData.script
// Build a pretty title for the Plunkr
var exampleNameParts = manifest.name.split('-');
exampleNameParts.unshift('AngularJS');
angular.forEach(exampleNameParts, function(part, index) {
exampleNameParts[index] = part.charAt(0).toUpperCase() + part.substr(1);
});
}
}
exampleName = exampleNameParts.join(' - ');
var postData = {};
angular.forEach(manifest.files, function(filename) {
filePromises.push($http.get(exampleFolder + '/' + filename, { transformResponse: [] })
.then(function(response) {
angular.forEach(allFiles, function(file, index) {
if (file.content && file.name != 'index.html') {
postData['files[' + file.name + ']'] = file.content;
}
});
// The manifests provide the production index file but Plunkr wants
// a straight index.html
if (filename === "index-production.html") {
filename = "index.html"
}
postData['files[index.html]'] = templateMerge(indexHtmlContent, indexProp);
postData['tags[]'] = "angularjs";
return {
name: filename,
content: response.data
};
}));
});
return $q.all(filePromises);
})
.then(function(files) {
var postData = {};
postData.private = true;
postData.description = 'AngularJS Example Plunkr';
angular.forEach(files, function(file) {
postData['files[' + file.name + ']'] = file.content;
});
formPostData('http://plnkr.co/edit/?p=preview', postData);
postData['tags[0]'] = "angularjs";
postData['tags[1]'] = "example";
postData.private = true;
postData.description = exampleName;
formPostData('http://plnkr.co/edit/?p=preview', postData);
});
};
})
.factory('openJsFiddle', function(templateMerge, formPostData, prepareEditorAssetTags, prepareDefaultAppModule) {
var HTML = '<div ng-app=\"{{module}}\">\n{{html:2}}</div>',
CSS = '</style> <!-- Ugly Hack to make remote files preload in jsFiddle --> \n' +
'{{head:0}}<style>{{css}}',
SCRIPT = '{{script}}',
SCRIPT_CACHE = '\n\n<!-- {{name}} -->\n<script type="text/ng-template" id="{{name}}">\n{{content:2}}</script>',
BASE_HREF_TAG = '<!-- Ugly Hack to make AngularJS routing work inside of jsFiddle -->\n' +
'<base href="/" />\n\n';
return function(content) {
var prop = {
module: content.module,
html: '',
css: '',
script: ''
};
if(!prop.module) {
var moduleData = prepareDefaultAppModule(content);
prop.script = moduleData.script;
prop.module = moduleData.module;
}
angular.forEach(content.html, function(file, index) {
if (index) {
prop.html += templateMerge(SCRIPT_CACHE, file);
} else {
prop.html += file.content;
}
});
prop.head = prepareEditorAssetTags(content, { includeLocalFiles : false });
angular.forEach(content.js, function(file, index) {
prop.script += file.content;
});
angular.forEach(content.css, function(file, index) {
prop.css += file.content;
});
var hasRouting = false;
angular.forEach(content.deps, function(file) {
hasRouting = hasRouting || file.name == 'angular-route.js';
});
var compiledHTML = templateMerge(HTML, prop);
if(hasRouting) {
compiledHTML = BASE_HREF_TAG + compiledHTML;
}
formPostData("http://jsfiddle.net/api/post/library/pure/", {
title: 'AngularJS Example',
html: compiledHTML,
js: templateMerge(SCRIPT, prop),
css: templateMerge(CSS, prop)
});
};
});
}]);
+4 -4
View File
@@ -8,7 +8,7 @@ angular.module('search', [])
}
$scope.search = function(q) {
var MIN_SEARCH_LENGTH = 3;
var MIN_SEARCH_LENGTH = 2;
if(q.length >= MIN_SEARCH_LENGTH) {
var results = docsSearch(q);
var totalAreas = 0;
@@ -35,7 +35,7 @@ angular.module('search', [])
}
}
if(result) {
$location.path(result.url);
$location.path(result.path);
$scope.hideResults();
}
};
@@ -131,7 +131,7 @@ angular.module('search', [])
return function(scope, element, attrs) {
var ESCAPE_KEY_KEYCODE = 27,
FORWARD_SLASH_KEYCODE = 191;
angular.element($document[0].body).bind('keydown', function(event) {
angular.element($document[0].body).on('keydown', function(event) {
var input = element[0];
if(event.keyCode == FORWARD_SLASH_KEYCODE && document.activeElement != input) {
event.stopPropagation();
@@ -140,7 +140,7 @@ angular.module('search', [])
}
});
element.bind('keydown', function(event) {
element.on('keydown', function(event) {
if(event.keyCode == ESCAPE_KEY_KEYCODE) {
event.stopPropagation();
event.preventDefault();
+15 -23
View File
@@ -22,7 +22,7 @@ angular.module('tutorials', [])
element.addClass('tutorial-nav');
element.append(templateMerge(
'<a href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-step-backward"></i> Previous</li></a>\n' +
'<a href="http://angular.github.com/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="glyphicon glyphicon-play"></i> Live Demo</li></a>\n' +
'<a href="http://angular.github.io/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="glyphicon glyphicon-play"></i> Live Demo</li></a>\n' +
'<a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-search"></i> Code Diff</li></a>\n' +
'<a href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="glyphicon glyphicon-step-forward"></i></li></a>', props));
}
@@ -31,28 +31,20 @@ angular.module('tutorials', [])
.directive('docTutorialReset', function() {
function tab(name, command, id, step) {
return '' +
' <div class=\'tab-pane well\' title="' + name + '" value="' + id + '">\n' +
' <ol>\n' +
' <li><p>Reset the workspace to step ' + step + '.</p>' +
' <pre>' + command + '</pre></li>\n' +
' <li><p>Refresh your browser or check the app out on <a href="http://angular.github.com/angular-phonecat/step-' + step + '/app">Angular\'s server</a>.</p></li>\n' +
' </ol>\n' +
' </div>\n';
}
return {
compile: function(element, attrs) {
var step = attrs.docTutorialReset;
element.html(
'<div ng-hide="show">' +
'<p><a href="" ng-click="show=true;$event.stopPropagation()">Workspace Reset Instructions ➤</a></p>' +
'</div>\n' +
'<div class="tabbable" ng-show="show" ng-model="$cookies.platformPreference">\n' +
tab('Git on Mac/Linux', 'git checkout -f step-' + step, 'gitUnix', step) +
tab('Git on Windows', 'git checkout -f step-' + step, 'gitWin', step) +
'</div>\n');
}
scope: {
'step': '@docTutorialReset'
},
template:
'<p><a href="" ng-click="show=!show;$event.stopPropagation()">Workspace Reset Instructions ➤</a></p>\n' +
'<div class="alert alert-info" ng-show="show">\n' +
' <p>Reset the workspace to step {{step}}.</p>' +
' <p><pre>git checkout -f step-{{step}}</pre></p>\n' +
' <p>Refresh your browser or check out this step online: '+
'<a href="http://angular.github.io/angular-phonecat/step-{{step}}/app">Step {{step}} Live Demo</a>.</p>\n' +
'</div>\n' +
'<p>The most important changes are listed below. You can see the full diff on ' +
'<a ng-href="https://github.com/angular/angular-phonecat/compare/step-{{step ? (step - 1): \'0~1\'}}...step-{{step}}">GitHub</a>\n' +
'</p>'
};
});
+38
View File
@@ -0,0 +1,38 @@
describe("code", function() {
var prettyPrintOne, oldPP;
var compile, scope;
var any = jasmine.any;
beforeEach(module('directives'));
beforeEach(inject(function($rootScope, $compile) {
// Provide stub for pretty print function
oldPP = window.prettyPrintOne;
prettyPrintOne = window.prettyPrintOne = jasmine.createSpy();
scope = $rootScope.$new();
compile = $compile;
}));
afterEach(function() {
window.prettyPrintOne = oldPP;
});
it('should pretty print innerHTML', function() {
compile('<code>var x;</code>')(scope);
expect(prettyPrintOne).toHaveBeenCalledWith('var x;', null, false);
});
it('should allow language declaration', function() {
compile('<code class="lang-javascript"></code>')(scope);
expect(prettyPrintOne).toHaveBeenCalledWith(any(String), 'javascript', false);
});
it('supports allow line numbers', function() {
compile('<code class="linenum"></code>')(scope);
expect(prettyPrintOne).toHaveBeenCalledWith(any(String), null, true);
});
});
+1
View File
@@ -3,6 +3,7 @@ describe("DocsController", function() {
angular.module('fake', [])
.value('$cookies', {})
.value('openPlunkr', function() {})
.value('NG_PAGES', {})
.value('NG_NAVIGATION', {})
.value('NG_VERSION', {});
+4
View File
@@ -25,6 +25,10 @@ module.exports = function(config) {
require('./tag-defs/tutorial-step')
]);
config.append('processing.defaultTagTransforms', [
require('dgeni-packages/jsdoc/tag-defs/transforms/trim-whitespace')
]);
config.append('processing.inlineTagDefinitions', [
require('./inline-tag-defs/type')
]);
+15 -8
View File
@@ -1,26 +1,33 @@
var writer = require('dgeni/lib/utils/doc-writer');
var fs = require('q-io/fs');
var log = require('winston');
var util = require("util");
var filter, outputPath, depth;
module.exports = {
name: 'debug-dump',
runBefore: ['write-files'],
description: 'This processor dumps docs that match a filter to a file',
init: function(config, injectables) {
process: function(docs, config) {
var filter, outputPath, depth;
filter = config.get('processing.debug-dump.filter');
outputPath = config.get('processing.debug-dump.outputPath');
depth = config.get('processing.debug-dump.depth', 2);
},
process: function(docs) {
if ( filter && outputPath ) {
log.info('Dumping docs:', filter, outputPath);
var filteredDocs = filter(docs);
var dumpedDocs = util.inspect(filteredDocs, depth);
return writer.writeFile(outputPath, dumpedDocs).then(function() {
return writeFile(outputPath, dumpedDocs).then(function() {
return docs;
});
}
}
};
};
function writeFile(file, content) {
return fs.makeTree(fs.directory(file)).then(function() {
return fs.write(file, content, 'wb');
});
}
+12 -10
View File
@@ -5,16 +5,18 @@ var path = require('canonical-path');
module.exports = {
name: 'error-docs',
description: 'Compute the various fields for docs in the Error area',
runAfter: ['tags-extracted'],
init: function(config, injectables) {
injectables.value('errorNamespaces', {});
var minerrInfoPath = config.get('processing.errors.minerrInfoPath');
if ( !minerrInfoPath ) {
throw new Error('Error in configuration: Please provide a path to the minerr info file (errors.json) ' +
'in the `config.processing.errors.minerrInfoPath` property');
}
injectables.value('minerrInfo', require(minerrInfoPath));
runAfter: ['tags-extracted', 'compute-path'],
runBefore: ['extra-docs-added'],
exports: {
errorNamespaces: ['factory', function() { return {}; }],
minerrInfo: ['factory', function(config) {
var minerrInfoPath = config.get('processing.errors.minerrInfoPath');
if ( !minerrInfoPath ) {
throw new Error('Error in configuration: Please provide a path to the minerr info file (errors.json) ' +
'in the `config.processing.errors.minerrInfoPath` property');
}
return require(minerrInfoPath);
}]
},
process: function(docs, partialNames, errorNamespaces, minerrInfo) {
+9 -7
View File
@@ -3,14 +3,16 @@ var versionInfo = require('../../../lib/versions/version-info');
module.exports = {
name: 'git-data',
runBefore: ['loading-files'],
runBefore: ['reading-files'],
description: 'This processor adds information from the local git repository to the extraData injectable',
init: function(config, injectables) {
injectables.value('gitData', {
version: versionInfo.currentVersion,
versions: versionInfo.previousVersions,
info: versionInfo.gitRepoInfo
});
exports: {
gitData: ['factory', function() {
return {
version: versionInfo.currentVersion,
versions: versionInfo.previousVersions,
info: versionInfo.gitRepoInfo
};
}]
},
process: function(extraData, gitData) {
extraData.git = gitData;
+3 -5
View File
@@ -1,20 +1,18 @@
var _ = require('lodash');
var log = require('winston');
var path = require('canonical-path');
var deployment;
module.exports = {
name: 'index-page',
runAfter: ['adding-extra-docs'],
runBefore: ['extra-docs-added'],
description: 'This processor creates docs that will be rendered as the index page for the app',
init: function(config) {
deployment = config.deployment;
process: function(docs, config) {
var deployment = config.deployment;
if ( !deployment || !deployment.environments ) {
throw new Error('No deployment environments found in the config.');
}
},
process: function(docs) {
// Collect up all the areas in the docs
var areas = {};
+9 -12
View File
@@ -3,20 +3,20 @@ var log = require('winston');
var fs = require('fs');
var path = require('canonical-path');
// Keywords to ignore
var wordsToIgnore = [];
var propertiesToIgnore;
var areasToSearch;
// Keywords start with "ng:" or one of $, _ or a letter
var KEYWORD_REGEX = /^((ng:|[\$_a-z])[\w\-_]+)/;
module.exports = {
name: 'keywords',
runAfter: ['docs-processed'],
runBefore: ['adding-extra-docs'],
description: 'This processor extracts all the keywords from the document',
init: function(config) {
process: function(docs, config) {
// Keywords to ignore
var wordsToIgnore = [];
var propertiesToIgnore;
var areasToSearch;
// Keywords start with "ng:" or one of $, _ or a letter
var KEYWORD_REGEX = /^((ng:|[\$_a-z])[\w\-_]+)/;
// Load up the keywords to ignore, if specified in the config
if ( config.processing.search && config.processing.search.ignoreWordsFile ) {
@@ -34,9 +34,6 @@ module.exports = {
propertiesToIgnore = _.indexBy(config.get('processing.search.propertiesToIgnore', []));
log.debug('Properties to ignore', propertiesToIgnore);
},
process: function(docs) {
var ignoreWordsMap = _.indexBy(wordsToIgnore);
// If the title contains a name starting with ng, e.g. "ngController", then add the module name
+10 -13
View File
@@ -11,17 +11,17 @@ var AREA_NAMES = {
};
function getNavGroup(pages, area, pageSorter, pageMapper) {
var navItems = _(pages)
// We don't want the child to include the index page as this is already catered for
.omit(function(page) { return page.id === 'index'; })
// Apply the supplied sorting function
.sortBy(pageSorter)
// Apply the supplied mapping function
.map(pageMapper)
.value();
return {
@@ -129,18 +129,15 @@ var navGroupMappers = {
}
};
var outputFolder;
module.exports = {
name: 'pages-data',
description: 'This plugin will create a new doc that will be rendered as an angularjs module ' +
'which will contain meta information about the pages and navigation',
runAfter: ['adding-extra-docs', 'component-groups-generate'],
runAfter: ['adding-extra-docs', 'component-groups-generate', 'compute-path'],
runBefore: ['extra-docs-added'],
init: function(config) {
outputFolder = config.rendering.outputFolder;
},
process: function(docs) {
process: function(docs, config) {
var outputFolder = config.rendering.outputFolder;
_(docs)
.filter(function(doc) { return doc.area === 'api'; })
@@ -176,7 +173,7 @@ module.exports = {
// - ngView
// - section "service"
// - $route
//
//
var areas = {};
_(navPages)
.groupBy('area')
@@ -191,7 +188,7 @@ module.exports = {
area.navGroups = navGroupMapper(pages, area);
});
// Extract a list of basic page information for mapping paths to paritals and for client side searching
// Extract a list of basic page information for mapping paths to partials and for client side searching
var pages = _(docs)
.map(function(doc) {
var page = _.pick(doc, [
+26 -29
View File
@@ -1,45 +1,20 @@
var _ = require('lodash');
var log = require('winston');
var path = require('canonical-path');
var trimIndentation = require('dgeni/lib/utils/trim-indentation');
var code = require('dgeni/lib/utils/code');
var protractorFolder;
function createProtractorDoc(example, file, env) {
var protractorDoc = {
docType: 'e2e-test',
id: 'protractorTest' + '-' + example.id,
template: 'protractorTests.template.js',
outputPath: path.join(protractorFolder, example.id, env + '_test.js'),
innerTest: file.fileContents,
pathPrefix: '.', // Hold for if we test with full jQuery
exampleId: example.id,
description: example.doc.id
};
if (env === 'jquery') {
protractorDoc.examplePath = example.outputFolder + '/index-jquery.html'
} else {
protractorDoc.examplePath = example.outputFolder + '/index.html'
}
return protractorDoc;
}
module.exports = {
name: 'protractor-generate',
description: 'Generate a protractor test file from the e2e tests in the examples',
runAfter: ['adding-extra-docs'],
runBefore: ['extra-docs-added'],
init: function(config, injectables) {
protractorFolder = config.get('rendering.protractor.outputFolder', 'ptore2e');
},
process: function(docs, examples) {
process: function(docs, examples, config) {
var protractorFolder = config.get('rendering.protractor.outputFolder', 'ptore2e');
_.forEach(examples, function(example) {
_.forEach(example.files, function(file) {
// Check if it's a Protractor test.
if (!(file.type == 'protractor')) {
if (file.type !== 'protractor') {
return;
}
@@ -48,5 +23,27 @@ module.exports = {
docs.push(createProtractorDoc(example, file, 'jqlite'));
});
});
function createProtractorDoc(example, file, env) {
var protractorDoc = {
docType: 'e2e-test',
id: 'protractorTest' + '-' + example.id,
template: 'protractorTests.template.js',
outputPath: path.join(protractorFolder, example.id, env + '_test.js'),
innerTest: file.fileContents,
pathPrefix: '.', // Hold for if we test with full jQuery
exampleId: example.id,
description: example.doc.id
};
if (env === 'jquery') {
protractorDoc.examplePath = example.outputFolder + '/index-jquery.html';
} else {
protractorDoc.examplePath = example.outputFolder + '/index.html';
}
return protractorDoc;
}
}
};
+2 -2
View File
@@ -1,9 +1,9 @@
module.exports = {
name: 'step',
transformFn: function(doc, tag) {
transforms: function(doc, tag, value) {
if ( doc.docType !== 'tutorial' ) {
throw new Error('Invalid tag, step. You should only use this tag on tutorial docs');
}
return parseInt(tag.description,10);
return parseInt(value,10);
}
};
@@ -0,0 +1,27 @@
{# Be aware that we need these extra new lines here or marked will not realise that the <div>
is HTML and wrap each line in a <p> - thus breaking the HTML #}
<div>
<a ng-click="openPlunkr('{$ doc.example.outputFolder $}')" class="btn pull-right">
<i class="glyphicon glyphicon-edit">&nbsp;</i>
Edit in Plunker</a>
<div class="runnable-example"
path="{$ doc.example.outputFolder $}"
{%- for attrName, attrValue in doc.example.attributes %}
{$ attrName $}="{$ attrValue $}"{% endfor %}>
{% for fileName, file in doc.example.files %}
<div class="runnable-example-file" {% for attrName, attrValue in file.attributes %}
{$ attrName $}="{$ attrValue $}"{% endfor %}>
{% code -%}
{$ file.fileContents $}
{%- endcode %}
</div>
{% endfor %}
<iframe class="runnable-example-frame" src="{$ doc.example.outputFolder $}/index.html" name="{$ doc.example.id $}"></iframe>
</div>
</div>
{# Be aware that we need these extra new lines here or marked will not realise that the <div>
above is HTML and wrap each line in a <p> - thus breaking the HTML #}
+3 -3
View File
@@ -6,7 +6,7 @@
Welcome to the AngularJS API docs page. These pages contain the AngularJS reference materials for version <strong ng-bind="version"></strong>.
The documentation is organized into **{@link guide/module modules}** which contain various components of an AngularJS application.
These components are {@link guide/directive directives}, {@link guide/dev_guide.services services}, {@link guide/filter filters}, {@link guide/providers providers}, {@link guide/templates types}, global APIs and testing mocks.
These components are {@link guide/directive directives}, {@link guide/services services}, {@link guide/filter filters}, {@link guide/providers providers}, {@link guide/templates templates}, global APIs, and testing mocks.
<div class="alert alert-info">
**Angular Namespaces `$` and `$$`**
@@ -212,7 +212,7 @@ Use ngTouch when developing for mobile browsers/devices.
{@link ngTouch#service Services / Factories}
</td>
<td>
The {@link ngTouch.$swipe $swipe} service is used to register and manage mobile DOM events.
The {@link ngTouch.$swipe $swipe} service is used to register and manage mobile DOM events.
</td>
</tr>
<tr>
@@ -252,7 +252,7 @@ Use ngSanitize to securely parse and manipulate HTML data in your application.
## {@link ngMock ngMock}
Use ngMock to inject and mock modules, factories, services and providers within your unit tests
Use ngMock to inject and mock modules, factories, services and providers within your unit tests
<div class="alert alert-info">Include the **angular-mocks.js** file into your test runner for this to work.</div>
+1 -1
View File
@@ -8,7 +8,7 @@ but the required directive controller is not present on the current DOM element
To resolve this error ensure that there is no typo in the required controller name and that the required directive controller is present on the current element.
If the required controller is expected to be on a ancestor element, make ensure that you prefix the controller name in the `require` definition with `^`.
If the required controller is expected to be on a ancestor element, make sure that you prefix the controller name in the `require` definition with `^`.
If the required controller is optionally requested, use `?` or `^?` to specify that.
+4 -1
View File
@@ -16,7 +16,7 @@ myModule.directive('myDirective', function factory() {
return {
...
scope: {
'bind': '=localValue'
localValue: '=bind'
}
...
}
@@ -30,6 +30,9 @@ Following are invalid uses of this directive:
<!-- ERROR because `myFn()=localValue` is an invalid statement -->
<my-directive bind="myFn()">
<!-- ERROR because attribute bind wasn't provided -->
<my-directive>
```
+14
View File
@@ -37,3 +37,17 @@ elements. For example:
```
<b>Hello</b> World!
```
Watch out for html comments at the beginning or end of templates, as these can cause this error as
well. Consider the following template:
```
<div class='container'>
<div class='wrapper>
...
</div> <!-- wrapper -->
</div> <!-- container -->
```
The `<!-- container -->` comment is interpreted as a second root element and causes the template to
be invalid.
+15 -8
View File
@@ -3,24 +3,31 @@
@fullName Module Unavailable
@description
This error occurs when trying to "re-open" a module that has not yet been defined.
This error occurs when you declare a dependency on a module that isn't defined anywhere or hasn't
been loaded in the current browser context.
When you receive this error, check that the name of the module in question is correct and that the
file in which this module is defined has been loaded (either via `<script>` tag, loader like
require.js, or testing harness like karma).
A less common reason for this error is trying to "re-open" a module that has not yet been defined.
To define a new module, call {@link angular.module angular.module} with a name
and an array of dependent modules, like so:
```
```js
// When defining a module with no module dependencies,
// the requires array should be defined and empty.
// the array of dependencies should be defined and empty.
var myApp = angular.module('myApp', []);
```
To retrieve a reference to the same module for further configuration, call
`angular.module` without the `requires` array.
`angular.module` without the array argument.
```
```js
var myApp = angular.module('myApp');
```
Calling `angular.module` without the `requires` array when the module has not yet
been defined causes this error to be thrown. To fix it, define your module with
a name and an empty array, as in the first example above.
Calling `angular.module` without the array of dependencies when the module has not yet been defined
causes this error to be thrown. To fix it, define your module with a name and an empty array, as in
the first example above.
@@ -0,0 +1,54 @@
@ngdoc error
@name $injector:strictdi
@fullName Explicit annotation required
@description
This error occurs when attempting to invoke a function or provider which
has not been explicitly annotated, while the application is running with
strict-di mode enabled.
For example:
```
angular.module("myApp", [])
// BadController cannot be invoked, because
// the dependencies to be injected are not
// explicitly listed.
.controller("BadController", function($scope, $http, $filter) {
// ...
});
```
To fix the error, explicitly annotate the function using either the inline
bracket notation, or with the $inject property:
```
function GoodController1($scope, $http, $filter) {
// ...
}
GoodController1.$inject = ["$scope", "$http", "$filter"];
angular.module("myApp", [])
// GoodController1 can be invoked because it
// had an $inject property, which is an array
// containing the dependency names to be
// injected.
.controller("GoodController1", GoodController1)
// GoodController2 can also be invoked, because
// the dependencies to inject are listed, in
// order, in the array, with the function to be
// invoked trailing on the end.
.controller("GoodController2", [
"$scope",
"$http",
"$filter",
function($scope, $http, $filter) {
// ...
}
]);
```
For more information about strict-di mode, see {@link ng.directive:ngApp ngApp}
and {@link api/angular.bootstrap angular.bootstrap}.
+6 -5
View File
@@ -9,18 +9,19 @@ correctly. For example:
```
angular.module('myApp', [])
.controller('myCtrl', ['myService', function (myService) {
.controller('MyController', ['myService', function (myService) {
// Do something with myService
}]);
```
This code will fail with `$injector:unpr` if `myService` is not defined. Making
sure each dependency is defined will fix the problem.
The above code will fail with `$injector:unpr` if `myService` is not defined.
Making sure each dependency is defined will fix the problem, as noted below.
```
angular.module('myApp', [])
.service('myService', function () { /* ... */ })
.controller('myCtrl', ['myService', function (myService) {
.controller('MyController', ['myService', function (myService) {
// Do something with myService
}]);
```
```
+280 -42
View File
@@ -3,72 +3,310 @@
@fullName Action Already In Progress
@description
At any point in time there can be only one `$digest` or $apply operation in progress.
The stack trace of this error allows you to trace the origin of the currently executing $apply or $digest call.
At any point in time there can be only one `$digest` or `$apply` operation in progress. This is to
prevent very hard to detect bugs from entering your application. The stack trace of this error
allows you to trace the origin of the currently executing `$apply` or `$digest` call, which caused
the error.
`$digest` or `$apply` are processing operational states of the Scope - data-structure in Angular that provides context for models and enables model mutation observation.
## Background
Trying to reenter a `$digest` or `$apply` while one of them is already in progress is typically a sign of programming error that needs to be fixed.
Angular uses a dirty-checking digest mechanism to monitor and update values of the scope during
the processing of your application. The digest works by checking all the values that are being
watched against their previous value and running any watch handlers that have been defined for those
values that have changed.
This digest mechanism is triggered by calling `$digest` on a scope object. Normally you do not need
to trigger a digest manually, because every external action that can trigger changes in your
application, such as mouse events, timeouts or server responses, wrap the Angular application code
in a block of code that will run `$digest` when the code completes.
You wrap Angular code in a block that will be followed by a `$digest` by calling `$apply` on a scope
object. So, in pseudo-code, the process looks like this:
```
element.on('mouseup', function() {
scope.$apply(function() {
$scope.doStuff();
});
});
```
where `$apply()` looks something like:
```
$apply = function(fn) {
try {
fn();
} finally() {
$digest();
}
}
```
## Digest Phases
Angular keeps track of what phase of processing we are in, the relevant ones being `$apply` and
`$digest`. Trying to reenter a `$digest` or `$apply` while one of them is already in progress is
typically a sign of programming error that needs to be fixed. So Angular will throw this error when
that occurs.
In most situations it should be well defined whether a piece of code will be run inside an `$apply`,
in which case you should not be calling `$apply` or `$digest`, or it will be run outside, in which
case you should wrap any code that will be interacting with Angular scope or services, in a call to
`$apply`.
As an example, all Controller code should expect to be run within Angular, so it should have no need
to call `$apply` or `$digest`. Conversely, code that is being trigger directly as a call back to
some external event, from the DOM or 3rd party library, should expect that it is never called from
within Angular, and so any Angular application code that it calls should first be wrapped in a call
to $apply.
## Common Causes
Apart from simply incorrect calls to `$apply` or `$digest` there are some cases when you may get
this error through no fault of your own.
### Inconsistent API (Sync/Async)
This error is often seen when interacting with an API that is sometimes sync and sometimes async.
For example:
For example, imagine a 3rd party library that has a method which will retrieve data for us. Since it
may be making an asynchronous call to a server, it accepts a callback function, which will be called
when the data arrives.
```
function MyController() {
function MyController($scope, thirdPartyComponent) {
thirdPartyComponent.getData(function(someData) {
scope.$apply(function() {
scope.someData = someData;
$scope.$apply(function() {
$scope.someData = someData;
});
});
}
```
The controller constructor is always instantiated from within an $apply cycle, so if the third-party component called our callback synchronously, we'd be trying to enter the $apply again.
We expect that our callback will be called asynchronously, and so from outside Angular. Therefore, we
correctly wrap our application code that interacts with Angular in a call to `$apply`.
To resolve this type of issue, either fix the api to be always synchronous or asynchronous or wrap the call to the api with setTimeout call to make it always asynchronous.
The problem comes if `getData()` decides to call the callback handler synchronously; perhaps it has
the data already cached in memory and so it immediately calls the callback to return the data,
synchronously.
Since, the `MyController` constructor is always instantiated from within an `$apply` call, our
handler is trying to enter a new `$apply` block from within one.
Other situation that leads to this error is when you are trying to reuse a function to by using it as a callback for code that is called by various apis inside and outside of $apply.
This is not an ideal design choice on the part of the 3rd party library.
For example:
To resolve this type of issue, either fix the api to be always synchronous or asynchronous or force
your callback handler to always run asynchronously by using the `$timeout` service.
```
myApp.directive('myDirective', function() {
function MyController($scope, thirdPartyComponent) {
thirdPartyComponent.getData(function(someData) {
$timeout(function() {
$scope.someData = someData;
}, 0);
});
}
```
Here we have used `$timeout` to schedule the changes to the scope in a future call stack.
By providing a timeout period of 0ms, this will occur as soon as possible and `$timeout` will ensure
that the code will be called in a single `$apply` block.
### Triggering Events Programmatically
The other situation that often leads to this error is when you trigger code (such as a DOM event)
programmatically (from within Angular), which is normally called by an external trigger.
For example, consider a directive that will set focus on an input control when a value in the scope
is true:
```
myApp.directive('setFocusIf', function() {
return {
link: function($scope, $element) {
function doSomeWork() {
$scope.$apply(function() {
// do work here, and update the model
};
}
$element.on('click', doSomeWork);
doSomeWork(); // << this will throw an exception because templates are compiled within $apply
}
}
});
```
The fix for the example above looks like this:
```
myApp.directive('myDirective', function() {
return {
link: function($scope, $element) {
function doSomeWork() {
// do work here, and update the model
}
$element.on('click', function() {
$scope.$apply(doSomeWork); // <<< the $apply call was moved to the callsite that doesn't execute in $apply call already
link: function($scope, $element, $attr) {
$scope.$watch($attr.setFocusIf, function(value) {
if ( value ) { $element[0].focus(); }
});
}
};
});
```
doSomeWork();
If we applied this directive to an input which also used the `ngFocus` directive to trigger some
work when the element receives focus we will have a problem:
```
<input set-focus-if="hasFocus" ng-focus="msg='has focus'">
<button ng-click="hasFocus = true">Focus</button>
```
In this setup, there are two ways to trigger ngFocus. First from a user interaction:
* Click on the input control
* The input control gets focus
* The `ngFocus` directive is triggered, setting `$scope.msg='has focus'` from within a new call to
`$apply()`
Second programmatically:
* Click the button
* The `ngClick` directive sets the value of `$scope.hasFocus` to true inside a call to `$apply`
* The `$digest` runs, which triggers the watch inside the `setFocusIf` directive
* The watch's handle runs, which gives the focus to the input
* The `ngFocus` directive is triggered, setting `$scope.msg='has focus'` from within a new call to
`$apply()`
In this second scenario, we are already inside a `$digest` when the ngFocus directive makes another
call to `$apply()`, causing this error to be thrown.
It is possible to workaround this problem by moving the call to set the focus outside of the digest,
by using `$timeOut(fn, 0, false)`, where the `false` value tells Angular not to wrap this `fn` in a
`$apply` block:
```
myApp.directive('setFocusIf', function($timeout) {
return {
link: function($scope, $element, $attr) {
$scope.$watch($attr.setFocusIf, function(value) {
if ( value ) {
$timeout(function() {
// We must reevaluate the value in case it was changed by a subsequent
// watch handler in the digest.
if ( $scope.$eval($attr.setFocusIf) ) {
$element[0].focus();
}
}, 0, false);
}
});
}
}
});
```
To learn more about Angular processing model please check out the {@link guide/concepts concepts doc} as well as the {@link ng.$rootScope.Scope api} doc.
## Diagnosing This Error
When you get this error it can be rather daunting to diagnose the cause of the issue. The best
course of action is to investigate the stack trace from the error. You need to look for places
where `$apply` or `$digest` have been called and find the context in which this occurred.
There should be two calls:
* The first call is the good `$apply`/`$digest` and would normally be triggered by some event near
the top of the call stack.
* The second call is the bad `$apply`/`$digest` and this is the one to investigate.
Once you have identified this call you work your way up the stack to see what the problem is.
* If the second call was made in your application code then you should look at why this code has been
called from within a `$apply`/`$digest`. It may be a simple oversight or maybe it fits with the
sync/async scenario described earlier.
* If the second call was made inside an Angular directive then it is likely that it matches the second
programmatic event trigger scenario described earlier. In this case you may need to look further up
the tree to what triggered the event in the first place.
### Example Problem
Let's look at how to investigate this error using the `setFocusIf` example from above. This example
defines a new `setFocusIf` directive that sets the focus on the element where it is defined when the
value of its attribute becomes true.
<example name="error-$rootScope-inprog" module="app">
<file name="index.html">
<button ng-click="focusInput = true">Focus</button>
<input ng-focus="count = count + 1" set-focus-if="focusInput" />
</file>
<file name="app.js">
angular.module('app', []).directive('setFocusIf', function() {
return function link($scope, $element, $attr) {
$scope.$watch($attr.setFocusIf, function(value) {
if ( value ) { $element[0].focus(); }
});
};
});
</file>
</example>
When you click on the button to cause the focus to occur we get our `$rootScope:inprog` error. The
stacktrace looks like this:
```
Error: [$rootScope:inprog]
at Error (native)
at angular.min.js:6:467
at n (angular.min.js:105:60)
at g.$get.g.$apply (angular.min.js:113:195)
at HTMLInputElement.<anonymous> (angular.min.js:198:401)
at angular.min.js:32:32
at Array.forEach (native)
at q (angular.min.js:7:295)
at HTMLInputElement.c (angular.min.js:32:14)
at Object.fn (app.js:12:38) angular.js:10111
(anonymous function) angular.js:10111
$get angular.js:7412
$get.g.$apply angular.js:12738 <--- $apply
(anonymous function) angular.js:19833 <--- called here
(anonymous function) angular.js:2890
q angular.js:320
c angular.js:2889
(anonymous function) app.js:12
$get.g.$digest angular.js:12469
$get.g.$apply angular.js:12742 <--- $apply
(anonymous function) angular.js:19833 <--- called here
(anonymous function) angular.js:2890
q angular.js:320
```
We can see (even though the Angular code is minified) that there were two calls to `$apply`, first
on line `19833`, then on line `12738` of `angular.js`.
It is this second call that caused the error. If we look at the angular.js code, we can see that
this call is made by an Angular directive.
```
var ngEventDirectives = {};
forEach(
'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
function(name) {
var directiveName = directiveNormalize('ng-' + name);
ngEventDirectives[directiveName] = ['$parse', function($parse) {
return {
compile: function($element, attr) {
var fn = $parse(attr[directiveName]);
return function(scope, element, attr) {
element.on(lowercase(name), function(event) {
scope.$apply(function() {
fn(scope, {$event:event});
});
});
};
}
};
}];
}
);
```
It is not possible to tell which from the stack trace, but we happen to know in this case that it is
the `ngFocus` directive.
Now look up the stack to see that our application code is only entered once in `app.js` at line `12`.
This is where our problem is:
```
10: link: function($scope, $element, $attr) {
11: $scope.$watch($attr.setFocusIf, function(value) {
12: if ( value ) { $element[0].focus(); } <---- This is the source of the problem
13: });
14: }
```
We can now see that the second `$apply` was caused by us programmatically triggering a DOM event
(i.e. focus) to occur. We must fix this by moving the code outside of the $apply block using
`$timeout` as described above.
## Further Reading
To learn more about Angular processing model please check out the
{@link guide/concepts concepts doc} as well as the {@link ng.$rootScope.Scope api} doc.
+1
View File
@@ -13,3 +13,4 @@ Angular template from a URL requires that the URL is one considered safe for loa
This helps prevent XSS and other security issues. Read more at {@link
api/ng.$sce Strict Contextual Escaping (SCE)}
You may want to include the ngSanitize module to use the automatic sanitizing.
+2
View File
@@ -2,6 +2,8 @@
@name Error Reference
@description
# Error Reference
Use the Error Reference manual to find information about error conditions in
your AngularJS app. Errors thrown in production builds of AngularJS will log
links to this site on the console.
+3 -3
View File
@@ -20,7 +20,7 @@ application.
</html>
```
Note that for bootrapping purposes, the `<html>` element is the same as `document`, so the following
Note that for bootstrapping purposes, the `<html>` element is the same as `document`, so the following
will also throw an error.
```
@@ -38,12 +38,12 @@ You can also get this error if you accidentally load AngularJS itself more than
<html ng-app>
<head>
<script src="angular.js"></script>
...
</head>
<body>
...
<script src="angular.js"></script>
+21 -33
View File
@@ -49,7 +49,7 @@ changes to $location are reflected into the browser address bar.
<tr>
<td class="head">integration with angular application life-cycle</td>
<td>none</td>
<td>knows about all internal life-cycle phases, integrates with $watch, ...</td>
<td>knows about all internal life-cycle phases, integrates with {@link ng.$rootScope.Scope#$watch $watch}, ...</td>
</tr>
<tr>
@@ -237,20 +237,6 @@ it('should show example', inject(
));
```
### Crawling your app
To allow indexing of your AJAX application, you have to add special meta tag in the head section of
your document:
```html
<meta name="fragment" content="!" />
```
This will cause crawler bot to request links with `_escaped_fragment_` param so that your server
can recognize the crawler and serve a HTML snapshots. For more information about this technique,
see [Making AJAX Applications
Crawlable](http://code.google.com/web/ajaxcrawling/docs/specification.html).
## HTML5 mode
In HTML5 mode, the `$location` service getters and setters interact with the browser URL address
@@ -344,20 +330,6 @@ are not prefixed with `.` and will not be intercepted by the `otherwise` rule in
Using this mode requires URL rewriting on server side, basically you have to rewrite all your links
to entry point of your application (e.g. index.html)
### Crawling your app
If you want your AJAX application to be indexed by web crawlers, you will need to add the following
meta tag to the HEAD section of your document:
```html
<meta name="fragment" content="!" />
```
This statement causes a crawler to request links with an empty `_escaped_fragment_` parameter so that
your server can recognize the crawler and serve it HTML snapshots. For more information about this
technique, see [Making AJAX
Applications Crawlable](http://code.google.com/web/ajaxcrawling/docs/specification.html).
### Relative links
Be sure to check all relative links, images, scripts etc. You must either specify the url base in
@@ -510,10 +482,12 @@ use a lower level API, {@link ng.$window $window.location.href}.
## Using $location outside of the scope life-cycle
`$location` knows about Angular's {@link ng.$rootScope.Scope scope} life-cycle. When a URL changes in
the browser it updates the `$location` and calls `$apply` so that all $watchers / $observers are
notified.
the browser it updates the `$location` and calls `$apply` so that all
{@link ng.$rootScope.Scope#$watch $watchers} /
{@link ng.$compile.directive.Attributes#$observe $observers} are notified.
When you change the `$location` inside the `$digest` phase everything is ok; `$location` will
propagate this change into browser and will notify all the $watchers / $observers.
propagate this change into browser and will notify all the {@link ng.$rootScope.Scope#$watch $watchers} /
{@link ng.$compile.directive.Attributes#$observe $observers}.
When you want to change the `$location` from outside Angular (for example, through a DOM Event or
during testing) - you must call `$apply` to propagate the changes.
@@ -525,6 +499,20 @@ forward slash if it is missing.
Note that the `!` prefix in the hashbang mode is not part of `$location.path()`; it is actually
hashPrefix.
## Crawling your app
To allow indexing of your AJAX application, you have to add special meta tag in the head section of
your document:
```html
<meta name="fragment" content="!" />
```
This will cause crawler bot to request links with `_escaped_fragment_` param so that your server
can recognize the crawler and serve a HTML snapshots. For more information about this technique,
see [Making AJAX Applications
Crawlable](http://code.google.com/web/ajaxcrawling/docs/specification.html).
# Testing with the $location service
@@ -632,7 +620,7 @@ then uses the information it obtains to compose hashbang URLs (such as
The Angular's compiler currently does not support two-way binding for methods (see [issue](https://github.com/angular/angular.js/issues/404)). If you should require two-way binding
to the $location object (using {@link input[text] ngModel} directive on an input
field), you will need to specify an extra model property (e.g. `locationPath`) with two watchers
field), you will need to specify an extra model property (e.g. `locationPath`) with two {@link ng.$rootScope.Scope#$watch $watchers}
which push $location updates in both directions. For example:
<example>
<file name="index.html">
+2 -2
View File
@@ -270,8 +270,8 @@ making calls to it when needed.
```js
myModule.directive('my-directive', ['$animate', function($animate) {
return function(element, scope, attrs) {
element.bind('click', function() {
return function(scope, element, attrs) {
element.on('click', function() {
if(element.hasClass('clicked')) {
$animate.removeClass(element, 'clicked');
} else {
+26 -13
View File
@@ -7,6 +7,7 @@
This page explains the Angular initialization process and how you can manually initialize Angular
if necessary.
## Angular `<script>` Tag
This example shows the recommended path for integrating Angular with what we call automatic
@@ -79,7 +80,6 @@ If the {@link ng.directive:ngApp `ng-app`} directive is found then Angular will:
## Manual Initialization
If you need to have more control over the initialization process, you can use a manual
bootstrapping method instead. Examples of when you'd need to do this include using script loaders
or the need to perform an operation before Angular compiles a page.
@@ -88,24 +88,36 @@ Here is an example of manually initializing Angular:
```html
<!doctype html>
<html xmlns:ng="http://angularjs.org">
<body>
Hello {{'World'}}!
<script src="http://code.angularjs.org/angular.js"></script>
<script>
angular.element(document).ready(function() {
angular.module('myApp', []);
angular.bootstrap(document, ['myApp']);
});
</script>
</body>
<html>
<body>
Hello {{'World'}}!
<script src="http://code.angularjs.org/snapshot/angular.js"></script>
<script>
angular.module('myApp', [])
.controller('MyController', ['$scope', function ($scope) {
$scope.greetMe = 'World';
}]);
angular.element(document).ready(function() {
angular.bootstrap(document, ['myApp']);
});
</script>
</body>
</html>
```
Note that we have provided the name of our application module to be loaded into the injector as the second
Note that we provided the name of our application module to be loaded into the injector as the second
parameter of the {@link angular.bootstrap} function. Notice that `angular.bootstrap` will not create modules
on the fly. You must create any custom {@link guide/module modules} before you pass them as a parameter.
You should call `angular.bootstrap()` *after* you've loaded or defined your modules.
You cannot add controllers, services, directives, etc after an application bootstraps.
<div class="alert alert-warning">
**Note:** You should not use the ng-app directive when manually bootstrapping your app.
</div>
This is the sequence that your code should follow:
1. After the page and all of the code is loaded, find the root element of your AngularJS
@@ -114,6 +126,7 @@ This is the sequence that your code should follow:
2. Call {@link angular.bootstrap} to {@link compiler compile} the element into an
executable, bi-directionally bound application.
## Deferred Bootstrap
This feature enables tools like Batarang and test runners to
+2 -2
View File
@@ -105,8 +105,8 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
}
function mouseup() {
$document.unbind('mousemove', mousemove);
$document.unbind('mouseup', mouseup);
$document.off('mousemove', mousemove);
$document.off('mouseup', mouseup);
}
};
});
+24 -24
View File
@@ -2,16 +2,16 @@
@name Conceptual Overview
@description
There are some concepts within Angular that you should understand before creating your first application.
This section touches all important parts of Angular really quickly using a simple example.
However, it won't explain all details.
For a more in-depth explanation, have a look at the {@link tutorial/ tutorial}.
# Conceptual Overview
This section briefly touches on all of the important parts of AngularJS using a simple example.
For a more in-depth explanation, see the {@link tutorial/ tutorial}.
| Concept | Description |
|------------------|------------------------------------------|
|{@link concepts#template Template} | HTML with additional markup |
|{@link concepts#directive Directives} | extend HTML with custom attributes and elements |
|{@link concepts#model Model} | the data that is shown to the user and with which the user interacts |
|{@link concepts#model Model} | the data shown to the user in the view and with which the user interacts |
|{@link concepts#scope Scope} | context where the model is stored so that controllers, directives and expressions can access it |
|{@link concepts#expression Expressions} | access variables and functions from the scope |
|{@link concepts#compiler Compiler} | parses the template and instantiates directives and expressions |
@@ -19,22 +19,22 @@ For a more in-depth explanation, have a look at the {@link tutorial/ tutorial}.
|{@link concepts#view View} | what the user sees (the DOM) |
|{@link concepts#databinding Data Binding} | sync data between the model and the view |
|{@link concepts#controller Controller} | the business logic behind views |
|{@link concepts#di Dependency Injection} | Creates and wires objects / functions |
|{@link concepts#di Dependency Injection} | Creates and wires objects and functions |
|{@link concepts#injector Injector} | dependency injection container |
|{@link concepts#module Module} | configures the Injector |
|{@link concepts#module Module} | a container for the different parts of an app including controllers, services, filters, directives which configures the Injector |
|{@link concepts#service Service} | reusable business logic independent of views |
# A first example: Data binding
## A first example: Data binding
In the following example we will build a form to calculate the costs of an invoice in different currencies.
Let's start with input fields for quantity and cost whose values are multiplied to produce the total of the invoice:
<example>
<example name="guide-concepts-1" ng-app-included="true">
<file name="index.html">
<div ng-init="qty=1;cost=2">
<div ng-app ng-init="qty=1;cost=2">
<b>Invoice:</b>
<div>
Quantity: <input type="number" ng-model="qty" required >
@@ -97,12 +97,12 @@ recalculated and the DOM is updated with their values.
The concept behind this is <a name="databinding">"{@link databinding two-way data binding}"</a>.
# Adding UI logic: Controllers
## Adding UI logic: Controllers
Let's add some more logic to the example that allows us to enter and calculate the costs in
different currencies and also pay the invoice.
<example module="invoice1">
<example name="guide-concepts-2" ng-app-included="true" >
<file name="invoice1.js">
angular.module('invoice1', [])
.controller('InvoiceController', function() {
@@ -128,7 +128,7 @@ different currencies and also pay the invoice.
});
</file>
<file name="index.html">
<div ng-controller="InvoiceController as invoice">
<div ng-app="invoice1" ng-controller="InvoiceController as invoice">
<b>Invoice:</b>
<div>
Quantity: <input type="number" ng-model="invoice.qty" required >
@@ -181,7 +181,7 @@ The following graphic shows how everything works together after we introduced th
<img style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-databinding2.png">
# View independent business logic: Services
## View independent business logic: Services
Right now, the `InvoiceController` contains all logic of our example. When the application grows it
is a good practice to move view independent logic from the controller into a so called
@@ -191,7 +191,7 @@ from the web, e.g. by calling the Yahoo Finance API, without changing the contro
Let's refactor our example and move the currency conversion into a service in another file:
<example module="invoice2">
<example name="guide-concepts-2" ng-app-included="true">
<file name="finance2.js">
angular.module('finance2', [])
.factory('currencyConverter', function() {
@@ -228,7 +228,7 @@ Let's refactor our example and move the currency conversion into a service in an
}]);
</file>
<file name="index.html">
<div ng-controller="InvoiceController as invoice">
<div ng-app="invoice2" ng-controller="InvoiceController as invoice">
<b>Invoice:</b>
<div>
Quantity: <input type="number" ng-model="invoice.qty" required >
@@ -254,7 +254,7 @@ Let's refactor our example and move the currency conversion into a service in an
What changed?
We moved the `convertCurrency` function and the definition of the existing currencies
into the new file `finance.js`. But how does the controller
into the new file `finance2.js`. But how does the controller
get a hold of the now separated function?
This is where <a name="di">"{@link di Dependency Injection}"</a> comes into play.
@@ -297,12 +297,12 @@ Angular uses this array syntax to define the dependencies so that the DI also wo
the code, which will most probably rename the argument name of the controller constructor function
to something shorter like `a`.
# Accessing the backend
## Accessing the backend
Let's finish our example by fetching the exchange rates from the Yahoo Finance API.
The following example shows how this is done with Angular:
<example module="invoice3">
<example name="guide-concepts-3" ng-app-included="true">
<file name="invoice3.js">
angular.module('invoice3', ['finance3'])
.controller('InvoiceController', ['currencyConverter', function(currencyConverter) {
@@ -356,7 +356,7 @@ The following example shows how this is done with Angular:
}]);
</file>
<file name="index.html">
<div ng-controller="InvoiceController as invoice">
<div ng-app="invoice3" ng-controller="InvoiceController as invoice">
<b>Invoice:</b>
<div>
Quantity: <input type="number" ng-model="invoice.qty" required >
@@ -379,8 +379,8 @@ The following example shows how this is done with Angular:
</example>
What changed?
Our `currencyConverter` service of the `finance` module now uses the
{@link ng.$http $http} service, a builtin service provided by Angular
for accessing the backend. It is a wrapper around [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
and [JSONP](http://en.wikipedia.org/wiki/JSONP) transports. Details can be found in the api docs of that service.
Our `currencyConverter` service of the `finance` module now uses the {@link ng.$http `$http`}, a
built-in service provided by Angular for accessing a server backend. `$http` is a wrapper around
[`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
and [JSONP](http://en.wikipedia.org/wiki/JSONP) transports.
+25 -25
View File
@@ -134,7 +134,7 @@ string "very". Depending on which button is clicked, the `spice` model is set to
<example module="spicyApp1">
<file name="index.html">
<div ng-controller="SpicyCtrl">
<div ng-controller="SpicyController">
<button ng-click="chiliSpicy()">Chili</button>
<button ng-click="jalapenoSpicy()">Jalapeño</button>
<p>The food is {{spice}} spicy!</p>
@@ -143,7 +143,7 @@ string "very". Depending on which button is clicked, the `spice` model is set to
<file name="app.js">
var myApp = angular.module('spicyApp1', []);
myApp.controller('SpicyCtrl', ['$scope', function($scope){
myApp.controller('SpicyController', ['$scope', function($scope) {
$scope.spice = 'very';
$scope.chiliSpicy = function() {
@@ -160,9 +160,9 @@ string "very". Depending on which button is clicked, the `spice` model is set to
Things to notice in the example above:
- The `ng-controller` directive is used to (implicitly) create a scope for our template, and the
scope is augmented (managed) by the `SpicyCtrl` Controller.
- `SpicyCtrl` is just a plain JavaScript function. As an (optional) naming convention the name
starts with capital letter and ends with "Ctrl" or "Controller".
scope is augmented (managed) by the `SpicyController` Controller.
- `SpicyController` is just a plain JavaScript function. As an (optional) naming convention the name
starts with capital letter and ends with "Controller" or "Controller".
- Assigning a property to `$scope` creates or updates the model.
- Controller methods can be created through direct assignment to scope (see the `chiliSpicy` method)
- The Controller methods and properties are available in the template (for the `<div>` element and
@@ -175,7 +175,7 @@ previous example.
<example module="spicyApp2">
<file name="index.html">
<div ng-controller="SpicyCtrl">
<div ng-controller="SpicyController">
<input ng-model="customSpice">
<button ng-click="spicy('chili')">Chili</button>
<button ng-click="spicy(customSpice)">Custom spice</button>
@@ -185,18 +185,18 @@ previous example.
<file name="app.js">
var myApp = angular.module('spicyApp2', []);
myApp.controller('SpicyCtrl', ['$scope', function($scope){
myApp.controller('SpicyController', ['$scope', function($scope) {
$scope.customSpice = "wasabi";
$scope.spice = 'very';
$scope.spicy = function(spice){
$scope.spicy = function(spice) {
$scope.spice = spice;
};
}]);
</file>
</example>
Notice that the `SpicyCtrl` Controller now defines just one method called `spicy`, which takes one
Notice that the `SpicyController` Controller now defines just one method called `spicy`, which takes one
argument called `spice`. The template then refers to this Controller method and passes in a string
constant `'chili'` in the binding for the first button and a model property `customSpice` (bound to an
input box) in the second button.
@@ -213,13 +213,13 @@ more information about scope inheritance.
<example module="scopeInheritance">
<file name="index.html">
<div class="spicy">
<div ng-controller="MainCtrl">
<div ng-controller="MainController">
<p>Good {{timeOfDay}}, {{name}}!</p>
<div ng-controller="ChildCtrl">
<div ng-controller="ChildController">
<p>Good {{timeOfDay}}, {{name}}!</p>
<div ng-controller="GrandChildCtrl">
<div ng-controller="GrandChildController">
<p>Good {{timeOfDay}}, {{name}}!</p>
</div>
</div>
@@ -234,16 +234,16 @@ more information about scope inheritance.
</file>
<file name="app.js">
var myApp = angular.module('scopeInheritance', []);
myApp.controller('MainCtrl', ['$scope', function($scope){
myApp.controller('MainController', ['$scope', function($scope) {
$scope.timeOfDay = 'morning';
$scope.name = 'Nikki';
}]);
myApp.controller('ChildCtrl', ['$scope', function($scope){
myApp.controller('ChildController', ['$scope', function($scope) {
$scope.name = 'Mattie';
}]);
myApp.controller('GrandChildCtrl', ['$scope', function($scope){
myApp.controller('GrandChildController', ['$scope', function($scope) {
$scope.timeOfDay = 'evening';
$scope.name = 'Gingerbreak Baby';
$scope.name = 'Gingerbread Baby';
}]);
</file>
</example>
@@ -252,11 +252,11 @@ Notice how we nested three `ng-controller` directives in our template. This will
scopes being created for our view:
- The root scope
- The `MainCtrl` scope, which contains `timeOfDay` and `name` properties
- The `ChildCtrl` scope, which inherits the `timeOfDay` property but overrides (hides) the `name`
- The `MainController` scope, which contains `timeOfDay` and `name` properties
- The `ChildController` scope, which inherits the `timeOfDay` property but overrides (hides) the `name`
property from the previous
- The `GrandChildCtrl` scope, which overrides (hides) both the `timeOfDay` property defined in `MainCtrl`
and the `name` property defined in `ChildCtrl`
- The `GrandChildController` scope, which overrides (hides) both the `timeOfDay` property defined in `MainController`
and the `name` property defined in `ChildController`
Inheritance works with methods in the same way as it does with properties. So in our previous
examples, all of the properties could be replaced with methods that return string values.
@@ -273,8 +273,8 @@ involves injecting the {@link ng.$rootScope $rootScope} and {@link ng.$controlle
myApp.controller('MyController', function($scope) {
$scope.spices = [{"name":"pasilla", "spiciness":"mild"},
{"name":"jalapeno", "spiceiness":"hot hot hot!"},
{"name":"habanero", "spiceness":"LAVA HOT!!"}];
{"name":"jalapeno", "spiciness":"hot hot hot!"},
{"name":"habanero", "spiciness":"LAVA HOT!!"}];
$scope.spice = "habanero";
});
```
@@ -316,11 +316,11 @@ describe('state', function() {
beforeEach(inject(function($rootScope, $controller) {
mainScope = $rootScope.$new();
$controller('MainCtrl', {$scope: mainScope});
$controller('MainController', {$scope: mainScope});
childScope = mainScope.$new();
$controller('ChildCtrl', {$scope: childScope});
$controller('ChildController', {$scope: childScope});
grandChildScope = childScope.$new();
$controller('GrandChildCtrl', {$scope: grandChildScope});
$controller('GrandChildController', {$scope: grandChildScope});
}));
it('should have over and selected', function() {
+1 -1
View File
@@ -8,7 +8,7 @@ Angular sets these CSS classes. It is up to your application to provide useful s
# CSS classes used by angular
* `ng-scope`
- **Usage:** angular applies this class to any element that where a new {@link ng.$rootScope.Scope scope}
- **Usage:** angular applies this class to any element for which a new {@link api/ng.$rootScope.Scope scope}
is defined. (see {@link guide/scope scope} guide for more information about scopes)
* `ng-binding`
+2 -2
View File
@@ -9,7 +9,7 @@ When the model changes, the view reflects the change, and vice versa.
## Data Binding in Classical Template Systems
<img class="right" src="img/One_Way_Data_Binding.png"/>
<img class="right" src="img/One_Way_Data_Binding.png"/><br />
Most templating systems bind data in only one direction: they merge template and model components
together into a view. After the merge occurs, changes to the model
or related sections of the view are NOT automatically reflected in the view. Worse, any changes
@@ -18,7 +18,7 @@ to write code that constantly syncs the view with the model and the model with t
## Data Binding in Angular Templates
<img class="right" src="img/Two_Way_Data_Binding.png"/>
<img class="right" src="img/Two_Way_Data_Binding.png"/><br />
Angular templates work differently. First the template (which is the uncompiled HTML along with
any additional markup or directives) is compiled on the browser. The compilation step produces a
live view. Any changes to the view are immediately reflected in the model, and any changes in
+135 -83
View File
@@ -4,30 +4,29 @@
# Dependency Injection
Dependency Injection (DI) is a software design pattern that deals with how code gets hold of its
dependencies.
Dependency Injection (DI) is a software design pattern that deals with how components get hold of
their dependencies.
The Angular injector subsystem is in charge of service instantiation, resolution
of dependencies, and provision of dependencies to components as requested.
The Angular injector subsystem is in charge of creating components, resolving their dependencies,
and providing them to other components as requested.
For in-depth discussion about DI, see
[Dependency Injection](http://en.wikipedia.org/wiki/Dependency_injection) at Wikipedia,
[Inversion of Control](http://martinfowler.com/articles/injection.html) by Martin Fowler,
or read about DI in your favorite software design pattern book.
## DI in a nutshell
## DI in a Nutshell
There are only three ways an object or a function can get a hold of its dependencies:
1. The dependency can be created, typically using the `new` operator.
2. The dependency can be looked up by referring to a global variable.
3. The dependency can be passed in to where it is needed.
There are only three ways a component (object or function) can get a hold of its dependencies:
1. The component can create the dependency, typically using the `new` operator.
2. The component can look up the dependency, by referring to a global variable.
3. The component can have the dependency passed to it where it is needed.
The first two options of creating or looking up dependencies are not optimal because they hard
code the dependency. This makes it difficult, if not impossible, to modify the dependencies.
This is especially problematic in tests, where it is often desirable to provide mock dependencies
for test isolation.
code the dependency to the component. This makes it difficult, if not impossible, to modify the
dependencies. This is especially problematic in tests, where it is often desirable to provide mock
dependencies for test isolation.
The third option is the most viable, since it removes the responsibility of locating the
dependency from the component. The dependency is simply handed to the component.
@@ -42,8 +41,8 @@ SomeClass.prototype.doSomething = function(name) {
}
```
In the above example `SomeClass` is not concerned with locating the `greeter` dependency, it
is simply handed the `greeter` at runtime.
In the above example `SomeClass` is not concerned with creating or locating the `greeter`
dependency, it is simply handed the `greeter` when it is instantiated.
This is desirable, but it puts the responsibility of getting hold of the dependency on the
code that constructs `SomeClass`.
@@ -51,74 +50,90 @@ code that constructs `SomeClass`.
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-module-injector.png">
To manage the responsibility of dependency creation, each Angular application has an {@link
angular.injector injector}. The injector is a service locator that is responsible for
angular.injector injector}. The injector is a
[service locator](http://en.wikipedia.org/wiki/Service_locator_pattern) that is responsible for
construction and lookup of dependencies.
Here is an example of using the injector service:
```js
// Provide the wiring information in a module
angular.module('myModule', []).
var myModule = angular.module('myModule', []);
```
// Teach the injector how to build a 'greeter'
// Notice that greeter itself is dependent on '$window'
factory('greeter', function($window) {
// This is a factory function, and is responsible for
// creating the 'greet' service.
return {
greet: function(text) {
$window.alert(text);
}
};
});
Teach the injector how to build a `greeter` service. Notice that `greeter` is dependent on the
`$window` service. The `greeter` service is an object that contains a `greet` method.
// New injector is created from the module.
// (This is usually done automatically by angular bootstrap)
```js
myModule.factory('greeter', function($window) {
return {
greet: function(text) {
$window.alert(text);
}
};
});
```
Create a new injector that can provide components defined in our `myModule` module and request our
`greeter` service from the injector. (This is usually done automatically by angular bootstrap).
```js
var injector = angular.injector(['myModule', 'ng']);
// Request any dependency from the injector
var greeter = injector.get('greeter');
```
Asking for dependencies solves the issue of hard coding, but it also means that the injector needs
to be passed throughout the application. Passing the injector breaks the [Law of Demeter](http://en.wikipedia.org/wiki/Law_of_Demeter). To remedy this, we turn the
dependency lookup responsibility to the injector by declaring the dependencies as in this example:
to be passed throughout the application. Passing the injector breaks the
[Law of Demeter](http://en.wikipedia.org/wiki/Law_of_Demeter). To remedy this, we use a declarative
notation in our HTML templates, to hand the responsibility of creating components over to the
injector, as in this example:
```html
<!-- Given this HTML -->
<div ng-controller="MyController">
<button ng-click="sayHello()">Hello</button>
</div>
```
```js
// And this controller definition
function MyController($scope, greeter) {
$scope.sayHello = function() {
greeter.greet('Hello World');
};
}
```
// The 'ng-controller' directive does this behind the scenes
When Angular compiles the HTML, it processes the `ng-controller` directive, which in turn
asks the injector to create an instance of the controller and its dependencies.
```js
injector.instantiate(MyController);
```
Notice that by having the `ng-controller` instantiate the class, it can satisfy all of the
dependencies of `MyController` without the controller ever knowing about the injector. This is
the best outcome. The application code simply asks for the dependencies it needs, without having to
deal with the injector. This setup does not break the Law of Demeter.
This is all done behinds the scenes. Notice that by having the `ng-controller` ask the injector to
instantiate the class, it can satisfy all of the dependencies of `MyController` without the
controller ever knowing about the injector.
This is the best outcome. The application code simply declares the dependencies it needs, without
having to deal with the injector. This setup does not break the Law of Demeter.
## Dependency Annotation
How does the injector know what service needs to be injected?
**How does the injector know what components need to be injected?**
The application developer needs to provide annotation information that the injector uses in order
to resolve the dependencies. Throughout Angular, certain API functions are invoked using the
injector, as per the API documentation. The injector needs to know what services to inject into
the function. Below are three equivalent ways of annotating your code with service name
information. These can be used interchangeably as you see fit and are equivalent.
the function. There are three equivalent ways of annotating your code with service name
information:
### Inferring Dependencies
- Implicitly from the function parameter names
- Using the `$inject` property annotation
- Using the inline array annotation
These can be used interchangeably as you see fit and are equivalent.
### Implicit Dependencies
The simplest way to get hold of the dependencies, is to assume that the function parameter names
are the names of the dependencies.
@@ -134,11 +149,12 @@ function declaration and extracting the parameter names. In the above example `$
`greeter` are two services which need to be injected into the function.
While straightforward, this method will not work with JavaScript minifiers/obfuscators as they
rename the method parameter names. This makes this way of annotating only useful for [pretotyping](http://www.pretotyping.org/), and demo applications.
rename the method parameter names. This makes this way of annotating only useful for
[pretotyping](http://www.pretotyping.org/), and demo applications.
### `$inject` Annotation
### `$inject` Property Annotation
To allow the minifers to rename the function parameters and still be able to inject right services
To allow the minifiers to rename the function parameters and still be able to inject right services,
the function needs to be annotated with the `$inject` property. The `$inject` property is an array
of service names to inject.
@@ -149,18 +165,18 @@ var MyController = function(renamed$scope, renamedGreeter) {
MyController['$inject'] = ['$scope', 'greeter'];
```
In this scenario the ordering of the values in the '$inject' array must match the ordering of the arguments to inject.
Using above code snippet as an example, '$scope' will be injected into 'renamed$scope' and 'greeter' into 'renamedGreeter'.
Care must be taken that the `$inject` annotation is kept in sync with the actual arguments in the
function declaration.
In this scenario the ordering of the values in the `$inject` array must match the ordering of the
arguments to inject. Using above code snippet as an example, `$scope` will be injected into
`renamed$scope` and `greeter` into `renamedGreeter`. Care must be taken that the `$inject`
annotation is kept in sync with the actual arguments in the function declaration.
This method of annotation is useful for controller declarations since it assigns the annotation
information with the function.
### Inline Annotation
### Inline Array Annotation
Sometimes using the `$inject` annotation style is not convenient such as when annotating
directives.
directives or services defined inline by a factory function.
For example:
@@ -190,18 +206,69 @@ someModule.factory('greeter', ['$window', function(renamed$window) {
}]);
```
Here, instead of simply providing the factory function, we pass an array, whose elements consist of
a list of strings (the names of the dependencies) followed by the function itself.
Keep in mind that all of the annotation styles are equivalent and can be used anywhere in Angular
where injection is supported.
## Where can I use DI?
## Where Can I Use DI?
DI is pervasive throughout Angular. You can use it in controllers, services, directives, filters,
animations, and `run` and `config` blocks.
DI is pervasive throughout Angular. You can use it when defining components or when providing `run`
and `config` blocks for a module.
### DI in controllers
- Components such as services, directives, filters and animations are defined by an injectable factory
method or constructor function. These components can be injected with "service" components as
dependencies.
Controllers are classes which are responsible for application behavior. The recommended way of
declaring controllers is using the array notation:
- The `run` and `config` methods accept a function, which can also be injected with "service"
components as dependencies.
- Controllers are defined by a constructor function, which can be injected with any of the "service"
components as dependencies, but they can also be provided with special dependencies. See "DI in
Controllers" below.
### Factory Methods
Factory methods are responsible for creating most objects in Angular. Examples are directives,
services, and filters. The factory methods are registered with the module, and the recommended way
of declaring factories is:
```js
angular.module('myModule', [])
.factory('serviceId', ['depService', function(depService) {
...
}])
.directive('directiveName', ['depService', function(depService) {
...
}])
.filter('filterName', ['depService', function(depService) {
...
}]);
```
### Module Methods
We can specify functions to run at configuration and run time for a module by calling the `run` and
`config` methods. These functions are injectable with dependencies just like the factory functions
above.
```js
angular.module('myModule', [])
.config(['depProvider', function(depProvider){
...
}])
.run(['depService', function(depService) {
...
}]);
```
### Controllers
Controllers are "classes" or "constructor functions" that are responsible for providing the
application behavior that supports the declarative markup in the template. The recommended way of
declaring Controllers is using the array notation:
```js
someModule.controller('MyController', ['$scope', 'dep1', 'dep2', function($scope, dep1, dep2) {
@@ -215,28 +282,13 @@ someModule.controller('MyController', ['$scope', 'dep1', 'dep2', function($scope
This avoids the creation of global functions for controllers and also protects against minification.
Controllers are special in that, unlike services, there can be many instances of them in the
application. For example, there would be one instance for every `ng-controller` directive in the template.
### Factory methods
Moreover, additional dependencies are made available to Controllers:
Factory methods are responsible for creating most objects in Angular. Examples are directives,
services, and filters. The factory methods are registered with the module, and the recommended way
of declaring factories is:
```js
angular.module('myModule', []).
config(['depProvider', function(depProvider){
...
}]).
factory('serviceId', ['depService', function(depService) {
...
}]).
directive('directiveName', ['depService', function(depService) {
...
}]).
filter('filterName', ['depService', function(depService) {
...
}]).
run(['depService', function(depService) {
...
}]);
```
* {@link scope `$scope`}: Controllers are always associated with a point in the DOM and so are provided with
access to the {@link scope scope} at that point. Other components, such as services only have access to the
singleton {@link $rootScope} service.
* {@link $route} resolves: If a controller is instantiated as part of a route, then any values that
are resolved as part of the route are made available for injection into the controller.
+59 -53
View File
@@ -18,7 +18,7 @@ how to implement them.
## What are Directives?
At a high level, directives are markers on a DOM element (such as an attribute, element
name, or CSS class) that tell AngularJS's **HTML compiler** ({@link ng.$compile `$compile`}) to
name, comment or CSS class) that tell AngularJS's **HTML compiler** ({@link ng.$compile `$compile`}) to
attach a specified behavior to that DOM element or even transform the DOM element and its children.
Angular comes with a set of these directives built-in, like `ngBind`, `ngModel`, and `ngView`.
@@ -43,13 +43,13 @@ determines when to use a given directive.
In the following example, we say that the `<input>` element **matches** the `ngModel` directive.
```javascript
```html
<input ng-model="foo">
```
The following also **matches** `ngModel`:
```javascript
```html
<input data-ng:model="foo">
```
@@ -70,12 +70,12 @@ Here are some equivalent examples of elements that match `ngBind`:
<example module="docsBindExample">
<file name="script.js">
angular.module('docsBindExample', [])
.controller('Ctrl1', function Ctrl1($scope) {
.controller('Controller', ['$scope', function($scope) {
$scope.name = 'Max Karl Ernst Ludwig Planck (April 23, 1858 October 4, 1947)';
});
}]);
</file>
<file name="index.html">
<div ng-controller="Ctrl1">
<div ng-controller="Controller">
Hello <input ng-model='name'> <hr/>
<span ng-bind="name"></span> <br/>
<span ng:bind="name"></span> <br/>
@@ -86,7 +86,7 @@ Here are some equivalent examples of elements that match `ngBind`:
</file>
<file name="protractorTest.js">
it('should show off bindings', function() {
expect(element(by.css('div[ng-controller="Ctrl1"] span[ng-bind]')).getText())
expect(element(by.css('div[ng-controller="Controller"] span[ng-bind]')).getText())
.toBe('Max Karl Ernst Ludwig Planck (April 23, 1858 October 4, 1947)');
});
</file>
@@ -218,12 +218,12 @@ Let's create a directive that simply replaces its contents with a static templat
<example module="docsSimpleDirective">
<file name="script.js">
angular.module('docsSimpleDirective', [])
.controller('Ctrl', function($scope) {
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
})
}])
.directive('myCustomer', function() {
return {
template: 'Name: {{customer.name}} Address: {{customer.address}}'
@@ -231,7 +231,7 @@ Let's create a directive that simply replaces its contents with a static templat
});
</file>
<file name="index.html">
<div ng-controller="Ctrl">
<div ng-controller="Controller">
<div my-customer></div>
</div>
</file>
@@ -257,12 +257,12 @@ using `templateUrl` instead:
<example module="docsTemplateUrlDirective">
<file name="script.js">
angular.module('docsTemplateUrlDirective', [])
.controller('Ctrl', function($scope) {
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
})
}])
.directive('myCustomer', function() {
return {
templateUrl: 'my-customer.html'
@@ -270,7 +270,7 @@ using `templateUrl` instead:
});
</file>
<file name="index.html">
<div ng-controller="Ctrl">
<div ng-controller="Controller">
<div my-customer></div>
</div>
</file>
@@ -282,7 +282,7 @@ using `templateUrl` instead:
Great! But what if we wanted to have our directive match the tag name `<my-customer>` instead?
If we simply put a `<my-customer>` element into the HTML, it doesn't work.
<div class="alert alert-waring">
<div class="alert alert-warning">
**Note:** When you create a directive, it is restricted to attribute only by default. In order to
create directives that are triggered by element or class name, you need to use the `restrict` option.
</div>
@@ -302,12 +302,12 @@ Let's change our directive to use `restrict: 'E'`:
<example module="docsRestrictDirective">
<file name="script.js">
angular.module('docsRestrictDirective', [])
.controller('Ctrl', function($scope) {
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
})
}])
.directive('myCustomer', function() {
return {
restrict: 'E',
@@ -317,7 +317,7 @@ Let's change our directive to use `restrict: 'E'`:
</file>
<file name="index.html">
<div ng-controller="Ctrl">
<div ng-controller="Controller">
<my-customer></my-customer>
</div>
</file>
@@ -358,18 +358,18 @@ re-use such a directive:
<example module="docsScopeProblemExample">
<file name="script.js">
angular.module('docsScopeProblemExample', [])
.controller('NaomiCtrl', function($scope) {
.controller('NaomiController', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
})
.controller('IgorCtrl', function($scope) {
}])
.controller('IgorController', ['$scope', function($scope) {
$scope.customer = {
name: 'Igor',
address: '123 Somewhere'
};
})
}])
.directive('myCustomer', function() {
return {
restrict: 'E',
@@ -378,11 +378,11 @@ re-use such a directive:
});
</file>
<file name="index.html">
<div ng-controller="NaomiCtrl">
<div ng-controller="NaomiController">
<my-customer></my-customer>
</div>
<hr>
<div ng-controller="IgorCtrl">
<div ng-controller="IgorController">
<my-customer></my-customer>
</div>
</file>
@@ -400,10 +400,10 @@ we call an **isolate scope**. To do this, we can use a directive's `scope` optio
<example module="docsIsolateScopeDirective">
<file name="script.js">
angular.module('docsIsolateScopeDirective', [])
.controller('Ctrl', function($scope) {
.controller('Controller', ['$scope', function($scope) {
$scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };
$scope.igor = { name: 'Igor', address: '123 Somewhere' };
})
}])
.directive('myCustomer', function() {
return {
restrict: 'E',
@@ -415,7 +415,7 @@ we call an **isolate scope**. To do this, we can use a directive's `scope` optio
});
</file>
<file name="index.html">
<div ng-controller="Ctrl">
<div ng-controller="Controller">
<my-customer info="naomi"></my-customer>
<hr>
<my-customer info="igor"></my-customer>
@@ -473,11 +473,11 @@ within our directive's template:
<example module="docsIsolationExample">
<file name="script.js">
angular.module('docsIsolationExample', [])
.controller('Ctrl', function($scope) {
.controller('Controller', ['$scope', function($scope) {
$scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };
$scope.vojta = { name: 'Vojta', address: '3456 Somewhere Else' };
})
}])
.directive('myCustomer', function() {
return {
restrict: 'E',
@@ -489,7 +489,7 @@ within our directive's template:
});
</file>
<file name="index.html">
<div ng-controller="Ctrl">
<div ng-controller="Controller">
<my-customer info="naomi"></my-customer>
</div>
</file>
@@ -510,8 +510,8 @@ that you explicitly pass in.
<div class="alert alert-warning">
**Note:** Normally, a scope prototypically inherits from its parent. An isolated scope does not.
See the {@link guide/directive#creating-custom-directives_demo_isolating-the-scope-of-a-directive
"Isolating the Scope of a Directive"} section for more information about isolate scopes.
See the {@link api/ng/service/$compile#directive-definition-object
"Directive Definition Object - scope"} section for more information about isolate scopes.
</div>
<div class="alert alert-success">
@@ -537,16 +537,16 @@ where:
In our `link` function, we want to update the displayed time once a second, or whenever a user
changes the time formatting string that our directive binds to. We will use the `$interval` service
to call a handler on a regular basis. This is easier than using `$timeout` but also works better with
end 2 end testing, where we want to ensure that all $timeouts have completed before completing the test.
end-to-end testing, where we want to ensure that all $timeouts have completed before completing the test.
We also want to remove the `$interval` if the directive is deleted so we don't introduce a memory leak.
<example module="docsTimeDirective">
<file name="script.js">
angular.module('docsTimeDirective', [])
.controller('Ctrl2', function($scope) {
.controller('Controller', ['$scope', function($scope) {
$scope.format = 'M/d/yy h:mm:ss a';
})
.directive('myCurrentTime', function($interval, dateFilter) {
}])
.directive('myCurrentTime', ['$interval', 'dateFilter', function($interval, dateFilter) {
function link(scope, element, attrs) {
var format,
@@ -574,10 +574,10 @@ We also want to remove the `$interval` if the directive is deleted so we don't i
return {
link: link
};
});
}]);
</file>
<file name="index.html">
<div ng-controller="Ctrl2">
<div ng-controller="Controller">
Date format: <input ng-model="format"> <hr/>
Current time is: <span my-current-time="format"></span>
</div>
@@ -619,9 +619,9 @@ To do this, we need to use the `transclude` option.
<example module="docsTransclusionDirective">
<file name="script.js">
angular.module('docsTransclusionDirective', [])
.controller('Ctrl', function($scope) {
.controller('Controller', ['$scope', function($scope) {
$scope.name = 'Tobias';
})
}])
.directive('myDialog', function() {
return {
restrict: 'E',
@@ -631,7 +631,7 @@ To do this, we need to use the `transclude` option.
});
</file>
<file name="index.html">
<div ng-controller="Ctrl">
<div ng-controller="Controller">
<my-dialog>Check out the contents, {{name}}!</my-dialog>
</div>
</file>
@@ -650,9 +650,9 @@ that redefines `name` as `Jeff`. What do you think the `{{name}}` binding will r
<example module="docsTransclusionExample">
<file name="script.js">
angular.module('docsTransclusionExample', [])
.controller('Ctrl', function($scope) {
.controller('Controller', ['$scope', function($scope) {
$scope.name = 'Tobias';
})
}])
.directive('myDialog', function() {
return {
restrict: 'E',
@@ -666,7 +666,7 @@ that redefines `name` as `Jeff`. What do you think the `{{name}}` binding will r
});
</file>
<file name="index.html">
<div ng-controller="Ctrl">
<div ng-controller="Controller">
<my-dialog>Check out the contents, {{name}}!</my-dialog>
</div>
</file>
@@ -701,7 +701,7 @@ own behavior to it.
<example module="docsIsoFnBindExample">
<file name="script.js">
angular.module('docsIsoFnBindExample', [])
.controller('Ctrl', function($scope, $timeout) {
.controller('Controller', ['$scope', '$timeout', function($scope, $timeout) {
$scope.name = 'Tobias';
$scope.hideDialog = function () {
$scope.dialogIsHidden = true;
@@ -709,7 +709,7 @@ own behavior to it.
$scope.dialogIsHidden = false;
}, 2000);
};
})
}])
.directive('myDialog', function() {
return {
restrict: 'E',
@@ -722,7 +722,7 @@ own behavior to it.
});
</file>
<file name="index.html">
<div ng-controller="Ctrl">
<div ng-controller="Controller">
<my-dialog ng-hide="dialogIsHidden" on-close="hideDialog()">
Check out the contents, {{name}}!
</my-dialog>
@@ -737,7 +737,7 @@ own behavior to it.
</example>
We want to run the function we pass by invoking it from the directive's scope, but have it run
in the context of the scope where its registered.
in the context of the scope where it's registered.
We saw earlier how to use `=attr` in the `scope` option, but in the above example, we're using
`&attr` instead. The `&` binding allows a directive to trigger evaluation of an expression in
@@ -747,7 +747,8 @@ callback functions to directive behaviors.
When the user clicks the `x` in the dialog, the directive's `close` function is called, thanks to
`ng-click.` This call to `close` on the isolated scope actually evaluates the expression
`hideDialog()` in the context of the original scope, thus running `Ctrl`'s `hideDialog` function.
`hideDialog()` in the context of the original scope, thus running `Controller`'s `hideDialog`
function.
<div class="alert alert-success">
**Best Practice:** use `&attr` in the `scope` option when you want your directive
@@ -766,8 +767,8 @@ element?
<example module="dragModule">
<file name="script.js">
angular.module('dragModule', []).
directive('myDraggable', function($document) {
angular.module('dragModule', [])
.directive('myDraggable', ['$document', function($document) {
return function(scope, element, attr) {
var startX = 0, startY = 0, x = 0, y = 0;
@@ -797,11 +798,11 @@ element?
}
function mouseup() {
$document.unbind('mousemove', mousemove);
$document.unbind('mouseup', mouseup);
$document.off('mousemove', mousemove);
$document.off('mouseup', mouseup);
}
};
});
}]);
</file>
<file name="index.html">
<span my-draggable>Drag ME</span>
@@ -900,6 +901,11 @@ So where does this `myTabs` controller come from? Directives can specify control
the unsurprisingly named `controller` option. As you can see, the `myTabs` directive uses this
option. Just like `ngController`, this option attaches a controller to the template of the directive.
If it is necessary to reference the controller or any functions bound to the controller's scope in
the template, you can use the option `controllerAs` to specify the name of the controller as an alias.
The directive needs to define a scope for this configuration to be used. This is particularly useful
in the case when the directive is used as a component.
Looking back at `myPane`'s definition, notice the last argument in its `link` function: `tabsCtrl`.
When a directive requires a controller, it receives that controller as the fourth argument of its
`link` function. Taking advantage of this, `myPane` can call the `addPane` function of `myTabs`.
+63 -291
View File
@@ -3,311 +3,83 @@
@name E2E Testing
@description
**Angular Scenario Runner is in maintenance mode - If you're starting a new Angular project,
consider using [Protractor](https://github.com/angular/protractor).**
# E2E Testing
<div class="alert alert-danger">
**Note:** In the past, end to end testing could be done with a deprecated tool called
[Angular Scenario Runner](http://code.angularjs.org/1.2.16/docs/guide/e2e-testing). That tool
is now in maintenance mode.
</div>
As applications grow in size and complexity, it becomes unrealistic to rely on manual testing to
verify the correctness of new features, catch bugs and notice regressions.
verify the correctness of new features, catch bugs and notice regressions. Unit tests
are the first line of defense for catching bugs, but sometimes issues come up with integration
between components which can't be captured in a unit test. End to end tests are made to find
these problems.
To solve this problem, we have built an Angular Scenario Runner which simulates user interactions
that will help you verify the health of your Angular application.
We have built [Protractor](https://github.com/angular/protractor), an end
to end test runner which simulates user interactions that will help you verify the health of your
Angular application.
# Overview
You write scenario tests in JavaScript. These tests describe how your application should behave
given a certain interaction in a specific state. A scenario is comprised of one or more `it` blocks
(you can think of these as the requirements of your application), which in turn are made of
**commands** and **expectations**. Commands tell the Runner to do something with the application
(such as navigate to a page or click on a button), and expectations tell the Runner to assert
something about the state (such as the value of a field or the current URL). If any expectation
fails, the runner marks the `it` as "failed" and continues on to the next one. Scenarios may also
have **beforeEach** and **afterEach** blocks, which will be run before (or after) each `it` block,
regardless of whether they pass or fail.
## Using Protractor
Protractor is a [Node.js](http://nodejs.org) program, and runs end to end tests that are also
written in JavaScript and run with node. Protractor uses [WebDriver](https://code.google.com/p/selenium/wiki/GettingStarted)
to control browsers and simulate user actions.
For more information on Protractor, view [getting started](https://github.com/angular/protractor/blob/master/docs/getting-started.md)
or the [api docs](https://github.com/angular/protractor/blob/master/docs/api.md).
Protractor uses [Jasmine](http://jasmine.github.io/1.3/introduction.html) for its test syntax.
As in unit testing, a test file is comprised of one or
more `it` blocks that describe the requirements of your application. `it` blocks are made of
**commands** and **expectations**. Commands tell Protractor to do something with the application
such as navigate to a page or click on a button. Expectations tell Protractor to assert something
about the application's state, such as the value of a field or the current URL.
If any expectation within an `it` block fails, the runner marks the `it` as "failed" and continues
on to the next block.
Test files may also have `beforeEach` and `afterEach` blocks, which will be run before or after
each `it` block regardless of whether the block passes or fails.
<img src="img/guide/scenario_runner.png">
In addition to the above elements, scenarios may also contain helper functions to avoid duplicating
In addition to the above elements, tests may also contain helper functions to avoid duplicating
code in the `it` blocks.
Here is an example of a simple scenario:
Here is an example of a simple test:
```js
describe('Buzz Client', function() {
it('should filter results', function() {
input('user').enter('jacksparrow');
element(':button').click();
expect(repeater('ul li').count()).toEqual(10);
input('filterText').enter('Bees');
expect(repeater('ul li').count()).toEqual(1);
});
describe('TODO list', function() {
it('should filter results', function() {
// Find the element with ng-model="user" and type "jacksparrow" into it
element(by.model('user')).sendKeys('jacksparrow');
// Find the first (and only) button on the page and click it
element(by.css(':button')).click();
// Verify that there are 10 tasks
expect(element.all(by.repeater('task in tasks')).count()).toEqual(10);
// Enter 'groceries' into the element with ng-model="filterText"
element(by.model('filterText')).sendKeys('groceries');
// Verify that now there is only one item in the task list
expect(element.all(by.repeater('task in tasks')).count()).toEqual(1);
});
});
```
Note that
[`input('user')`](https://github.com/angular/angular.js/blob/master/docs/content/guide/dev_guide.e2e-testing.ngdoc#L119)
finds the `<input>` element with `ng-model="user"` not `name="user"`.
This test describes the requirements of a ToDo list, specifically, that it should be able to
filter the list of items.
This scenario describes the requirements of a Buzz Client, specifically, that it should be able to
filter the stream of the user. It starts by entering a value in the input field with ng-model="user", clicking
the only button on the page, and then it verifies that there are 10 items listed. It then enters
'Bees' in the input field with ng-model='filterText' and verifies that the list is reduced to a single item.
## Example
See the [angular-seed](https://github.com/angular/angular-seed) project for more examples, or look
at the embedded examples in the Angular documentation (For example, [$http](http://docs.angularjs.org/api/ng/service/$http)
has an end to end test in the example under the `protractor.js` tag).
The API section below lists the available commands and expectations for the Runner.
## Caveats
# API
Source: https://github.com/angular/angular.js/blob/master/src/ngScenario/dsl.js
## pause()
Pauses the execution of the tests until you call `resume()` in the console (or click the resume
link in the Runner UI).
## sleep(seconds)
Pauses the execution of the tests for the specified number of `seconds`.
## browser().navigateTo(url)
Loads the `url` into the test frame.
## browser().navigateTo(url, fn)
Loads the URL returned by `fn` into the testing frame. The given `url` is only used for the test
output. Use this when the destination URL is dynamic (that is, the destination is unknown when you
write the test).
## browser().reload()
Refreshes the currently loaded page in the test frame.
## browser().window().href()
Returns the window.location.href of the currently loaded page in the test frame.
## browser().window().path()
Returns the window.location.pathname of the currently loaded page in the test frame.
## browser().window().search()
Returns the window.location.search of the currently loaded page in the test frame.
## browser().window().hash()
Returns the window.location.hash (without `#`) of the currently loaded page in the test frame.
## browser().location().url()
Returns the {@link ng.$location $location.url()} of the currently loaded page in
the test frame.
## browser().location().path()
Returns the {@link ng.$location $location.path()} of the currently loaded page in
the test frame.
## browser().location().search()
Returns the {@link ng.$location $location.search()} of the currently loaded page
in the test frame.
## browser().location().hash()
Returns the {@link ng.$location $location.hash()} of the currently loaded page in
the test frame.
## expect(future).{matcher}
Asserts the value of the given `future` satisfies the `matcher`. All API statements return a
`future` object, which get a `value` assigned after they are executed. Matchers are defined using
`angular.scenario.matcher`, and they use the value of futures to run the expectation. For example:
`expect(browser().location().href()).toEqual('http://www.google.com')`. Available matchers
are presented further down this document.
## expect(future).not().{matcher}
Asserts the value of the given `future` satisfies the negation of the `matcher`.
## using(selector, label)
Scopes the next DSL element selection.
## binding(name)
Returns the value of the first binding matching the given `name`.
## input(name).enter(value)
Enters the given `value` in the text field with the corresponding ng-model `name`.
## input(name).check()
Checks/unchecks the checkbox with the corresponding ng-model `name`.
## input(name).select(value)
Selects the given `value` in the radio button with the corresponding ng-model `name`.
## input(name).val()
Returns the current value of an input field with the corresponding ng-model `name`.
## repeater(selector, label).count()
Returns the number of rows in the repeater matching the given jQuery `selector`. The `label` is
used for test output.
## repeater(selector, label).row(index)
Returns an array with the bindings in the row at the given `index` in the repeater matching the
given jQuery `selector`. The `label` is used for test output.
## repeater(selector, label).column(binding)
Returns an array with the values in the column with the given `binding` in the repeater matching
the given jQuery `selector`. The `label` is used for test output.
## select(name).option(value)
Picks the option with the given `value` on the select with the given ng-model `name`.
## select(name).options(value1, value2...)
Picks the options with the given `values` on the multi select with the given ng-model `name`.
## element(selector, label).count()
Returns the number of elements that match the given jQuery `selector`. The `label` is used for test
output.
## element(selector, label).click()
Clicks on the element matching the given jQuery `selector`. The `label` is used for test output.
## element(selector, label).query(fn)
Executes the function `fn(selectedElements, done)`, where selectedElements are the elements that
match the given jQuery `selector` and `done` is a function that is called at the end of the `fn`
function. The `label` is used for test output.
## element(selector, label).{method}()
Returns the result of calling `method` on the element matching the given jQuery `selector`, where
`method` can be any of the following jQuery methods: `val`, `text`, `html`, `height`,
`innerHeight`, `outerHeight`, `width`, `innerWidth`, `outerWidth`, `position`, `scrollLeft`,
`scrollTop`, `offset`. The `label` is used for test output.
## element(selector, label).{method}(value)
Executes the `method` passing in `value` on the element matching the given jQuery `selector`, where
`method` can be any of the following jQuery methods: `val`, `text`, `html`, `height`,
`innerHeight`, `outerHeight`, `width`, `innerWidth`, `outerWidth`, `position`, `scrollLeft`,
`scrollTop`, `offset`. The `label` is used for test output.
## element(selector, label).{method}(key)
Returns the result of calling `method` passing in `key` on the element matching the given jQuery
`selector`, where `method` can be any of the following jQuery methods: `attr`, `prop`, `css`. The
`label` is used for test output.
## element(selector, label).{method}(key, value)
Executes the `method` passing in `key` and `value` on the element matching the given jQuery
`selector`, where `method` can be any of the following jQuery methods: `attr`, `prop`, `css`. The
`label` is used for test output.
# Matchers
Matchers are used in combination with the `expect(...)` function as described above and can
be negated with `not()`. For instance: `expect(element('h1').text()).not().toEqual('Error')`.
Source: https://github.com/angular/angular.js/blob/master/src/ngScenario/matchers.js
```js
// value and Object comparison following the rules of angular.equals().
expect(value).toEqual(value)
// a simpler value comparison using ===
expect(value).toBe(value)
// checks that the value is defined by checking its type.
expect(value).toBeDefined()
// the following two matchers are using JavaScript's standard truthiness rules
expect(value).toBeTruthy()
expect(value).toBeFalsy()
// verify that the value matches the given regular expression. The regular
// expression may be passed in form of a string or a regular expression
// object.
expect(value).toMatch(expectedRegExp)
// a check for null using ===
expect(value).toBeNull()
// Array.indexOf(...) is used internally to check whether the element is
// contained within the array.
expect(value).toContain(expected)
// number comparison using < and >
expect(value).toBeLessThan(expected)
expect(value).toBeGreaterThan(expected)
```
# Example
See the [angular-seed](https://github.com/angular/angular-seed) project for more examples.
## Conditional actions with element(...).query(fn)
E2E testing with angular scenario is highly asynchronous and hides a lot of complexity by
queueing actions and expectations that can handle futures. From time to time, you might need
conditional assertions or element selection. Even though you should generally try to avoid this
(as it is can be sign for unstable tests), you can add conditional behavior with
`element(...).query(fn)`. The following code listing shows how this function can be used to delete
added entries (where an entry is some domain object) using the application's web interface.
Imagine the application to be structured into two views:
1. *Overview view* which lists all the added entries in a table and
2. a *detail view* which shows the entries' details and contains a delete button. When clicking the
delete button, the user is redirected back to the *overview page*.
```js
beforeEach(function () {
var deleteEntry = function () {
browser().navigateTo('/entries');
// we need to select the <tbody> element as it might be the case that there
// are no entries (and therefore no rows). When the selector does not
// result in a match, the test would be marked as a failure.
element('table tbody').query(function (tbody, done) {
// ngScenario gives us a jQuery lite wrapped element. We call the
// `children()` function to retrieve the table body's rows
var children = tbody.children();
if (children.length > 0) {
// if there is at least one entry in the table, click on the link to
// the entry's detail view
element('table tbody a').click();
// and, after a route change, click the delete button
element('.btn-danger').click();
}
// if there is more than one entry shown in the table, queue another
// delete action.
if (children.length > 1) {
deleteEntry();
}
// remember to call `done()` so that ngScenario can continue
// test execution.
done();
});
};
// start deleting entries
deleteEntry();
});
```
In order to understand what is happening, we should emphasize that ngScenario calls are not
immediately executed, but queued (in ngScenario terms, we would be talking about adding
future actions). If we had only one entry in our table, then the following future actions
would be queued:
```js
// delete entry 1
browser().navigateTo('/entries');
element('table tbody').query(function (tbody, done) { ... });
element('table tbody a');
element('.btn-danger').click();
```
For two entries, ngScenario would have to work on the following queue:
```js
// delete entry 1
browser().navigateTo('/entries');
element('table tbody').query(function (tbody, done) { ... });
element('table tbody a');
element('.btn-danger').click();
// delete entry 2
// indented to represent "recursion depth"
browser().navigateTo('/entries');
element('table tbody').query(function (tbody, done) { ... });
element('table tbody a');
element('.btn-danger').click();
```
# Caveats
`ngScenario` does not work with apps that manually bootstrap using `angular.bootstrap`. You must use the `ng-app` directive.
Protractor does not work out-of-the-box with apps that bootstrap manually using
`angular.bootstrap`. You must use the `ng-app` directive.
+174 -9
View File
@@ -2,7 +2,9 @@
@name Expressions
@description
Expressions are JavaScript-like code snippets that are usually placed in bindings such as
# Angular Expressions
Angular expressions are JavaScript-like code snippets that are usually placed in bindings such as
`{{ expression }}`.
For example, these are valid expressions in Angular:
@@ -88,21 +90,23 @@ You can try evaluating different expressions here:
</example>
# Context
## Context
Angular does not use JavaScript's `eval()` to evaluate expressions. Instead Angular's
{@link ng.$parse $parse} service processes these expressions.
Unlike JavaScript, where names default to global `window` properties, Angular expressions must use
{@link ng.$window `$window`} explicitly to refer to the global `window` object. For example, if you
want to call `alert()` in an expression you must use `$window.alert()`. This restriction is
intentional. It prevents accidental access to the global state a common source of subtle bugs.
Angular expressions do not have access to global variables like `window`, `document` or `location`.
This restriction is intentional. It prevents accidental access to the global state a common source of subtle bugs.
Instead use services like `$window` and `$location` in functions called from expressions. Such services
provide mockable access to globals.
<example>
<file name="index.html">
<div class="example2" ng-controller="Cntl1">
Name: <input ng-model="name" type="text"/>
<button ng-click="greet()">Greet</button>
<button ng-click="window.alert('Should not see me')">Won't greet</button>
</div>
</file>
@@ -150,7 +154,168 @@ Similarly, invoking a function `a.b.c()` on `undefined` or `null` simply returns
## No Control Flow Statements
You cannot write a control flow statement in an expression. The reason behind this is core to the
Angular philosophy that application logic should be in controllers, not the views. If you need a
conditional, loop, or to throw from a view expression, delegate to a JavaScript method instead.
Apart from the ternary operator (`a ? b : c`), you cannot write a control flow statement in an
expression. The reason behind this is core to the Angular philosophy that application logic should
be in controllers, not the views. If you need a real conditional, loop, or to throw from a view
expression, delegate to a JavaScript method instead.
## `$event`
Directives like {@link ng.directive:ngClick `ngClick`} and {@link ng.directive:ngFocus `ngFocus`}
expose a `$event` object within the scope of that expression.
<example module="eventExampleApp">
<file name="index.html">
<div ng-controller="EventController">
<button ng-click="clickMe($event)">Event</button>
<p><code>$event</code>: <pre> {{$event | json}}</pre></p>
<p><code>clickEvent</code>: <pre>{{clickEvent | json}}</pre></p>
</div>
</file>
<file name="script.js">
angular.module('eventExampleApp', []).
controller('EventController', ['$scope', function($scope) {
/*
* expose the event object to the scope
*/
$scope.clickMe = function(clickEvent) {
$scope.clickEvent = simpleKeys(clickEvent);
console.log(clickEvent);
};
/*
* return a copy of an object with only non-object keys
* we need this to avoid circular references
*/
function simpleKeys (original) {
return Object.keys(original).reduce(function (obj, key) {
obj[key] = typeof original[key] === 'object' ? '{ ... }' : original[key];
return obj;
}, {});
}
}]);
</file>
</example>
Note in the example above how we can pass in `$event` to `clickMe`, but how it does not show up
in `{{$event}}`. This is because `$event` is outside the scope of that binding.
## One-time binding
An expression that starts with `::` is considered a one-time expression. One-time expressions
will stop recalculating once they are stable, which happens after the first digest if the expression
result is a non-undefined value (see value stabilization algorithm below).
<example module="oneTimeBidingExampleApp">
<file name="index.html">
<div ng-controller="EventController">
<button ng-click="clickMe($event)">Click Me</button>
<p id="one-time-binding-example">One time binding: {{::name}}</p>
<p id="normal-binding-example">Normal binding: {{name}}</p>
</div>
</file>
<file name="script.js">
angular.module('oneTimeBidingExampleApp', []).
controller('EventController', ['$scope', function($scope) {
var counter = 0;
var names = ['Igor', 'Misko', 'Chirayu', 'Lucas'];
/*
* expose the event object to the scope
*/
$scope.clickMe = function(clickEvent) {
$scope.name = names[counter % names.length];
counter++;
};
}]);
</file>
<file name="protractor.js" type="protractor">
it('should freeze binding after its value has stabilized', function() {
var oneTimeBiding = element(by.id('one-time-binding-example'));
var normalBinding = element(by.id('normal-binding-example'));
expect(oneTimeBiding.getText()).toEqual('One time binding:');
expect(normalBinding.getText()).toEqual('Normal binding:');
element(by.buttonText('Click Me')).click();
expect(oneTimeBiding.getText()).toEqual('One time binding: Igor');
expect(normalBinding.getText()).toEqual('Normal binding: Igor');
element(by.buttonText('Click Me')).click();
expect(oneTimeBiding.getText()).toEqual('One time binding: Igor');
expect(normalBinding.getText()).toEqual('Normal binding: Misko');
element(by.buttonText('Click Me')).click();
element(by.buttonText('Click Me')).click();
expect(oneTimeBiding.getText()).toEqual('One time binding: Igor');
expect(normalBinding.getText()).toEqual('Normal binding: Lucas');
});
</file>
</example>
### Why this feature
The main purpose of one-time binding expression is to provide a way to create a binding
that gets deregistered and frees up resources once the binding is stabilized.
Reducing the number of expressions being watched makes the digest loop faster and allows more
information to be displayed at the same time.
### Value stabilization algorithm
One-time binding expressions will retain the value of the expression at the end of the
digest cycle as long as that value is not undefined. If the value of the expression is set
within the digest loop and later, within the same digest loop, it is set to undefined,
then the expression is not fulfilled and will remain watched.
1. Given an expression that starts with `::` when a digest loop is entered and expression
is dirty-checked store the value as V
2. If V is not undefined mark the result of the expression as stable and schedule a task
to deregister the watch for this expression when we exit the digest loop
3. Process the digest loop as normal
4. When digest loop is done and all the values have settled process the queue of watch
deregistration tasks. For each watch to be deregistered check if it still evaluates
to value that is not `undefined`. If that's the case, deregister the watch. Otherwise
keep dirty-checking the watch in the future digest loops by following the same
algorithm starting from step 1
### How to benefit from one-time binding
When interpolating text or attributes. If the expression, once set, will not change
then it is a candidate for one-time expression.
```html
<div name="attr: {{::color}}">text: {{::name}}</div>
```
When using a directive with bidirectional binding and the parameters will not change
```js
someModule.directive('someDirective', function() {
return {
scope: {
name: '=',
color: '@'
},
template: '{{name}}: {{color}}'
};
});
```
```html
<div some-directive name=“::myName” color=“My color is {{::myColor}}”></div>
```
When using a directive that takes an expression
```html
<ul>
<li ng-repeat="item in ::items">{{item.name}};</li>
</ul>
```
+15 -15
View File
@@ -5,7 +5,7 @@
A filter formats the value of an expression for display to the user. They can be used in view templates,
controllers or services and it is easy to define your own filter.
The underlying API is the {@link ng.$filterProvider filterProvider}.
The underlying API is the {@link ng.$filterProvider `filterProvider`}.
## Using filters in view templates
@@ -29,12 +29,12 @@ E.g. the markup `{{ 1234 | number:2 }}` formats the number 1234 with 2 decimal p
{@link ng.filter:number `number`} filter. The resulting value is `1,234.00`.
## Using filters in controllers and services
## Using filters in controllers, services, and directives
You can also use filters in controllers and services. For this, add a dependency with the name `<filterName>Filter`
to your controller or service. E.g. using the dependency `numberFilter` will inject the number filter.
The injected argument is a function that takes the value to format as first argument and filter parameters
starting with the second argument.
You can also use filters in controllers, services, and directives. For this, inject a dependency
with the name `<filterName>Filter` to your controller/service/directive. E.g. using the dependency
`numberFilter` will inject the number filter. The injected argument is a function that takes the
value to format as first argument and filter parameters starting with the second argument.
The example below uses the filter called {@link ng.filter:filter `filter`}.
This filter reduces arrays into sub arrays based on
@@ -89,9 +89,9 @@ function.
The following sample filter reverses a text string. In addition, it conditionally makes the
text upper-case.
<example module="MyReverseModule">
<example module="myReverseModule">
<file name="index.html">
<div ng-controller="Ctrl">
<div ng-controller="Controller">
<input ng-model="greeting" type="text"><br>
No filter: {{greeting}}<br>
Reverse: {{greeting|reverse}}<br>
@@ -100,9 +100,10 @@ text upper-case.
</file>
<file name="script.js">
angular.module('MyReverseModule', []).
filter('reverse', function() {
angular.module('myReverseModule', [])
.filter('reverse', function() {
return function(input, uppercase) {
input = input || '';
var out = "";
for (var i = 0; i < input.length; i++) {
out = input.charAt(i) + out;
@@ -113,11 +114,10 @@ text upper-case.
}
return out;
};
});
function Ctrl($scope) {
$scope.greeting = 'hello';
}
})
.controller('Controller', ['$scope', function($scope) {
$scope.greeting = 'hello';
}]);
</file>
</example>
+78
View File
@@ -62,6 +62,8 @@ To allow styling of form as well as controls, `ngModel` adds these CSS classes:
- `ng-invalid`
- `ng-pristine`
- `ng-dirty`
- `ng-touched`
- `ng-untouched`
The following example uses the CSS to display validity of each form control.
In the example both `user.name` and `user.email` are required, but are rendered with red background only when they are dirty.
@@ -181,6 +183,82 @@ This allows us to extend the above example with these features:
# Custom triggers
By default, any change to the content will trigger a model update and form validation. You can
override this behavior using the {@link ng.directive:ngModelOptions ngModelOptions} directive to
bind only to specified list of events. I.e. `ng-model-options="{ updateOn: 'blur' }"` will update
and validate only after the control loses focus. You can set several events using a space delimited
list. I.e. `ng-model-options="{ updateOn: 'mousedown blur' }"`
If you want to keep the default behavior and just add new events that may trigger the model update
and validation, add "default" as one of the specified events.
I.e. `ng-model-options="{ updateOn: 'default blur' }"`
The following example shows how to override immediate updates. Changes on the inputs within the form will update the model
only when the control loses focus (blur event).
<example>
<file name="index.html">
<div ng-controller="ControllerUpdateOn">
<form>
Name:
<input type="text" ng-model="user.name" ng-model-options="{ updateOn: 'blur' }" /><br />
Other data:
<input type="text" ng-model="user.data" /><br />
</form>
<pre>username = "{{user.name}}"</pre>
</div>
</file>
<file name="script.js">
function ControllerUpdateOn($scope) {
$scope.user = {};
}
</file>
</example>
# Non-immediate (debounced) model updates
You can delay the model update/validation by using the `debounce` key with the
{@link ng.directive:ngModelOptions ngModelOptions} directive. This delay will also apply to
parsers, validators and model flags like `$dirty` or `$pristine`.
I.e. `ng-model-options="{ debounce: 500 }"` will wait for half a second since
the last content change before triggering the model update and form validation.
If custom triggers are used, custom debouncing timeouts can be set for each event using an object
in `debounce`. This can be useful to force immediate updates on some specific circumstances
(like blur events).
I.e. `ng-model-options="{ updateOn: 'default blur', debounce: { default: 500, blur: 0 } }"`
If those attributes are added to an element, they will be applied to all the child elements and controls that inherit from it unless they are
overridden.
This example shows how to debounce model changes. Model will be updated only 250 milliseconds after last change.
<example>
<file name="index.html">
<div ng-controller="ControllerUpdateOn">
<form>
Name:
<input type="text" ng-model="user.name" ng-model-options="{ debounce: 250 }" /><br />
</form>
<pre>username = "{{user.name}}"</pre>
</div>
</file>
<file name="script.js">
function ControllerUpdateOn($scope) {
$scope.user = {};
}
</file>
</example>
# Custom Validation
Angular provides basic implementation for most common html5 {@link ng.directive:input input}
+73 -59
View File
@@ -2,53 +2,54 @@
@name i18n and l10n
@description
# I18n and L10n in AngularJS
# i18n and l10n
**What is i18n and l10n?**
Internationalization (i18n) is the process of developing products in such a way that they can be
localized for languages and cultures easily. Localization (l10n), is the process of adapting
applications and text to enable their usability in a particular cultural or linguistic market. For
application developers, internationalizing an application means abstracting all of the strings and
other locale-specific bits (such as date or currency formats) out of the application. Localizing an
application means providing translations and localized formats for the abstracted bits.
Internationalization, abbreviated i18n, is the process of developing products in such a way that
they can be localized for languages and cultures easily. Localization, abbreviated l10n, is the
process of adapting applications and text to enable their usability in a particular cultural or
linguistic market. For application developers, internationalizing an application means abstracting
all of the strings and other locale-specific bits (such as date or currency formats) out of the
application. Localizing an application means providing translations and localized formats for the
abstracted bits.
**What level of support for i18n/l10n is currently in Angular?**
## How does Angular support i18n/l10n?
Currently, Angular supports i18n/l10n for
[datetime](http://docs.angularjs.org/#!/api/ng.filter:date),
[number](http://docs.angularjs.org/#!/api/ng.filter:number) and
[currency](http://docs.angularjs.org/#!/api/ng.filter:currency) filters.
Angular supports i18n/l10n for {@link ng.filter:date date}, {@link ng.filter:number number} and
{@link ng.filter:currency currency} filters.
Additionally, Angular supports localizable pluralization support provided by the {@link
ng.directive:ngPluralize ngPluralize directive}.
Additionally, Angular supports localizable pluralization support through the {@link
ng.directive:ngPluralize `ngPluralize` directive}.
All localizable Angular components depend on locale-specific rule sets managed by the {@link
ng.$locale $locale service}.
ng.$locale `$locale` service}.
For readers who want to jump straight into examples, we have a few web pages that showcase how to
use Angular filters with various locale rule sets. You can find these examples either on
[Github](https://github.com/angular/angular.js/tree/master/i18n/e2e) or in the i18n/e2e folder of
Angular development package.
There a few examples that showcase how to use Angular filters with various locale rule sets in the
[`i18n/e2e` directory](https://github.com/angular/angular.js/tree/master/i18n/e2e) of the Angular
source code.
**What is a locale id?**
## What is a locale ID?
A locale is a specific geographical, political, or cultural region. The most commonly used locale
ID consists of two parts: language code and country code. For example, en-US, en-AU, zh-CN are all
valid locale IDs that have both language codes and country codes. Because specifying a country code
in locale ID is optional, locale IDs such as en, zh, and sk are also valid. See the
[ICU ](http://userguide.icu-project.org/locale) website for more information about using locale IDs.
ID consists of two parts: language code and country code. For example, `en-US`, `en-AU`, and
`zh-CN` are all valid locale IDs that have both language codes and country codes. Because
specifying a country code in locale ID is optional, locale IDs such as `en`, `zh`, and `sk` are
also valid. See the [ICU](http://userguide.icu-project.org/locale) website for more information
about using locale IDs.
## Supported locales in Angular
**Supported locales in Angular**
Angular separates number and datetime format rule sets into different files, each file for a
particular locale. You can find a list of currently supported locales
[here](https://github.com/angular/angular.js/tree/master/src/ngLocale)
# Providing locale rules to Angular
## Providing locale rules to Angular
There are two approaches to providing locale rules to Angular:
**1. Pre-bundled rule sets**
### 1. Pre-bundled rule sets
You can pre-bundle the desired locale file with Angular by concatenating the content of the
locale-specific file to the end of `angular.js` or `angular.min.js` file.
@@ -61,7 +62,7 @@ locale, you can do the following:
When the application containing `angular_de-de.js` script instead of the generic angular.js script
starts, Angular is automatically pre-configured with localization rules for the german locale.
**2. Including locale js script in index.html page**
### 2. Including a locale script in `index.html`
You can also include the locale specific js file in the index.html page. For example, if one client
requires German locale, you would serve index_de-de.html which will look something like this:
@@ -77,48 +78,61 @@ requires German locale, you would serve index_de-de.html which will look somethi
</html>
```
**Comparison of the two approaches**
Both approaches described above requires you to prepare different index.html pages or js files for
each locale that your app may be localized into. You also need to configure your server to serve
### Comparison of the two approaches
Both approaches described above require you to prepare different `index.html` pages or JavaScript
files for each locale that your app may use. You also need to configure your server to serve
the correct file that correspond to the desired locale.
However, the second approach (Including locale js script in index.html page) is likely to be slower
because an extra script needs to be loaded.
The second approach (including the locale JavaScript file in `index.html`) may be slower because
an extra script needs to be loaded.
# "Gotchas"
## Caveats
**Currency symbol "gotcha"**
Although Angular makes i18n convenient, there are several things you need to be conscious of as you
develop your app.
Angular's [currency filter](http://docs.angularjs.org/#!/api/ng.filter:currency) allows
you to use the default currency symbol from the {@link ng.$locale locale service},
or you can provide the filter with a custom currency symbol. If your app will be used only in one
locale, it is fine to rely on the default currency symbol. However, if you anticipate that viewers
in other locales might use your app, you should provide your own currency symbol to make sure the
actual value is understood.
### Currency symbol
For example, if you want to display an account balance of 1000 dollars with the following binding
containing currency filter: `{{ 1000 | currency }}`, and your app is currently in en-US locale.
'$1000.00' will be shown. However, if someone in a different local (say, Japan) views your app, their
browser will specify the locale as ja, and the balance of '¥1000.00' will be shown instead. This
will really upset your client.
Angular's {@link ng.filter:currency currency filter} allows you to use the default currency symbol
from the {@link ng.$locale locale service}, or you can provide the filter with a custom currency
symbol.
<div class="alert alert-success">
**Best Practice:** If your app will be used only in one locale, it is fine to rely on the default
currency symbol. If you anticipate that viewers in other locales might use your app, you should
explicitly provide a currency symbol.
</div>
Let's say you are writing a banking app and you want to display an account balance of 1000 dollars.
You write the following binding using the currency filter:
```html
{{ 1000 | currency }}
```
If your app is currently in the `en-US` locale, the browser will show `$1000.00`. If someone in the
Japanese locale (`ja`) views your app, their browser will show a balance of `¥1000.00` instead.
This is problematinc because $1000 is not the same as ¥1000.
In this case, you need to override the default currency symbol by providing the
[currency filter](http://docs.angularjs.org/#!/api/ng.filter:currency) with a currency symbol as
a parameter when you configure the filter, for example, {{ 1000 | currency:"USD$"}}. This way,
Angular will always show a balance of 'USD$1000' and disregard any locale changes.
{@link ng.filter:currency} currency filter with a currency symbol as a parameter.
**Translation length "gotcha"**
If we change the above to `{{ 1000 | currency:"USD$"}}`, Angular will always show a balance of
`USD$1000` regardless of locale.
Keep in mind that translated strings/datetime formats can vary greatly in length. For example,
`June 3, 1977` will be translated to Spanish as `3 de junio de 1977`. There are bound to be other
more extreme cases. Hence, when internationalizing your apps, you need to apply CSS rules
accordingly and do thorough testing to make sure UI components do not overlap.
### Translation length
Translated strings/datetime formats can vary greatly in length. For example, `June 3, 1977` will be
translated to Spanish as `3 de junio de 1977`.
**Timezones**
When internationalizing your app, you need to do thorough testing to make sure UI components behave
as expected even when their contents vary greatly in content size.
Keep in mind that Angular datetime filter uses the time zone settings of the browser. So the same
### Timezones
The Angular datetime filter uses the time zone settings of the browser. The same
application will show different time information depending on the time zone settings of the
computer that the application is running on. Neither Javascript nor Angular currently supports
computer that the application is running on. Neither JavaScript nor Angular currently supports
displaying the date with a timezone specified by the developer.
+24 -19
View File
@@ -1,32 +1,37 @@
@ngdoc overview
@name Internet Explorer Compatibility
@name Internet Explorer Compatibility
@description
# Overview
# Internet Explorer Compatibility
<div class="alert alert-warning">
**Note:** AngularJS 1.3 is dropping support for IE8. Read more about it on
[our blog](http://blog.angularjs.org/2013/12/angularjs-13-new-release-approaches.html).
AngularJS 1.2 will continue to support IE8, but the core team does not plan to spend time
addressing issues specific to IE8 or earlier.
</div>
This document describes the Internet Explorer (IE) idiosyncrasies when dealing with custom HTML
attributes and tags. Read this document if you are planning on deploying your Angular application
on IE v8.0 or earlier.
on IE8 or earlier.
The project currently supports and will attempt to fix bugs for IE8 and above. The continuous
integration server runs all the tests against IE8. See http://ci.angularjs.org.
The project currently supports and will attempt to fix bugs for IE9 and above. The continuous
integration server runs all the tests against IE9, IE10, and IE11. See
[Travis CI](https://travis-ci.org/angular/angular.js) and
[ci.angularjs.org](http://ci.angularjs.org).
IE7 and below are not tested and the project makes no guarantee that Angular will work on it.
A subset of the AngularJS functionality may work. It is up to you to test and decide whether
it works for your particular app.
It is very unlikely that issues specific to IE7 or earlier will be given any time by the core team.
[GitHub](https://github.com/angular/angular.js/issues/4974)
We do not run tests on IE8 and below. A subset of the AngularJS functionality may work on these
browsers, but it is up to you to test and decide whether it works for your particular app.
# Short Version
## Short Version
To make your Angular application work on IE please make sure that:
1. You polyfill JSON.stringify for IE7 and below. You can use
[JSON2](https://github.com/douglascrockford/JSON-js) or
[JSON3](http://bestiejs.github.com/json3/) polyfills for this.
```html
<!doctype html>
<html xmlns:ng="http://angularjs.org">
@@ -42,7 +47,7 @@ To make your Angular application work on IE please make sure that:
```
2. add `id="ng-app"` to the root element in conjunction with `ng-app` attribute
```html
<!doctype html>
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
@@ -54,7 +59,7 @@ To make your Angular application work on IE please make sure that:
`<div ng-view>` instead), or
4. if you **do use** custom element tags, then you must take these steps to make IE 8 and below happy:
```html
<!doctype html>
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
@@ -64,7 +69,7 @@ To make your Angular application work on IE please make sure that:
document.createElement('ng-include');
document.createElement('ng-pluralize');
document.createElement('ng-view');
// Optionally these for CSS
document.createElement('ng:include');
document.createElement('ng:pluralize');
@@ -79,7 +84,7 @@ To make your Angular application work on IE please make sure that:
```
5. Use `ng-style` tags instead of `style="{{ someCss }}"`. The later works in Chrome and Firefox
but does not work in Internet Explorer <= 11 (the most recent version at time of writing).
The **important** parts are:
@@ -92,7 +97,7 @@ The **important** parts are:
happy.
# Long Version
## Long Version
IE has issues with element tag names which are not standard HTML tag names. These fall into two
categories, and each category has its own fix.
@@ -165,7 +170,7 @@ In IE, the behavior is that the `BODY` element has three children:
## CSS Styling of Custom Tag Names
To make CSS selectors work with custom elements, the custom element name must be pre-created with
To make CSS selectors work with custom elements, the custom element name must be pre-created with
`document.createElement('my-tag')` regardless of XML namespace.
```html
+7 -3
View File
@@ -57,6 +57,7 @@ In Angular applications, you move the job of filling page templates with data fr
* **Other Languages:** [CoffeeScript](http://www.coffeescriptlove.com/2013/08/angularjs-and-coffeescript-tutorials.html), [Dart](https://github.com/angular/angular.dart.tutorial/wiki)
* **Realtime: **[Socket.io](http://www.creativebloq.com/javascript/angularjs-collaboration-board-socketio-2132885), [OmniBinder](https://github.com/jeffbcross/omnibinder)
* **Visualization:** [SVG](http://gaslight.co/blog/angular-backed-svgs), [D3.js](http://www.ng-newsletter.com/posts/d3-on-angular.html)
* **Local Storage and session:** [ngStorage](https://github.com/gsklee/ngStorage)
## Tools
@@ -69,17 +70,19 @@ In Angular applications, you move the job of filling page templates with data fr
This is a short list of libraries with specific support and documentation for working with Angular. You can find a full list of all known Angular external libraries at [ngmodules.org](http://ngmodules.org/).
* **Internationalization:** [angular-translate](http://pascalprecht.github.io/angular-translate/), [angular-gettext](http://angular-gettext.rocketeer.be/)
* **Internationalization:** [angular-translate](http://angular-translate.github.io), [angular-gettext](http://angular-gettext.rocketeer.be/)
* **RESTful services:** [Restangular](https://github.com/mgonto/restangular)
* **SQL and NoSQL backends:** [BreezeJS](http://www.breezejs.com/), [AngularFire](http://angularfire.com/)
* **UI Widgets: **[KendoUI](http://kendo-labs.github.io/angular-kendo/#/), [UI Bootstrap](http://angular-ui.github.io/bootstrap/), [Wijmo](http://wijmo.com/tag/angularjs-2/)
* **UI Widgets: **[KendoUI](http://kendo-labs.github.io/angular-kendo/#/), [UI Bootstrap](http://angular-ui.github.io/bootstrap/), [Wijmo](http://wijmo.com/tag/angularjs-2/), [ngTagsInput](https://github.com/mbenford/ngTagsInput)
* **Advanced Routing:** [UI-Router](https://github.com/angular-ui/ui-router)
* **Maps:** [UI-Map (Google Maps)](https://github.com/angular-ui/ui-map)
## Deployment
### General
* **Javascript minification: **[Background](http://thegreenpizza.github.io/2013/05/25/building-minification-safe-angular.js-applications/), [ngmin automation tool](http://www.thinkster.io/pick/XlWneEZCqY/angularjs-ngmin)
* **Tracking:** [Angularyitcs (Google Analytics)](http://ngmodules.org/modules/angularytics), [Logging Client-Side Errors](http://www.bennadel.com/blog/2542-Logging-Client-Side-Errors-With-AngularJS-And-Stacktrace-js.htm)
* **Analytics and Logging:** [Angularyitcs (Google Analytics)](http://ngmodules.org/modules/angularytics), [Angulartics (Analytics)](https://github.com/luisfarzati/angulartics), [Logging Client-Side Errors](http://www.bennadel.com/blog/2542-Logging-Client-Side-Errors-With-AngularJS-And-Stacktrace-js.htm)
* **SEO:** [By hand](http://www.yearofmoo.com/2012/11/angularjs-and-seo.html), [prerender.io](http://prerender.io/), [Brombone](http://www.brombone.com/), [SEO.js](http://getseojs.com/), [SEO4Ajax](http://www.seo4ajax.com/)
### Server-Specific
@@ -110,6 +113,7 @@ This is a short list of libraries with specific support and documentation for wo
* **Free online:**
[thinkster.io](http://thinkster.io),
[CodeAcademy](http://www.codecademy.com/courses/javascript-advanced-en-2hJ3J/0/1)
[CodeSchool](https://www.codeschool.com/courses/shaping-up-with-angular-js)
* **Paid online:**
[Pluralsite (3 courses)](http://www.pluralsight.com/training/Courses/Find?highlight=true&searchTerm=angularjs),
[Tuts+](https://tutsplus.com/course/easier-js-apps-with-angular/),
+83 -7
View File
@@ -2,6 +2,7 @@
@name Migrating from 1.0 to 1.2
@description
# Migrating from 1.0 to 1.2
AngularJS version 1.2 introduces several breaking changes that may require changes to your
application's source code.
@@ -26,7 +27,7 @@ below should still apply, but you may want to consult the
<li>{@link guide/migration#ngroute-has-been-moved-into-its-own-module ngRoute has been moved into its own module}</li>
<li>{@link guide/migration#templates-no-longer-automatically-unwrap-promises Templates no longer automatically unwrap promises}</li>
<li>{@link guide/migration#syntax-for-named-wildcard-parameters-changed-in Syntax for named wildcard parameters changed in <code>$route</code>}</li>
<li>{@link guide/migration#you-can-only-bind-one-expression-to You can only bind one expression to <code>*[src]</code> or <code>*[ng-src]</code>}</li>
<li>{@link guide/migration#you-can-only-bind-one-expression-to You can only bind one expression to <code>*[src]</code>, <code>*[ng-src]</code> or <code>action</code>}</li>
<li>{@link guide/migration#interpolations-inside-dom-event-handlers-are-now-disallowed Interpolations inside DOM event handlers are now disallowed}</li>
<li>{@link guide/migration#directives-cannot-end-with--start-or--end Directives cannot end with -start or -end}</li>
<li>{@link guide/migration#in-$q,-promisealways-has-been-renamed-promisefinally In $q, promise.always has been renamed promise.finally}</li>
@@ -48,6 +49,8 @@ below should still apply, but you may want to consult the
<li>{@link guide/migration#underscore-prefixed/suffixed-properties-are-non-bindable Underscore-prefixed/suffixed properties are non-bindable}</li>
<li>{@link guide/migration#you-cannot-bind-to-select[multiple] You cannot bind to select[multiple]}</li>
<li>{@link guide/migration#uncommon-region-specific-local-files-were-removed-from-i18n Uncommon region-specific local files were removed from i18n}</li>
<li>{@link guide/migration#services-can-now-return-functions Services can now return functions}</li>
<li>{@link guide/migration#modifying-the-dom-outside-digest-cycle Modifying the DOM outside digest cycle}</li>
</ul>
@@ -137,17 +140,18 @@ $routeProvider.when('/Book1/:book/Chapter/:chapter/:highlight*/edit',
See [04cebcc1](https://github.com/angular/angular.js/commit/04cebcc133c8b433a3ac5f72ed19f3631778142b).
## You can only bind one expression to `*[src]` or `*[ng-src]`
## You can only bind one expression to `*[src]`, `*[ng-src]` or `action`
With the exception of `<a>` and `<img>` elements, you cannot bind more than one expression to the
`src` attribute of elements.
`src` or `action` attribute of elements.
This is one of several improvements to security introduces by Angular 1.2.
Concatenating expressions makes it hard to understand whether some combination of concatenated
values are unsafe to use and potentially subject to XSS vulnerabilities. To simplify the task of
auditing for XSS issues, we now require that a single expression be used for `*[src/ng-src]`
bindings such as bindings for `iframe[src]`, `object[src]`, etc.
bindings such as bindings for `iframe[src]`, `object[src]`, etc. In addition, this requirement is
enforced for `form` tags with `action` attributes.
<table class="table table-bordered code-table">
<thead>
@@ -540,9 +544,45 @@ See [1adf29af](https://github.com/angular/angular.js/commit/1adf29af13890d612868
## Isolate scope only exposed to directives with `scope` property
Directives without isolate scope do not get the isolate scope from an isolate directive on the
same element. If your code depends on this behavior (non-isolate directive needs to access state
from within the isolate scope), change the isolate directive to use scope locals to pass these explicitly.
If you declare a scope option on a directive, that directive will have an
[isolate scope](https://github.com/angular/angular.js/wiki/Understanding-Scopes). In Angular 1.0, if a
directive with an isolate scope is used on an element, all directives on that same element have access
to the same isolate scope. For example, say we have the following directives:
```
// This directive declares an isolate scope.
.directive('isolateScope', function() {
return {
scope: {},
link: function($scope) {
console.log('one = ' + $scope.$id);
}
};
})
// This directive does not.
.directive('nonIsolateScope', function() {
return {
link: function($scope) {
console.log('two = ' + $scope.$id);
}
};
});
```
Now what happens if we use both directives on the same element?
```
<div isolate-scope non-isolate-scope></div>
```
In Angular 1.0, the nonIsolateScope directive will have access to the isolateScope directives scope. The
log statements will print the same id, because the scope is the same. But in Angular 1.2, the nonIsolateScope
will not use the same scope as isolateScope. Instead, it will inherit the parent scope. The log statements
will print different ids.
If your code depends on the Angular 1.0 behavior (non-isolate directive needs to access state
from within the isolate scope), change the isolate directive to use scope locals to pass these explicitly:
**Before**
@@ -653,3 +693,39 @@ load and use your copy of the locale file provided that you maintain it yourself
See [6382e21f](https://github.com/angular/angular.js/commit/6382e21fb28541a2484ac1a241d41cf9fbbe9d2c).
## Services can now return functions
Previously, the service constructor only returned objects regardless of whether a function was returned.
Now, `$injector.instantiate` (and thus `$provide.service`) behaves the same as the native
`new` operator and allows functions to be returned as a service.
If using a JavaScript preprocessor it's quite possible when upgrading that services could start behaving incorrectly.
Make sure your services return the correct type wanted.
**Coffeescript example**
```
myApp.service 'applicationSrvc', ->
@something = "value"
@someFunct = ->
"something else"
```
pre 1.2 this service would return the whole object as the service.
post 1.2 this service returns `someFunct` as the value of the service
you would need to change this services to
```
myApp.service 'applicationSrvc', ->
@something = "value"
@someFunct = ->
"something else"
return
```
to continue to return the complete instance.
See [c22adbf1](https://github.com/angular/angular.js/commit/c22adbf160f32c1839fbb35382b7a8c6bcec2927).
+4 -3
View File
@@ -66,8 +66,9 @@ that you break your application to multiple modules like this:
* And an application level module which depends on the above modules and contains any
initialization code.
We've also written a document on how we organize large apps at Google and on how to write
reusable components.
We've also
[written a document](http://blog.angularjs.org/2014/02/an-angularjs-style-guide-and-best.html)
on how we organize large apps at Google.
The above is a suggestion. Tailor it to your needs.
@@ -172,7 +173,7 @@ angular.module('myModule', []).
<div class="alert alert-info">
When bootstrapping, first Angular applies all constant definitions.
Then Angular applies configuration blocks in the order same order they were registered.
Then Angular applies configuration blocks in the same order they were registered.
</div>
## Run Blocks
+9 -9
View File
@@ -83,7 +83,7 @@ On to more complex examples!
## Factory Recipe
The Value recipe is very simple to write, but lacks some important features we often need when
creating services. Let's now look at the Value recipe's more powerful sibling, the Factory.The
creating services. Let's now look at the Value recipe's more powerful sibling, the Factory. The
Factory recipe adds the following abilities:
* ability to use other services (have dependencies)
@@ -97,7 +97,7 @@ created by this recipe.
Note: All services in Angular are singletons. That means that the injector uses each recipe at most
once to create the object. The injector then caches the reference for all future needs.
Since Factory is more powerful version of Value recipe, you can construct the same service with it.
Since Factory is more a powerful version of the Value recipe, you can construct the same service with it.
Using our previous `clientId` Value recipe example, we can rewrite it as a Factory recipe like
this:
@@ -111,8 +111,8 @@ But given that the token is just a string literal, sticking with the Value recip
appropriate as it makes the code easier to follow.
Let's say, however, that we would also like to create a service that computes a token used for
authentication against a remote API. This token will be called 'apiToken' and will be computed
based on the `clientId` value and a secret stored in browser's local storage:
authentication against a remote API. This token will be called `apiToken` and will be computed
based on the `clientId` value and a secret stored in the browser's local storage:
```javascript
myApp.factory('apiToken', ['clientId', function apiTokenFactory(clientId) {
@@ -129,11 +129,11 @@ myApp.factory('apiToken', ['clientId', function apiTokenFactory(clientId) {
```
In the code above, we see how the `apiToken` service is defined via the Factory recipe that depends
on `clientId` service. The factory service then uses NSA-proof encryption to produce an authentication
on the `clientId` service. The factory service then uses NSA-proof encryption to produce an authentication
token.
Note: It is a best practice to name the factory functions as "<serviceId>Factory"
(e.g. apiTokenFactory). While this names are not required, they help when navigating the code base
Note: It is best practice to name the factory functions as `<serviceId>Factory`
(e.g. apiTokenFactory). While this naming convention is not required, it helps when navigating the code base
or looking at stack traces in the debugger.
Just like with Value recipe, Factory recipe can create a service of any type, whether it be a
@@ -143,7 +143,7 @@ primitive, object literal, function, or even an instance of a custom type.
## Service Recipe
JavaScript developers often use custom types to write object-oriented code. Let's explore how we
could launch a unicorn into space via our `unicornLauncher` service that is an instance of
could launch a unicorn into space via our `unicornLauncher` service which is an instance of a
custom type:
```javascript
@@ -187,7 +187,7 @@ myApp.service('unicornLauncher', ["apiToken", UnicornLauncher]);
Much simpler!
Note: Yes, we have called one of our service recipes 'Service'. We regret this and know that we'll
be somehow punished for our mis-deed. It's like we named one of our offspring 'Children'. Boy,
be somehow punished for our mis-deed. It's like we named one of our offspring 'Child'. Boy,
that would mess with the teachers.
+9 -9
View File
@@ -268,7 +268,7 @@ the `$digest` phase. This delay is desirable, since it coalesces multiple model
4. **Mutation observation**
At the end `$apply`, Angular performs a {@link ng.$rootScope.Scope#$digest
At the end of `$apply`, Angular performs a {@link ng.$rootScope.Scope#$digest
$digest} cycle on the root scope, which then propagates throughout all child scopes. During
the `$digest` cycle, all `$watch`ed expressions or functions are checked for model mutation
and if a mutation is detected, the `$watch` listener is called.
@@ -350,15 +350,15 @@ The diagram and the example below describe how Angular interacts with the browse
Angular modifies the normal JavaScript flow by providing its own event processing loop. This
splits the JavaScript into classical and Angular execution context. Only operations which are
applied in Angular execution context will benefit from Angular data-binding, exception handling,
property watching, etc... You can also use $apply() to enter Angular execution context from JavaScript. Keep in
applied in the Angular execution context will benefit from Angular data-binding, exception handling,
property watching, etc... You can also use $apply() to enter the Angular execution context from JavaScript. Keep in
mind that in most places (controllers, services) $apply has already been called for you by the
directive which is handling the event. An explicit call to $apply is needed only when
implementing custom event callbacks, or when working with third-party library callbacks.
1. Enter Angular execution context by calling {@link guide/scope scope}`.`{@link
ng.$rootScope.Scope#$apply $apply}`(stimulusFn)`. Where `stimulusFn` is
the work you wish to do in Angular execution context.
1. Enter the Angular execution context by calling {@link guide/scope scope}`.`{@link
ng.$rootScope.Scope#$apply $apply}`(stimulusFn)`, where `stimulusFn` is
the work you wish to do in the Angular execution context.
2. Angular executes the `stimulusFn()`, which typically modifies application state.
3. Angular enters the {@link ng.$rootScope.Scope#$digest $digest} loop. The
loop is made up of two smaller loops which process {@link
@@ -388,7 +388,7 @@ user enters text into the text field.
1. the {@link ng.directive:ngModel ng-model} and {@link
ng.directive:input input} {@link guide/directive
directive} set up a `keydown` listener on the `<input>` control.
2. the {@link ng.$interpolate &#123;&#123;name&#125;&#125; } interpolation
2. the {@link ng.$interpolate interpolation}
sets up a {@link ng.$rootScope.Scope#$watch $watch} to be notified of
`name` changes.
2. During the runtime phase:
@@ -400,8 +400,8 @@ user enters text into the text field.
3. Angular applies the `name = 'X';` to the model.
4. The {@link ng.$rootScope.Scope#$digest $digest} loop begins
5. The {@link ng.$rootScope.Scope#$watch $watch} list detects a change
on the `name` property and notifies the {@link ng.$interpolate
&#123;&#123;name&#125;&#125; } interpolation, which in turn updates the DOM.
on the `name` property and notifies the {@link ng.$interpolate interpolation},
which in turn updates the DOM.
6. Angular exits the execution context, which in turn exits the `keydown` event and with it
the JavaScript execution context.
7. The browser re-renders the view with update text.
+9 -3
View File
@@ -130,8 +130,14 @@ injection of `$window`, `$scope`, and our `notify` service:
</example>
<div class="alert alert-danger">
**Careful:** If you plan to [minify](http://en.wikipedia.org/wiki/Minification_(programming) your code,
your variable names will get renamed unless you use one of the annotation techniques above.
**Careful:** If you plan to [minify](http://en.wikipedia.org/wiki/Minification_(programming&#41;) your
code, your variable names will get renamed unless you use one of the annotation techniques above.
</div>
<div class="alert alert-info">
If you use a tool like [ngmin](https://github.com/btford/ngmin#ngmin) in your workflow you can
use implicit dependency notation within your codebase and let **ngmin** automatically convert such
injectable functions to the array notation prior to minifying.
</div>
@@ -293,5 +299,5 @@ it('should clear messages after alert', function() {
## Related API
* {@link ./ng Angular Service API}
* {@link ./api/ng/service Angular Service API}
* {@link angular.injector Injector API}
+10 -4
View File
@@ -65,7 +65,7 @@ function MyClass() {
A problem surfaces in tests when we would like to instantiate a `MockXHR` that would
allow us to return fake data and simulate network failures. By calling `new XHR()` we are
permanently bound to the actual XHR and there is no way to replace it. Yes, we could monkey
permanently bound to the actual XHR and there is no way to replace it. Yes, we could monkey
patch, but that is a bad idea for many reasons which are outside the scope of this document.
Here's an example of how the class above becomes hard to test when resorting to monkey patching:
@@ -96,7 +96,7 @@ function MyClass() {
```
While no new dependency instance is created, it is fundamentally the same as `new` in
that no way exists to intercept the call to `global.xhr` for testing purposes, other then
that no way exists to intercept the call to `global.xhr` for testing purposes, other than
through monkey patching. The basic issue for testing is that a global variable needs to be mutated in
order to replace it with call to a mock method. For further explanation of why this is bad see: [Brittle Global
State & Singletons](http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/)
@@ -133,7 +133,7 @@ function MyClass() {
However, where does the serviceRegistry come from? If it is:
* `new`-ed up, the test has no chance to reset the services for testing.
* a global look-up then the service returned is global as well (but resetting is easier, since
* a global look-up then the service returned is global as well (but resetting is easier, since
only one global variable exists to be reset).
The class above is hard to test since we have to change the global state:
@@ -296,7 +296,7 @@ Now we can add a directive to our app.
app.directive('aGreatEye', function () {
return {
restrict: 'E',
replace: true,
replace: true,
template: '<h1>lidless, wreathed in flame, {{1 + 1}} times</h1>'
};
});
@@ -337,6 +337,12 @@ We inject the $compile service and $rootScope before each jasmine test. The $com
to render the aGreatEye directive. After rendering the directive we ensure that the directive has
replaced the content and "lidless, wreathed in flame, 2 times" is present.
### Testing Directives With External Templates
If your directive uses `templateUrl`, consider using
[karma-ng-html2js-preprocessor](https://github.com/karma-runner/karma-ng-html2js-preprocessor)
to pre-compile HTML templates and thus avoid having to load them over HTTP during test execution.
Otherwise you may run into issues if the test directory hierarchy differs from the application's.
## Sample project
See the [angular-seed](https://github.com/angular/angular-seed) project for an example.
+6 -6
View File
@@ -11,12 +11,12 @@ See the [contributing guidelines](https://github.com/angular/angular.js/blob/mas
for how to contribute your own code to AngularJS.
1. {@link #building-and-testing-angularjs_installing-dependencies Installing Dependencies}
2. {@link #building-and-testing-angularjs_forking-angular-on-github Forking Angular on Github}
3. {@link #building-and-testing-angularjs_building-angularjs Building AngularJS}
4. {@link #building-and-testing-angularjs_running-a-local-development-web-server Running a Local Development Web Server}
5. {@link #building-and-testing-angularjs_running-the-unit-test-suite Running the Unit Test Suite}
6. {@link #building-and-testing-angularjs_running-the-end-to-end-test-suite Running the End-to-end Test Suite}
1. {@link misc/contribute#installing-dependencies Installing Dependencies}
2. {@link misc/contribute#forking-angular-on-github Forking Angular on Github}
3. {@link misc/contribute#building-angularjs Building AngularJS}
4. {@link misc/contribute#running-a-local-development-web-server Running a Local Development Web Server}
5. {@link misc/contribute#running-the-unit-test-suite Running the Unit Test Suite}
6. {@link misc/contribute#running-the-end-to-end-test-suite Running the End-to-end Test Suite}
## Installing Dependencies
+2 -2
View File
@@ -17,7 +17,7 @@ production.
To point your code to an angular script on the Google CDN server, use the following template. This
example points to the minified version 1.2.0:
<pre>
```
<!doctype html>
<html ng-app>
<head>
@@ -27,7 +27,7 @@ example points to the minified version 1.2.0:
<body>
</body>
</html>
</pre>
```
Note that only versions 1.0.1 and above are available on the CDN, if you need an earlier version
you can use the <http://code.angularjs.org/> URL which was the previous recommended location for
+2 -2
View File
@@ -72,7 +72,7 @@ The size of the file is < 36KB compressed and minified.
### Can I use the open-source Closure Library with Angular?
Yes, you can use widgets from the [Closure Library](http://code.google.com/closure/library)
Yes, you can use widgets from the [Closure Library](https://developers.google.com/closure/library/)
in Angular.
### Does Angular use the jQuery library?
@@ -82,7 +82,7 @@ application is being bootstrapped. If jQuery is not present in your script path,
to its own implementation of the subset of jQuery that we call {@link angular.element jQLite}.
Due to a change to use `on()`/`off()` rather than `bind()`/`unbind()`, Angular 1.2 only operates with
jQuery 1.7.1 or above.
jQuery 1.7.1 or above. However, Angular does not currently support jQuery 2.x or above.
### What is testability like in Angular?
+7
View File
@@ -1,3 +1,10 @@
@ngdoc overview
@name Miscellaneous
@description
# Miscellaneous Links
- {@link misc/started Getting Started}
- {@link misc/downloading Downloading AngularJS}
- {@link misc/faq Frequently Asked Questions}
- {@link misc/contribute Building AngularJS}
+2
View File
@@ -2,6 +2,8 @@
@name Getting Started
@description
# Getting Started
We want you to have an easy time while starting to use Angular. We've put together the following steps on your path to
becoming an Angular expert.
+238 -92
View File
@@ -3,6 +3,7 @@
@step -1
@description
# PhoneCat Tutorial App
A great way to get introduced to AngularJS is to work through this tutorial, which walks you through
the construction of an AngularJS web app. The app you will build is a catalog that displays a list
@@ -12,25 +13,25 @@ details for any device.
<img class="diagram" src="img/tutorial/catalog_screen.png" width="488" height="413" alt="demo
application running in the browser">
Work through the tutorial to see how Angular makes browsers smarter — without the use of extensions
or plug-ins. As you work through the tutorial, you will:
Follow the tutorial to see how Angular makes browsers smarter — without the use of native
extensions or plug-ins:
* See examples of how to use client-side data binding and dependency injection to build dynamic
views of data that change immediately in response to user actions.
* See how Angular creates listeners on your data without the need for DOM manipulation.
* Learn a better, easier way to test your web apps.
* Learn how to use Angular services to make common web tasks, such as getting data into your app,
easier.
And all of this works in any browser without modification to the browser!
* See examples of how to use client-side data binding to build dynamic views of data that change
immediately in response to user actions.
* See how Angular keeps your views in sync with your data without the need for DOM manipulation.
* Learn a better, easier way to test your web apps, with Karma and Protractor.
* Learn how to use dependency injection and services to make common web tasks, such as getting data
into your app, easier.
When you finish the tutorial you will be able to:
* Create a dynamic application that works in any browser.
* Define the differences between Angular and common JavaScript frameworks.
* Understand how data binding works in AngularJS.
* Use the angular-seed project to quickly boot-strap your own projects.
* Create and run tests.
* Create a dynamic application that works in all modern browsers.
* Use data binding to wire up your data model to your views.
* Create and run unit tests, with Karma.
* Create and run end to end tests, with Protractor.
* Move application logic out of the template and into Controllers.
* Get data from a server using Angular services.
* Apply animations to your application, using ngAnimate.
* Identify resources for learning more about AngularJS.
The tutorial guides you through the entire process of building a simple application, including
@@ -41,88 +42,233 @@ You can go through the whole tutorial in a couple of hours or you may want to sp
really digging into it. If you're looking for a shorter introduction to AngularJS, check out the
{@link misc/started Getting Started} document.
# Get Started
The rest of this page explains how you can set up your machine to work with the code on your local
machine. If you just want to read the tutorial then you can just go straight to the first step:
[Step 0 - Bootstrapping](tutorial/step_00).
# Working with the code
You can follow this tutorial and hack on the code in either the Mac/Linux or the Windows
environment. The tutorial relies on the use of Git versioning system for source code management.
You don't need to know anything about Git to follow the tutorial. Select one of the tabs below
and follow the instructions for setting up your computer.
You can follow along with this tutorial and hack on the code in the comfort of your own computer.
In this way you can get hands-on practice of really writing AngularJS code and also on using the
recommended testing tools.
<div class="tabbable" show="true">
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux">
<ol>
<li><p>You'll need Git, which you can get from
<a href="http://git-scm.com/download" title="Git site download">the Git site</a>.</p></li>
<li><p>Clone the angular-phonecat repository located at
<a href="https://github.com/angular/angular-phonecat" title="Github Phonecat Repo">Github</a> by
running the following command:</p>
<pre>git clone https://github.com/angular/angular-phonecat.git</pre>
<p>This command creates the <code>angular-phonecat</code> directory in your current directory.</p></li>
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
<pre>cd angular-phonecat</pre>
<p>The tutorial instructions, from now on, assume you are running all commands from the
<code>angular-phonecat</code> directory.</p></li>
<li><p>You will also need Node.js and Karma to run unit tests, so please verify that you have
<a href="http://nodejs.org/" title="NodeJS org">Node.js</a> v0.10 or better installed
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
command in a terminal window:</p></li>
<pre>node --version</pre>
<div class="alert alert-info">**Helpful note:** If you need to run a different version of
node.js in your local environment, consider installing
<a href="https://github.com/creationix/nvm" title="Node Version Manager Github Repo link">
Node Version Manager (nvm)</a>.</div>
<p>Additionally install <a href="http://karma-runner.github.io/" title="Karma site">Karma</a> and
its plugins if you don't have it already:</p>
<pre>
npm install
</pre></li>
<li><p>You will need an http server running on your system. Mac and Linux machines typically
have Apache pre-installed, but If you don't already have one installed, you can use <code>node</code>
to run <code>scripts/web-server.js</code>, a simple bundled http server.</p></li>
</ol>
</div>
The tutorial relies on the use of the [Git][git] versioning system for source code management.
You don't need to know anything about Git to follow the tutorial other than how to install and run
a few git commands.
<div class="tab-pane well" id="git-win" title="Git on Windows">
<ol>
<li><p>You will need Node.js and Karma to run unit tests, so please verify that you have
<a href="http://nodejs.org/" title="NodeJS site">Node.js</a> v0.10 or better installed
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
command in a terminal window:</p>
<pre>node --version</pre>
<div class="alert alert-info">**Helpful note:** If you need to run a different version of
node.js in your local environment, consider installing
<a href="https://github.com/creationix/nvm" title="Node Version Manager Github Repo link">
Node Version Manager (nvm)</a>.</div>
<p>Additionally install <a href="http://karma-runner.github.io/" title="Karma site">Karma</a>
if you don't have it already:</p>
<pre>npm install -g karma</pre>
</li>
<li><p>You'll also need Git, which you can get from
<a href="http://git-scm.com/download" title="Git site download">the Git site</a>.</p></li>
<li><p>Clone the angular-phonecat repository located at <a
href="https://github.com/angular/angular-phonecat" "Github Angular-phonecat Repo">Github</a> by running
the following command:</p><pre>git clone https://github.com/angular/angular-phonecat.git</pre>
<p>This command creates the <code>angular-phonecat</code> directory in your current directory.</p></li>
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
<pre>cd angular-phonecat</pre>
<p>The tutorial instructions assume you are running all commands from the <code>angular-phonecat</code>
directory.</p>
<p>You should run all <code>git</code> commands from Git bash.</p>
<p>Other commands like <code>test.bat</code> or <code>e2e-test.bat</code> should be executed from the
Windows command line.</li>
<li><p>You need an http server running on your system, but if you don't already have one already
installed, you can use <code>node</code> to run <code>scripts\web-server.js</code>, a simple bundled
http server.</p></li>
</ol>
</div>
The last thing to do is to make sure your computer has a web browser and a good text editor installed. Now,
let's get some cool stuff done!
### Install Git
<a href="tutorial/step_00" title="Next Step"><span class="btn btn-primary">Get Started!</span></a>
You can download and install Git from http://git-scm.com/download. Once installed you should have
access to the `git` command line tool. The main commands that you will need to use are:
- `git clone ...` : clone a remote repository onto your local machine
- `git checkout ...` : check out a particular branch or a tagged version of the code to hack on
### Download angular-phonecat
Clone the [angular-phonecat repository][angular-phonecat] located at GitHub by running the following
command:
```
git clone --depth=14 https://github.com/angular/angular-phonecat.git
```
This command creates the `angular-phonecat` directory in your current directory.
<div class="alert alert-info">The `--depth=14` option just tells Git to pull down only the last 14 commits. This makes the
download much smaller and faster.
</div>
Change your current directory to `angular-phonecat`.
```
cd angular-phonecat
```
The tutorial instructions, from now on, assume you are running all commands from the
`angular-phonecat` directory.
### Install Node.js
If you want to run the preconfigured local web-server and the test tools then you will also need
[Node.js v0.10.27+][node].
You can download a Node.js installer for your operating system from http://nodejs.org/download/.
Check the version of Node.js that you have installed by running the following command:
```
node --version
```
In Debian based distributions, there is a name clash with another utility called `node`. The
suggested solution is to also install the `nodejs-legacy` apt package, which renames `node` to
`nodejs`.
```
apt-get install nodejs-legacy
nodejs --version
```
<div class="alert alert-info">If you need to run a different versions of node.js
in your local environment, consider installing
<a href="https://github.com/creationix/nvm" title="Node Version Manager Github Repo link">
Node Version Manager (nvm)
</a>.
</div>
Once you have Node.js installed on your machine you can download the tool dependencies by running:
```
npm install
```
This command will download the following tools, into the `node_modules` directory:
- [Bower][bower] - client-side code package manager
- [Http-Server][http-server] - simple local static web server
- [Karma][karma] - unit test runner
- [Protractor][protractor] - end to end (E2E) test runner
Running `npm install` will also automatically use bower to download the Angular framework into the
`app/bower_components` directory.
<div class="alert alert-info">
Note the angular-phonecat project is setup to install and run these utilities via npm scripts.
This means that you do not have to have any of these utilities installed globally on your system
to follow the tutorial. See **Installing Helper Tools** below for more information.
</div>
The project is preconfigured with a number of npm helper scripts to make it easy to run the common
tasks that you will need while developing:
- `npm start` : start a local development web-server
- `npm test` : start the Karma unit test runner
- `npm run protractor` : run the Protractor end to end (E2E) tests
- `npm run update-webdriver` : install the drivers needed by Protractor
### Install Helper Tools (optional)
The Bower, Http-Server, Karma and Protractor modules are also executables, which can be installed
globally and run directly from a terminal/command prompt. You don't need to do this to follow the
tutorial, but if you decide you do want to run them directly, you can install these modules globally
using, `sudo npm install -g ...`.
For instance to install the Bower command line executable you would do:
```
sudo npm install -g bower
```
*(Omit the sudo if running on Windows)*
Then you can run the bower tool directly, such as:
```
bower install
```
### Running Development Web Server
While Angular applications are purely client-side code, and it is possible to open them in a web
browser directly from the file system, it is better to serve them from a HTTP web server. In
particular, for security reasons, most modern browsers will not allow JavaScript to make server
requests if the page is loaded directly from the file system.
The angular-phonecat project is configured with a simple static web server for hosting the
application during development. Start the web server by running:
```
npm start
```
This will create a local webserver that is listening to port 8000 on your local machine.
You can now browse to the application at:
```
http://localhost:8000/app/index.html
```
### Running Unit Tests
We use unit tests to ensure that the JavaScript code in our application is operating correctly.
Unit tests focus on testing small isolated parts of the application. The unit tests are kept in the
`test/unit` directory.
The angular-phonecat project is configured to use [Karma][karma] to run the unit tests for the
application. Start Karma by running:
```
npm test
```
This will start the Karma unit test runner. Karma will read the configuration file at
`test/karma.conf.js`. This configuration file tells Karma to:
- open up a Chrome browser and connect it to Karma
- execute all the unit tests in this browser
- report the results of these tests in the terminal/command line window
- watch all the project's JavaScript files and re-run the tests whenever any of these change
It is good to leave this running all the time, in the background, as it will give you immediate
feedback about whether your changes pass the unit tests while you are working on the code.
### Running End to End Tests
We use End to End tests to ensure that the application as a whole operates as expected.
End to End tests are designed to test the whole client side application, in particular that the
views are displaying and behaving correctly. It does this by simulating real user interaction with
the real application running in the browser.
The End to End tests are kept in the `test/e2e` directory.
The angular-phonecat project is configured to use [Protractor][protractor] to run the End to End
tests for the application. Protractor relies upon a set of drivers to allow it to interact with
the browser. You can install these drivers by running:
```
npm run update-webdriver
```
*(You should only need to do this once.)*
Since Protractor works by interacting with a running application, we need to start our web server:
```
npm start
```
Then in a separate terminal/command line window, we can run the Protractor test scripts against the
application by running:
```
npm run protractor
```
Protractor will read the configuration file at `test/protractor-conf.js`. This configuration tells
Protractor to:
- open up a Chrome browser and connect it to the application
- execute all the End to End tests in this browser
- report the results of these tests in the terminal/command line window
- close down the browser and exit
It is good to run the end to end tests whenever you make changes to the HTML views or want to check
that the application as a whole is executing correctly. It is very common to run End to End tests
before pushing a new commit of changes to a remote repository.
[git]: http://git-scm.com/
[node]: http://nodejs.org/
[angular-phonecat]: https://github.com/angular/angular-phonecat
[protractor]: https://github.com/angular/protractor
[bower]: http://bower.io/
[http-server]: https://github.com/nodeapps/http-server
[karma]: https://github.com/karma-runner/karma
+29 -69
View File
@@ -11,65 +11,27 @@ with the most important source code files, learn how to start the development se
angular-seed, and run the application in the browser.
<div class="tabbable" show="true" ng-model="$cookies.platformPreference">
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux" value="gitUnix">
<ol>
<li><p>In <code>angular-phonecat</code> directory, run this command:</p>
<pre>git checkout -f step-0</pre>
<p>This resets your workspace to step 0 of the tutorial app.</p>
<p>You must repeat this for every future step in the tutorial and change the number to
the number of the step you are on. This will cause any changes you made within
your working directory to be lost.</p></li>
In `angular-phonecat` directory, run this command:
<li>To see the app running in a browser, do one of the following:
<ul>
<li><b>For node.js users:</b>
<ol>
<li>In a <i>separate</i> terminal tab or window, run <code>node ./scripts/web-server.js</code> to start the web server.</li>
<li>Open a browser window for the app and navigate to <a
href="http://localhost:8000/app/index.html" target="_blank">`http://localhost:8000/app/index.html`</a></li>
</ol>
</li>
<li><b>For other http servers:</b>
<ol>
<li>Configure the server to serve the files in the <code>angular-phonecat</code> directory.</li>
<li>Navigate in your browser to <code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
</ol>
</li>
</ul>
</li>
</ol>
</div>
```
git checkout -f step-0
```
<div class="tab-pane well" id="git-win" title="Git on Windows" value="gitWin">
<ol>
<li><p>Open Git bash and run this command (in <code>angular-phonecat</code> directory):</p>
<pre>git checkout -f step-0</pre>
<p>This resets your workspace to step 0 of the tutorial app.</p>
<p>You must repeat this for every future step in the tutorial and change the number to
the number of the step you are on. This will cause any changes you made within
your working directory to be lost.</p></li>
<li>To see the app running in a browser, do one of the following:
<ul>
<li><b>For node.js users:</b>
<ol>
<li>In a <i>separate</i> terminal tab or window, run <code>node scripts\web-server.js</code> to start the web server.</li>
<li>Open a browser window for the app and navigate to <a href="http://localhost:8000/app/index.html" target="_blank">`http://localhost:8000/app/index.html`</a></li>
</ol>
</li>
<li><b>For other http servers:</b>
<ol>
<li>Configure the server to serve the files in the <code>angular-phonecat</code> directory.</li>
<li>Navigate in your browser to <code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
</ol>
</li>
</ul>
</li>
</ol>
</div>
</div>
This resets your workspace to step 0 of the tutorial app.
You must repeat this for every future step in the tutorial and change the number to the number of
the step you are on. This will cause any changes you made within your working directory to be lost.
If you haven't already done so you need to install the dependencies by running:
```
npm install
```
To see the app running in a browser, open a *separate* terminal/command line tab or window, then
run `npm start` to start the web server. Now, open a browser window for the app and navigate to
<a href="http://localhost:8000/app/index.html" target="_blank">`http://localhost:8000/app/index.html`</a>
You can now see the page in your browser. It's not very exciting, but that's OK.
@@ -84,9 +46,9 @@ __`app/index.html`:__
<head>
<meta charset="utf-8">
<title>My HTML File</title>
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">
<link rel="stylesheet" href="css/app.css">
<link rel="stylesheet" href="css/bootstrap.css">
<script src="lib/angular/angular.js"></script>
<script src="bower_components/angular/angular.js"></script>
</head>
<body>
@@ -105,7 +67,7 @@ __`app/index.html`:__
<html ng-app>
The `ng-app` attribute represents an Angular directive named `ngApp` (Angular uses
`name-with-dashes` for its custom attributes and `camelCase` for the corresponding directives
`spinal-case` for its custom attributes and `camelCase` for the corresponding directives
which implement them).
This directive is used to flag the html element that Angular should consider to be the root element
of our application.
@@ -114,7 +76,7 @@ __`app/index.html`:__
* AngularJS script tag:
<script src="lib/angular/angular.js">
<script src="bower_components/angular/angular.js">
This code downloads the `angular.js` script and registers a callback that will be executed by the
browser when the containing HTML page is fully downloaded. When the callback is executed, Angular
@@ -171,16 +133,18 @@ and one static binding, and our model is empty. That will soon change!
## What are all these files in my working directory?
Most of the files in your working directory come from the [angular-seed project](https://github.com/angular/angular-seed) which is typically used to bootstrap
new Angular projects. The seed project includes the latest Angular libraries, test libraries,
scripts and a simple example app, all pre-configured for developing a typical web app.
Most of the files in your working directory come from the [angular-seed project][angular-seed] which
is typically used to bootstrap new Angular projects. The seed project is pre-configured to install
the angular framework (via `bower` into the `app/bower_components/` folder) and tools for developing
a typical web app (via `npm`).
For the purposes of this tutorial, we modified the angular-seed with the following changes:
* Removed the example app
* Added phone images to `app/img/phones/`
* Added phone data files (JSON) to `app/phones/`
* Added [Bootstrap](http://getbootstrap.com) files to `app/css/` and `app/img/`
* Added a dependency on [Bootstrap](http://getbootstrap.com) in the `bower.json` file.
@@ -199,9 +163,5 @@ Now let's go to {@link step_01 step 1} and add some content to the web app.
<ul doc-tutorial-nav="0"></ul>
<div style="display: none">
Note: During the bootstrap the injector and the root scope will then be associated with the
element on which the `ngApp` directive was declared, so when debugging the app you can retrieve
them from browser console via `angular.element(rootElement).scope()` and
`angular.element(rootElement).injector()`.
</div>
[angular-seed]: https://github.com/angular/angular-seed
+2 -5
View File
@@ -12,15 +12,12 @@ dynamically display the same result with any set of data.
In this step you will add some basic information about two cell phones to an HTML page.
- The page now contains a list with information about two phones.
<div doc-tutorial-reset="1"></div>
The page now contains a list with information about two phones.
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-0...step-1):
__`app/index.html`:__
**`app/index.html`:**
```html
<ul>
+72 -67
View File
@@ -9,20 +9,16 @@
Now it's time to make the web page dynamic — with AngularJS. We'll also add a test that verifies the
code for the controller we are going to add.
There are many ways to structure the code for an application. For Angular apps, we encourage the
use of [the Model-View-Controller (MVC)
design pattern](http://en.wikipedia.org/wiki/ModelViewController) to decouple the code and to separate concerns. With that in mind, let's use a
little Angular and JavaScript to add model, view, and controller components to our app.
There are many ways to structure the code for an application. For Angular apps, we encourage the use of
[the Model-View-Controller (MVC) design pattern](http://en.wikipedia.org/wiki/ModelViewController)
to decouple the code and to separate concerns. With that in mind, let's use a little Angular and
JavaScript to add model, view, and controller components to our app.
- The list of three phones is now generated dynamically from data
<div doc-tutorial-reset="2"></div>
The app now contains a list with three phones.
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-1...step-2):
## View and Template
In Angular, the __view__ is a projection of the model through the HTML __template__. This means that
@@ -37,7 +33,7 @@ __`app/index.html`:__
<html ng-app="phonecatApp">
<head>
...
<script src="lib/angular/angular.js"></script>
<script src="bower_components/angular/angular.js"></script>
<script src="js/controllers.js"></script>
</head>
<body ng-controller="PhoneListCtrl">
@@ -53,21 +49,21 @@ __`app/index.html`:__
</html>
```
We replaced the hard-coded phone list with the
{@link ng.directive:ngRepeat ngRepeat directive} and two
{@link guide/expression Angular expressions} enclosed in curly braces:
`{{phone.name}}` and `{{phone.snippet}}`:
We replaced the hard-coded phone list with the {@link ng.directive:ngRepeat ngRepeat directive}
and two {@link guide/expression Angular expressions}:
* The `ng-repeat="phone in phones"` statement in the `<li>` tag is an Angular repeater. The
repeater tells Angular to create a `<li>` element for each phone in the list using the first `<li>`
* The `ng-repeat="phone in phones"` attribute in the `<li>` tag is an Angular repeater directive.
The repeater tells Angular to create a `<li>` element for each phone in the list using the `<li>`
tag as the template.
* The expressions wrapped in curly braces (`{{phone.name}}` and `{{phone.snippet}}`) will be replaced
by the value of the expressions.
We have added a new directive, called `ng-controller`, which attaches a `PhoneListCtrl`
__controller__ to the DOM at this point.
__controller__ to the DOM at this point:
* As we've learned in {@link step_00 step 0}, the curly braces around `phone.name` and `phone.snippet` denote
bindings. As opposed to evaluating constants, these expressions are referring to our application
model, which was set up in our `PhoneListCtrl` controller.
* The expressions in curly braces (`{{phone.name}}` and `{{phone.snippet}}` denote
bindings, which are referring to our application model, which is set up in our `PhoneListCtrl`
controller.
<img class="diagram" src="img/tutorial/tutorial_02.png">
@@ -128,23 +124,19 @@ To learn more about Angular scopes, see the {@link ng.$rootScope.Scope angular s
## Tests
The "Angular way" of separating controller from the view, makes it easy to test code as it is being
developed. If our controller is available on the global namespace then we can simply instantiate it
with a mock `scope` object. Take a look at the following unit test for our controller:
__`test/unit/controllersSpec.js`:__
developed. If our controller is available on the global namespace then we could simply instantiate it
with a mock `scope` object:
```js
describe('PhoneCat controllers', function() {
describe('PhoneListCtrl', function(){
describe('PhoneListCtrl', function(){
it('should create "phones" model with 3 phones', function() {
var scope = {},
ctrl = new PhoneListCtrl(scope);
it('should create "phones" model with 3 phones', function() {
var scope = {},
ctrl = new PhoneListCtrl(scope);
expect(scope.phones.length).toBe(3);
});
expect(scope.phones.length).toBe(3);
});
});
```
@@ -154,67 +146,72 @@ Angular. Since testing is such a critical part of software development, we make
tests in Angular so that developers are encouraged to write them.
### Testing non-Global Controllers
In practice, you will not want to have your controller functions in the global namespace. Instead,
we have registered our controllers in the `phonecatApp` module. In this case Angular provides a
service, `$controller`, which will retrieve your controller by name. Here is the same test using
`$controller`:
In practice, you will not want to have your controller functions in the global namespace. Instead,
you can see that we have registered it via an anonymous constructor function on the `phonecatApp`
module.
In this case Angular provides a service, `$controller`, which will retrieve your controller by name.
Here is the same test using `$controller`:
__`test/unit/controllersSpec.js`:__
```js
describe('PhoneCat controllers', function() {
describe('PhoneListCtrl', function(){
beforeEach(module('phonecatApp'));
describe('PhoneListCtrl', function(){
it('should create "phones" model with 3 phones', inject(function($controller) {
var scope = {},
ctrl = $controller('PhoneListCtrl', {$scope:scope});
it('should create "phones" model with 3 phones', inject(function($controller) {
var scope = {},
ctrl = $controller('PhoneListCtrl', { $scope: scope });
expect(scope.phones.length).toBe(3);
}));
expect(scope.phones.length).toBe(3);
}));
});
});
```
Don't forget that we need to load up the `phonecatApp` module into the test so that the controller
is available to be injected.
* Before each test we tell Angular to load the `phonecatApp` module.
* We ask Angular to `inject` the `$controller` service into our test function
* We use `$controller` to create an instance of the `PhoneListCtrl`
* With this instance, we verify that the phones array property on the scope contains three records.
### Writing and Running Tests
Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework when
writing tests. Although Angular does not require you to use Jasmine, we wrote all of the tests in
this tutorial in Jasmine. You can learn about Jasmine on the [Jasmine home page](http://jasmine.github.io/) and at the [Jasmine docs](http://jasmine.github.io/).
this tutorial in Jasmine v1.3. You can learn about Jasmine on the [Jasmine home page][jasmine] and
at the [Jasmine docs][jasmine-docs].
The angular-seed project is pre-configured to run all unit tests using [Karma](http://karma-runner.github.io/). Ensure that the necessary karma plugins are installed.
You can do this by issuing `npm install` into your terminal.
The angular-seed project is pre-configured to run unit tests using [Karma][karma] but you will need
to ensure that Karma and its necessary plugins are installed. You can do this by running
`npm install`.
To run the tests, and then watch the files for changes: `npm test`.
To run the test, do the following:
1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run
`./scripts/test.sh` (if you are on Windows, run scripts\test.bat) to start the Karma server (the
config file necessary to start the server is located at `./config/karma.conf.js`).
2. Karma will start a new instance of Chrome browser automatically. Just ignore it and let it run in
* Karma will start a new instance of Chrome browser automatically. Just ignore it and let it run in
the background. Karma will use this browser for test execution.
* You should see the following or similar output in the terminal:
3. You should see the following or similar output in the terminal:
info: Karma server started at http://localhost:9876/
info (launcher): Starting browser "Chrome"
info (Chrome 22.0): Connected on socket id tPUm9DXcLHtZTKbAEO-n
Chrome 22.0: Executed 1 of 1 SUCCESS (0.093 secs / 0.004 secs)
<pre>
info: Karma server started at http://localhost:9876/
info (launcher): Starting browser "Chrome"
info (Chrome 22.0): Connected on socket id tPUm9DXcLHtZTKbAEO-n
Chrome 22.0: Executed 1 of 1 SUCCESS (0.093 secs / 0.004 secs)
</pre>
Yay! The test passed! Or not...
4. To rerun the tests, just change any of the source or test .js files. Karma will notice the change
* To rerun the tests, just change any of the source or test .js files. Karma will notice the change
and will rerun the tests for you. Now isn't that sweet?
# Experiments
* Add another binding to `index.html`. For example:
<p>Total number of phones: {{phones.length}}</p>
```html
<p>Total number of phones: {{phones.length}}</p>
```
* Create a new model property in the controller and bind to it from the template. For example:
@@ -226,7 +223,11 @@ To run the test, do the following:
Refresh your browser and verify that it says "Hello, World!".
* Create a repeater that constructs a simple table:
* Update the unit test for the controller in ./test/unit/controllersSpec.js to reflect the previous change. For example by adding:
expect(scope.name).toBe('World');
* Create a repeater in `index.html` that constructs a simple table:
<table>
<tr><th>row number</th></tr>
@@ -240,7 +241,7 @@ To run the test, do the following:
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
</table>
* Make the unit test fail by changing the `toBe(3)` statement to `toBe(4)`.
* Make the unit test fail by changing `expect(scope.phones.length).toBe(3)` to instead use `toBe(4)`.
# Summary
@@ -251,3 +252,7 @@ to the app.
<ul doc-tutorial-nav="2"></ul>
[jasmine]: http://jasmine.github.io/
[jasmine-docs]: http://jasmine.github.io/1.3/introduction.html
[karma]: http://karma-runner.github.io/
+66 -74
View File
@@ -11,15 +11,10 @@ simple; we will add full text search (yes, it will be simple!). We will also wri
test, because a good end-to-end test is a good friend. It stays with your app, keeps an eye on it,
and quickly detects regressions.
<div doc-tutorial-reset="3"></div>
The app now has a search box. Notice that the phone list on the page changes depending on what a
* The app now has a search box. Notice that the phone list on the page changes depending on what a
user types into the search box.
The most important differences between Steps 2 and 3 are listed below. You can see the full diff on
[GitHub](https://github.com/angular/angular-phonecat/compare/step-2...step-3):
<div doc-tutorial-reset="3"></div>
## Controller
@@ -33,14 +28,14 @@ __`app/index.html`:__
```html
<div class="container-fluid">
<div class="row-fluid">
<div class="span2">
<div class="row">
<div class="col-md-2">
<!--Sidebar content-->
Search: <input ng-model="query">
</div>
<div class="span10">
<div class="col-md-10">
<!--Body content-->
<ul class="phones">
@@ -96,104 +91,101 @@ describe('PhoneCat App', function() {
describe('Phone list view', function() {
beforeEach(function() {
browser().navigateTo('../../app/index.html');
browser.get('app/index.html');
});
it('should filter the phone list as user types into the search box', function() {
expect(repeater('.phones li').count()).toBe(3);
input('query').enter('nexus');
expect(repeater('.phones li').count()).toBe(1);
var phoneList = element.all(by.repeater('phone in phones'));
var query = element(by.model('query'));
input('query').enter('motorola');
expect(repeater('.phones li').count()).toBe(2);
expect(phoneList.count()).toBe(3);
query.sendKeys('nexus');
expect(phoneList.count()).toBe(1);
query.clear();
query.sendKeys('motorola');
expect(phoneList.count()).toBe(2);
});
});
});
```
Even though the syntax of this test looks very much like our controller unit test written with
Jasmine, the end-to-end test uses APIs of {@link guide/dev_guide.e2e-testing Angular's end-to-end
test runner}.
To run the end-to-end test, open one of the following in a new browser tab:
* node.js users: http://localhost:8000/test/e2e/runner.html
* users with other http servers:
`http://localhost:[port-number]/[context-path]/test/e2e/runner.html`
* casual reader: http://angular.github.com/angular-phonecat/step-3/test/e2e/runner.html
Previously we've seen how Karma can be used to execute unit tests. Well, it can also run the
end-to-end tests! Use `./scripts/e2e-test.sh` (if you are on Windows, run `scripts\e2e-test.bat`) script for that. End-to-end tests are slow, so unlike
with unit tests, Karma will exit after the test run and will not automatically rerun the test
suite on every file change. To rerun the test suite, execute the `e2e-test.sh` or `e2e-test.bat` script again.
Note: You must ensure you've installed the karma-ng-scenario framework plugin prior to running the
`e2e-test.sh` script. You can do this by issuing `npm install` into your terminal.
This test verifies that the search box and the repeater are correctly wired together. Notice how
easy it is to write end-to-end tests in Angular. Although this example is for a simple test, it
really is that easy to set up any functional, readable, end-to-end test.
### Running End to End Tests with Protractor
Even though the syntax of this test looks very much like our controller unit test written with
Jasmine, the end-to-end test uses APIs of [Protractor](https://github.com/angular/protractor). Read
about the Protractor APIs at https://github.com/angular/protractor/blob/master/docs/api.md.
Much like Karma is the test runner for unit tests, we use Protractor to run end-to-end tests.
Try it with `npm run protractor`. End-to-end tests are slow, so unlike with unit tests, Protractor
will exit after the test run and will not automatically rerun the test suite on every file change.
To rerun the test suite, execute `npm run protractor` again.
<div class="alert alert-info">
Note: You must ensure you've installed the protractor and updated webdriver prior to running the
`npm run protractor`. You can do this by issuing `npm install` and `npm run update-webdriver` into
your terminal.
</div>
# Experiments
* Display the current value of the `query` model by adding a `{{query}}` binding into the
### Display Current Query
Display the current value of the `query` model by adding a `{{query}}` binding into the
`index.html` template, and see how it changes when you type in the input box.
* Let's see how we can get the current value of the `query` model to appear in the HTML page title.
### Display Query in Title
Let's see how we can get the current value of the `query` model to appear in the HTML page title.
You might think you could just add the `{{query}}` to the title tag element as follows:
* Add the following end-to-end test into the `describe` block within `test/e2e/scenarios.js`:
<title>Google Phone Gallery: {{query}}</title>
```js
it('should display the current filter value in the title bar', function() {
expect(browser.getTitle()).toMatch(/Google Phone Gallery:\s*$/);
element(by.model('query')).sendKeys('nexus');
expect(browser.getTitle()).toMatch(/Google Phone Gallery: nexus$/);
});
```
Run protractor (`npm run protractor`) to see this test fail.
* You might think you could just add the `{{query}}` to the title tag element as follows:
<title>Google Phone Gallery: {{query}}</title>
However, when you reload the page, you won't see the expected result. This is because the "query"
model lives in the scope, defined by the `ng-controller="PhoneListCtrl"` directive, on the body element:
model lives in the scope, defined by the `ng-controller="PhoneListCtrl"` directive, on the body
element:
<body ng-controller="PhoneListCtrl">
If you want to bind to the query model from the `<title>` element, you must __move__ the
`ngController` declaration to the HTML element because it is the common parent of both the body
and title elements:
`ngController` declaration to the HTML element because it is the common parent of both the body
and title elements:
<html ng-app="phonecatApp" ng-controller="PhoneListCtrl">
Be sure to __remove__ the `ng-controller` declaration from the body element.
While using double curlies works fine within the title element, you might have noticed that
* Re-run `npm run protractor` to see the test now pass.
* While using double curlies works fine within the title element, you might have noticed that
for a split second they are actually displayed to the user while the page is loading. A better
solution would be to use the {@link ng.directive:ngBind
ngBind} or {@link ng.directive:ngBindTemplate
ngBindTemplate} directives, which are invisible to the user while the page is loading:
solution would be to use the {@link ng.directive:ngBind ngBind} or
{@link ng.directive:ngBindTemplate ngBindTemplate} directives, which are invisible to the user
while the page is loading:
<title ng-bind-template="Google Phone Gallery: {{query}}">Google Phone Gallery</title>
* Add the following end-to-end test into the `describe` block within `test/e2e/scenarios.js`:
```js
it('should display the current filter value within an element with id "status"',
function() {
expect(element('#status').text()).toMatch(/Current filter: \s*$/);
input('query').enter('nexus');
expect(element('#status').text()).toMatch(/Current filter: nexus\s*$/);
//alternative version of the last assertion that tests just the value of the binding
using('#status').expect(binding('query')).toBe('nexus');
});
```
Refresh the browser tab with the end-to-end test runner to see the test fail. To make the test
pass, edit the `index.html` template to add a `div` or `p` element with `id` `"status"` and content
with the `query` binding, prefixed by "Current filter:". For instance:
<div id="status">Current filter: {{query}}</div>
* Add a `pause()` statement inside of an end-to-end test and rerun it. You'll see the runner pause;
this gives you the opportunity to explore the state of your application while it is displayed in
the browser. The app is live! You can change the search query to prove it. Notice how useful this
is for troubleshooting end-to-end tests.
<title ng-bind-template="Google Phone Gallery: {{query}}">Google Phone Gallery</title>
# Summary
+30 -26
View File
@@ -10,17 +10,13 @@ In this step, you will add a feature to let your users control the order of the
list. The dynamic ordering is implemented by creating a new model property, wiring it together with
the repeater, and letting the data binding magic do the rest of the work.
* In addition to the search box, the app displays a drop down menu that allows users to control the
order in which the phones are listed.
<div doc-tutorial-reset="4"></div>
You should see that in addition to the search box, the app displays a drop down menu that allows
users to control the order in which the phones are listed.
The most important differences between Steps 3 and 4 are listed below. You can see the full diff on
[GitHub](https://github.com/angular/angular-phonecat/compare/step-3...step-4):
## Template
__`app/index.html`:__
@@ -36,7 +32,7 @@ __`app/index.html`:__
<ul class="phones">
<li ng-repeat="phone in phones | filter:query | orderBy:orderProp">
{{phone.name}}
<span>{{phone.name}}</span>
<p>{{phone.snippet}}</p>
</li>
</ul>
@@ -91,7 +87,7 @@ phonecatApp.controller('PhoneListCtrl', function ($scope) {
record. This property is used to order phones by age.
* We added a line to the controller that sets the default value of `orderProp` to `age`. If we had
not set a default value here, the `orderBy` filter would remain uninitialized until our
not set a default value here, the `orderBy` filter would remain uninitialized until our
user picked an option from the drop down menu.
This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the
@@ -117,7 +113,7 @@ describe('PhoneCat controllers', function() {
var scope, ctrl;
beforeEach(module('phonecatApp'));
beforeEach(inject(function($controller) {
scope = {};
ctrl = $controller('PhoneListCtrl', {$scope:scope});
@@ -143,7 +139,7 @@ shared by all tests in the parent `describe` block.
You should now see the following output in the Karma tab:
Chrome 22.0: Executed 2 of 2 SUCCESS (0.021 secs / 0.001 secs)
<pre>Chrome 22.0: Executed 2 of 2 SUCCESS (0.021 secs / 0.001 secs)</pre>
Let's turn our attention to the end-to-end test.
@@ -152,28 +148,36 @@ __`test/e2e/scenarios.js`:__
```js
...
it('should be possible to control phone order via the drop down select box',
function() {
//let's narrow the dataset to make the test assertions shorter
input('query').enter('tablet');
it('should be possible to control phone order via the drop down select box', function() {
expect(repeater('.phones li', 'Phone List').column('phone.name')).
toEqual(["Motorola XOOM\u2122 with Wi-Fi",
"MOTOROLA XOOM\u2122"]);
var phoneNameColumn = element.all(by.repeater('phone in phones').column('{{phone.name}}'));
var query = element(by.model('query'));
select('orderProp').option('Alphabetical');
function getNames() {
return phoneNameColumn.map(function(elm) {
return elm.getText();
});
}
expect(repeater('.phones li', 'Phone List').column('phone.name')).
toEqual(["MOTOROLA XOOM\u2122",
"Motorola XOOM\u2122 with Wi-Fi"]);
});
...
query.sendKeys('tablet'); //let's narrow the dataset to make the test assertions shorter
expect(getNames()).toEqual([
"Motorola XOOM\u2122 with Wi-Fi",
"MOTOROLA XOOM\u2122"
]);
element(by.model('orderProp')).findElement(by.css('option[value="name"]')).click();
expect(getNames()).toEqual([
"MOTOROLA XOOM\u2122",
"Motorola XOOM\u2122 with Wi-Fi"
]);
});...
```
The end-to-end test verifies that the ordering mechanism of the select box is working correctly.
You can now rerun `./scripts/e2e-test.sh` or refresh the browser tab with the end-to-end test
`runner.html` to see the tests run, or you can see them running on [Angular's server](http://angular.github.com/angular-phonecat/step-4/test/e2e/runner.html).
You can now rerun `npm run protractor` to see the tests run.
# Experiments
+32 -34
View File
@@ -7,20 +7,16 @@
Enough of building an app with three phones in a hard-coded dataset! Let's fetch a larger dataset
from our server using one of Angular's built-in {@link guide/dev_guide.services services} called {@link
from our server using one of Angular's built-in {@link guide/services services} called {@link
ng.$http $http}. We will use Angular's {@link guide/di dependency
injection (DI)} to provide the service to the `PhoneListCtrl` controller.
* There are now a list of 20 phones, loaded from the server.
<div doc-tutorial-reset="5"></div>
You should now see a list of 20 phones.
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-4...step-5):
## Data
The `app/phones/phones.json` file in your project is a dataset that contains a larger list of phones
stored in the JSON format.
@@ -44,7 +40,7 @@ Following is a sample of the file:
We'll use Angular's {@link ng.$http $http} service in our controller to make an HTTP
request to your web server to fetch the data in the `app/phones/phones.json` file. `$http` is just
one of several built-in {@link guide/dev_guide.services angular services} that handle common operations
one of several built-in {@link guide/services Angular services} that handle common operations
in web apps. Angular injects these services for you where you need them.
Services are managed by Angular's {@link guide/di DI subsystem}. Dependency injection
@@ -74,10 +70,10 @@ tutorial.)
The `$http` service returns a {@link ng.$q promise object} with a `success`
method. We call this method to handle the asynchronous response and assign the phone data to the
scope controlled by this controller, as a model called `phones`. Notice that angular detected the
scope controlled by this controller, as a model called `phones`. Notice that Angular detected the
json response and parsed it for us!
To use a service in angular, you simply declare the names of the dependencies you need as arguments
To use a service in Angular, you simply declare the names of the dependencies you need as arguments
to the controller's constructor function, as follows:
phonecatApp.controller('PhoneListCtrl', function ($scope, $http) {...}
@@ -90,13 +86,13 @@ Note that the names of arguments are significant, because the injector uses thes
dependencies.
<img class="diagram" src="img/tutorial/xhr_service_final.png">
<img class="diagram" src="img/tutorial/tutorial_05.png">
### `$` Prefix Naming Convention
You can create your own services, and in fact we will do exactly that in step 11. As a naming
convention, angular's built-in services, Scope methods and a few other Angular APIs have a `$`
convention, Angular's built-in services, Scope methods and a few other Angular APIs have a `$`
prefix in front of the name.
The `$` prefix is there to namespace Angular-provided services.
@@ -109,28 +105,30 @@ properties are considered private, and should not be accessed or modified.
### A Note on Minification
Since Angular infers the controller's dependencies from the names of arguments to the controller's
constructor function, if you were to [minify](http://goo.gl/SAnnsm) the JavaScript code for `PhoneListCtrl` controller, all of its function arguments would be
minified as well, and the dependency injector would not be able to identify services correctly.
constructor function, if you were to [minify](http://goo.gl/SAnnsm) the JavaScript code for
`PhoneListCtrl` controller, all of its function arguments would be minified as well, and the
dependency injector would not be able to identify services correctly.
There are two ways to overcome issues caused by minification:
We can overcome this problem by annotating the function with the names of the dependencies, provided
as strings, which will not get minified. There are two ways to provide these injection annotations:
* You can create a `$inject` property on the controller function which holds an array of strings.
* Create a `$inject` property on the controller function which holds an array of strings.
Each string in the array is the name of the service to inject for the corresponding parameter.
In the case of our example we would write:
In our example we would write:
```js
function PhoneListCtrl($scope, $http) {...}
PhoneListCtrl.$inject = ['$scope', '$http'];
phonecatApp.controller('PhoneListCtrl', PhoneListCtrl);
```
```js
function PhoneListCtrl($scope, $http) {...}
PhoneListCtrl.$inject = ['$scope', '$http'];
phonecatApp.controller('PhoneListCtrl', PhoneListCtrl);
```
* Use the inline bracket notation which wraps the function to be injected into an array of strings
(representing the dependency names) followed by the function to be injected:
* Use an inline annotation where, instead of just providing the function, you provide an array.
This array contains a list of the service names, followed by the function itself.
```js
function PhoneListCtrl($scope, $http) {...}
phonecatApp.controller('PhoneListCtrl', ['$scope', '$http', PhoneListCtrl]);
```
```js
function PhoneListCtrl($scope, $http) {...}
phonecatApp.controller('PhoneListCtrl', ['$scope', '$http', PhoneListCtrl]);
```
Both of these methods work with any function that can be injected by Angular, so it's up to your
project's style guide to decide which one you use.
@@ -166,9 +164,9 @@ __`test/unit/controllersSpec.js`:__
Because we started using dependency injection and our controller has dependencies, constructing the
controller in our tests is a bit more complicated. We could use the `new` operator and provide the
constructor with some kind of fake `$http` implementation. However, the recommended (and easier) way
is to create a controller in the test environment in the same way that angular does it in the
production code behind the scenes, as follows:
constructor with some kind of fake `$http` implementation. However, Angular provides a mock `$http`
service that we can use in unit tests. We configure "fake" responses to server requests by calling
methods on a service called $httpBackend:
```js
describe('PhoneCat controllers', function() {
@@ -181,7 +179,7 @@ describe('PhoneCat controllers', function() {
// The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
// This allows us to inject a service but then attach it to a variable
// with the same name as the service.
// with the same name as the service in order to avoid a name conflict.
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('phones/phones.json').
@@ -252,13 +250,13 @@ Finally, we verify that the default value of `orderProp` is set correctly:
You should now see the following output in the Karma tab:
Chrome 22.0: Executed 2 of 2 SUCCESS (0.028 secs / 0.007 secs)
<pre>Chrome 22.0: Executed 2 of 2 SUCCESS (0.028 secs / 0.007 secs)</pre>
# Experiments
* At the bottom of `index.html`, add a `{{phones | json}}` binding to see the list of phones
* At the bottom of `index.html`, add a `<pre>{{phones | json}}</pre>` binding to see the list of phones
displayed in json format.
* In the `PhoneListCtrl` controller, pre-process the http response by limiting the number of phones
@@ -269,7 +267,7 @@ to the first 5 in the list. Use the following code in the `$http` callback:
# Summary
Now that you have learned how easy it is to use angular services (thanks to Angular's dependency
Now that you have learned how easy it is to use Angular services (thanks to Angular's dependency
injection), go to {@link step_06 step 6}, where you will add some
thumbnail images of phones and some links.
+9 -12
View File
@@ -10,15 +10,10 @@ In this step, you will add thumbnail images for the phones in the phone list, an
now, will go nowhere. In subsequent steps you will use the links to display additional information
about the phones in the catalog.
* There are now links and images of the phones in the list.
<div doc-tutorial-reset="6"></div>
You should now see links and images of the phones in the list.
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-5...step-6):
## Data
Note that the `phones.json` file contains unique ids and image urls for each of the phones. The
@@ -63,7 +58,7 @@ the element attribute.
We also added phone images next to each record using an image tag with the {@link
ng.directive:ngSrc ngSrc} directive. That directive prevents the
browser from treating the angular `{{ expression }}` markup literally, and initiating a request to
browser from treating the Angular `{{ expression }}` markup literally, and initiating a request to
invalid url `http://localhost:8000/app/{{phone.imageUrl}}`, which it would have done if we had only
specified an attribute binding in a regular `src` attribute (`<img src="{{phone.imageUrl}}">`).
Using the `ngSrc` directive prevents the browser from making an http request to an invalid location.
@@ -76,9 +71,12 @@ __`test/e2e/scenarios.js`__:
```js
...
it('should render phone specific links', function() {
input('query').enter('nexus');
element('.phones li a').click();
expect(browser().location().url()).toBe('/phones/nexus-s');
var query = element(by.model('query'));
query.sendKeys('nexus');
element(by.css('.phones li a')).click();
browser.getLocationAbsUrl().then(function(url) {
expect(url.split('#')[1]).toBe('/phones/nexus-s');
});
});
...
```
@@ -86,8 +84,7 @@ __`test/e2e/scenarios.js`__:
We added a new end-to-end test to verify that the app is generating correct links to the phone
views that we will implement in the upcoming steps.
You can now rerun `./scripts/e2e-test.sh` or refresh the browser tab with the end-to-end test
runner to see the tests run, or you can see them running on [Angular's server](http://angular.github.com/angular-phonecat/step-6/test/e2e/runner.html).
You can now rerun `npm run protractor` to see the tests run.
# Experiments
+184 -132
View File
@@ -7,18 +7,50 @@
In this step, you will learn how to create a layout template and how to build an app that has
multiple views by adding routing.
multiple views by adding routing, using an Angular module called 'ngRoute'.
* When you now navigate to `app/index.html`, you are redirected to `app/index.html#/phones`
and the phone list appears in the browser.
* When you click on a phone link the url changes to one specific to that phone and the stub of a
phone detail page is displayed.
<div doc-tutorial-reset="7"></div>
## Dependencies
Note that when you now navigate to `app/index.html`, you are redirected to `app/index.html#/phones`
and the same phone list appears in the browser. When you click on a phone link the stub of a phone
detail page is displayed.
The routing functionality added by this step is provided by angular in the `ngRoute` module, which
is distributed separately from the core Angular framework.
We are using [Bower][bower] to install client side dependencies. This step updates the
`bower.json` configuration file to include the new dependency:
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-6...step-7).
```json
{
"name": "angular-seed",
"description": "A starter project for AngularJS",
"version": "0.0.0",
"homepage": "https://github.com/angular/angular-seed",
"license": "MIT",
"private": true,
"dependencies": {
"angular": "1.2.x",
"angular-mocks": "~1.2.x",
"bootstrap": "~3.1.1",
"angular-route": "~1.2.x"
}
}
```
The new dependency `"angular-route": "~1.2.x"` tells bower to install a version of the
angular-route component that is compatible with version 1.2.x. We must tell bower to download
and install this dependency.
If you have bower installed globally then you can run `bower install` but for this project we have
preconfigured npm to run bower install for us:
```
npm install
```
## Multiple Views, Routing and Layout Template
@@ -34,12 +66,11 @@ template into what we call a "layout template". This is a template that is commo
our application. Other "partial templates" are then included into this layout template depending on
the current "route" — the view that is currently displayed to the user.
Application routes in Angular are declared via the
{@link ngRoute.$routeProvider $routeProvider}, which is the provider of the
{@link ngRoute.$route $route service}. This service makes it easy to wire together
controllers, view templates, and the current
URL location in the browser. Using this feature we can implement [deep linking](http://en.wikipedia.org/wiki/Deep_linking), which lets us utilize the browser's
history (back and forward navigation) and bookmarks.
Application routes in Angular are declared via the {@link ngRoute.$routeProvider $routeProvider},
which is the provider of the {@link ngRoute.$route $route service}. This service makes it easy to
wire together controllers, view templates, and the current URL location in the browser. Using this
feature we can implement [deep linking](http://en.wikipedia.org/wiki/Deep_linking), which lets us
utilize the browser's history (back and forward navigation) and bookmarks.
### A Note About DI, Injector and Providers
@@ -66,109 +97,10 @@ service, the `$routeProvider` exposes APIs that allow you to define routes for y
Angular modules solve the problem of removing global state from the application and provide a way
of configuring the injector. As opposed to AMD or require.js modules, Angular modules don't try to
solve the problem of script load ordering or lazy script fetching. These goals are totally independent and
both module systems can live side by side and fulfil their goals.
both module systems can live side by side and fulfill their goals.
## The App Module
__`app/js/app.js`:__
```js
var phonecatApp = angular.module('phonecatApp', [
'ngRoute',
'phonecatControllers'
]);
phonecatApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/phones', {
templateUrl: 'partials/phone-list.html',
controller: 'PhoneListCtrl'
}).
when('/phones/:phoneId', {
templateUrl: 'partials/phone-detail.html',
controller: 'PhoneDetailCtrl'
}).
otherwise({
redirectTo: '/phones'
});
}]);
```
In order to configure our application with routes, we need to create a module for our application.
We call this module `phonecatApp`. Notice the second argument passed to `angular.module`:
`['ngRoute', 'phonecatControllers']`. This array lists the modules that `phonecatApp` depends on.
Above, we added `angular-route.js` to `index.html`. That's not all we need to do to be able to use
it, however. We also have to add `ngRoute` as a dependency of our app. To improve the organization
of the app, we're going to move the controllers into their own file (as shown below), and call the
module `phonecatControllers`. By listing these two modules as dependencies of `phonecatApp`, we
can use the directives and services they provide.
Thus using the `config` API we request the `$routeProvider` to be injected into our config function
and use the {@link ngRoute.$routeProvider#when `$routeProvider.when`} API to define our routes.
Our application routes are defined as follows:
* The phone list view will be shown when the URL hash fragment is `/phones`. To construct this
view, Angular will use the `phone-list.html` template and the `PhoneListCtrl` controller.
* The phone details view will be shown when the URL hash fragment matches '/phone/:phoneId', where
`:phoneId` is a variable part of the URL. To construct the phone details view, angular will use the
`phone-detail.html` template and the `PhoneDetailCtrl` controller.
We reused the `PhoneListCtrl` controller that we constructed in previous steps and we added a new,
empty `PhoneDetailCtrl` controller to the `app/js/controllers.js` file for the phone details view.
`$routeProvider.otherwise({redirectTo: '/phones'})` triggers a redirection to `/phones` when the browser
address doesn't match either of our routes.
Note the use of the `:phoneId` parameter in the second route declaration. The `$route` service uses
the route declaration — `'/phones/:phoneId'` — as a template that is matched against the current
URL. All variables defined with the `:` notation are extracted into the
{@link ngRoute.$routeParams `$routeParams`} object.
In order for our application to bootstrap with our newly created module we'll also need to specify
the module name as the value of the {@link ng.directive:ngApp ngApp}
directive:
__`app/index.html`:__
```html
<!doctype html>
<html lang="en" ng-app="phonecatApp">
...
```
## Controllers
__`app/js/controllers.js`:__
```js
var phonecatControllers = angular.module('phonecatControllers', []);
phonecatControllers.controller('PhoneListCtrl', ['$scope', '$http',
function ($scope, $http) {
$http.get('phones/phones.json').success(function(data) {
$scope.phones = data;
});
$scope.orderProp = 'age';
}]);
phonecatControllers.controller('PhoneDetailCtrl', ['$scope', '$routeParams',
function($scope, $routeParams) {
$scope.phoneId = $routeParams.phoneId;
}]);
```
Again, note that we created a new module called `phonecatControllers`. For small AngularJS applications,
it's common to create just one module for all of your controllers if there are just a few. For larger apps,
you will probably want to create separate modules for each major feature of your app.
Because our example app is relatively small, we'll add all of our controllers to this module.
To deepen your understanding of DI on Angular, see
[Understanding Dependency Injection](https://github.com/angular/angular.js/wiki/Understanding-Dependency-Injection).
## Template
@@ -177,9 +109,8 @@ ngView} directive. The role of the `ngView` directive is to include the view tem
route into the layout template. This makes it a perfect fit for our `index.html` template.
<div class="alert alert-info">
**Note:** Starting with AngularJS version 1.2, `ngRoute` is in its own module and must be loaded by loading
the `angular-route.js` file distributed with Angular. The easiest way to load the file is to add a `<script>`
tag to your `index.html` file as shown below.
**Note:** Starting with AngularJS version 1.2, `ngRoute` is in its own module and must be loaded by
loading the additional `angular-route.js` file, which we download via Bower above.
</div>
__`app/index.html`:__
@@ -189,8 +120,8 @@ __`app/index.html`:__
<html lang="en" ng-app="phonecatApp">
<head>
...
<script src="lib/angular/angular.js"></script>
<script src="lib/angular/angular-route.js"></script>
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/angular-route/angular-route.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
</head>
@@ -202,6 +133,12 @@ __`app/index.html`:__
</html>
```
We have added two new `<script>` tags in our index file to load up extra JavaScript files into our
application:
- `angular-route.js` : defines the Angular `ngRoute` module, which provides us with routing.
- `app.js` : this file now holds the root module of our application.
Note that we removed most of the code in the `index.html` template and replaced it with a single
line containing a div with the `ng-view` attribute. The code that we removed was placed into the
`phone-list.html` template:
@@ -210,8 +147,8 @@ __`app/partials/phone-list.html`:__
```html
<div class="container-fluid">
<div class="row-fluid">
<div class="span2">
<div class="row">
<div class="col-md-2">
<!--Sidebar content-->
Search: <input ng-model="query">
@@ -222,7 +159,7 @@ __`app/partials/phone-list.html`:__
</select>
</div>
<div class="span10">
<div class="col-md-10">
<!--Body content-->
<ul class="phones">
@@ -248,10 +185,116 @@ We also added a placeholder template for the phone details view:
__`app/partials/phone-detail.html`:__
```html
TBD: detail view for {{phoneId}}
TBD: detail view for <span>{{phoneId}}</span>
```
Note how we are using `phoneId` model defined in the `PhoneDetailCtrl` controller.
Note how we are using the `phoneId` expression which will be defined in the `PhoneDetailCtrl` controller.
## The App Module
To improve the organization of the app, we are making use of Angular's `ngRoute` module and we've
moved the controllers into their own module `phonecatControllers` (as shown below).
We added `angular-route.js` to `index.html` and created a new `phonecatControllers` module in
`controllers.js`. That's not all we need to do to be able to use their code, however. We also have
to add the modules dependencies of our app. By listing these two modules as dependencies of
`phonecatApp`, we can use the directives and services they provide.
__`app/js/app.js`:__
```js
var phonecatApp = angular.module('phonecatApp', [
'ngRoute',
'phonecatControllers'
]);
...
```
Notice the second argument passed to `angular.module`, `['ngRoute', 'phonecatControllers']`. This
array lists the modules that `phonecatApp` depends on.
```js
...
phonecatApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/phones', {
templateUrl: 'partials/phone-list.html',
controller: 'PhoneListCtrl'
}).
when('/phones/:phoneId', {
templateUrl: 'partials/phone-detail.html',
controller: 'PhoneDetailCtrl'
}).
otherwise({
redirectTo: '/phones'
});
}]);
```
Using the `phonecatApp.config()` method, we request the `$routeProvider` to be injected into our
config function and use the {@link ngRoute.$routeProvider#when `$routeProvider.when()`} method to
define our routes.
Our application routes are defined as follows:
* `when('/phones')`: The phone list view will be shown when the URL hash fragment is `/phones`. To
construct this view, Angular will use the `phone-list.html` template and the `PhoneListCtrl`
controller.
* `when('/phones/:phoneId')`: The phone details view will be shown when the URL hash fragment
matches '/phones/:phoneId', where `:phoneId` is a variable part of the URL. To construct the phone
details view, Angular will use the `phone-detail.html` template and the `PhoneDetailCtrl`
controller.
* `otherwise({redirectTo: '/phones'})`: triggers a redirection to `/phones` when the browser
address doesn't match either of our routes.
We reused the `PhoneListCtrl` controller that we constructed in previous steps and we added a new,
empty `PhoneDetailCtrl` controller to the `app/js/controllers.js` file for the phone details view.
Note the use of the `:phoneId` parameter in the second route declaration. The `$route` service uses
the route declaration — `'/phones/:phoneId'` — as a template that is matched against the current
URL. All variables defined with the `:` notation are extracted into the
{@link ngRoute.$routeParams `$routeParams`} object.
## Controllers
__`app/js/controllers.js`:__
```js
var phonecatControllers = angular.module('phonecatControllers', []);
phonecatControllers.controller('PhoneListCtrl', ['$scope', '$http',
function ($scope, $http) {
$http.get('phones/phones.json').success(function(data) {
$scope.phones = data;
});
$scope.orderProp = 'age';
}]);
phonecatControllers.controller('PhoneDetailCtrl', ['$scope', '$routeParams',
function($scope, $routeParams) {
$scope.phoneId = $routeParams.phoneId;
}]);
```
Again, note that we created a new module called `phonecatControllers`. For small AngularJS
applications, it's common to create just one module for all of your controllers if there are just a
few. As your application grows it is quite common to refactor your code into additional modules.
For larger apps, you will probably want to create separate modules for each major feature of
your app.
Because our example app is relatively small, we'll just add all of our controllers to the
`phonecatControllers` module.
## Test
@@ -261,28 +304,34 @@ to various URLs and verify that the correct view was rendered.
```js
...
it('should redirect index.html to index.html#/phones', function() {
browser().navigateTo('app/index.html');
expect(browser().location().url()).toBe('/phones');
it('should redirect index.html to index.html#/phones', function() {
browser.get('app/index.html');
browser.getLocationAbsUrl().then(function(url) {
expect(url.split('#')[1]).toBe('/phones');
});
});
describe('Phone list view', function() {
beforeEach(function() {
browser.get('app/index.html#/phones');
});
...
describe('Phone detail view', function() {
describe('Phone detail view', function() {
beforeEach(function() {
browser().navigateTo('app/index.html#/phones/nexus-s');
browser.get('app/index.html#/phones/nexus-s');
});
it('should display placeholder page with phoneId', function() {
expect(binding('phoneId')).toBe('nexus-s');
expect(element(by.binding('phoneId')).getText()).toBe('nexus-s');
});
});
});
```
You can now rerun `./scripts/e2e-test.sh` or refresh the browser tab with the end-to-end test
runner to see the tests run, or you can see them running on [Angular's server](http://angular.github.com/angular-phonecat/step-7/test/e2e/runner.html).
You can now rerun `npm run protractor` to see the tests run.
# Experiments
@@ -308,3 +357,6 @@ step 8} to implement the phone details view.
<ul doc-tutorial-nav="7"></ul>
[bower]: http://bower.io
+17 -17
View File
@@ -9,18 +9,15 @@
In this step, you will implement the phone details view, which is displayed when a user clicks on a
phone in the phone list.
* When you click on a phone on the list, the phone details page with phone-specific information
is displayed.
To implement the phone details view we used {@link ng.$http $http} to fetch our data, and we
fleshed out the `phone-detail.html` view template.
<div doc-tutorial-reset="8"></div>
Now when you click on a phone on the list, the phone details page with phone-specific information
is displayed.
To implement the phone details view we will use {@link ng.$http $http} to fetch
our data, and we'll flesh out the `phone-detail.html` view template.
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-7...step-8):
## Data
In addition to `phones.json`, the `app/phones/` directory also contains one json file for each
@@ -79,7 +76,7 @@ route by the `$route` service.
## Template
The TBD placeholder line has been replaced with lists and bindings that comprise the phone details.
Note where we use the angular `{{expression}}` markup and `ngRepeat` to project phone data from
Note where we use the Angular `{{expression}}` markup and `ngRepeat` to project phone data from
our model into the view.
@@ -127,7 +124,11 @@ step 5.
__`test/unit/controllersSpec.js`:__
```js
...
beforeEach(module('phonecatApp'));
...
describe('PhoneDetailCtrl', function(){
var scope, $httpBackend, ctrl;
@@ -153,7 +154,7 @@ __`test/unit/controllersSpec.js`:__
You should now see the following output in the Karma tab:
Chrome 22.0: Executed 3 of 3 SUCCESS (0.039 secs / 0.012 secs)
<pre>Chrome 22.0: Executed 3 of 3 SUCCESS (0.039 secs / 0.012 secs)</pre>
We also added a new end-to-end test that navigates to the Nexus S detail page and verifies that the
@@ -166,26 +167,25 @@ __`test/e2e/scenarios.js`:__
describe('Phone detail view', function() {
beforeEach(function() {
browser().navigateTo('../../app/index.html#/phones/nexus-s');
browser.get('app/index.html#/phones/nexus-s');
});
it('should display nexus-s page', function() {
expect(binding('phone.name')).toBe('Nexus S');
expect(element(by.binding('phone.name')).getText()).toBe('Nexus S');
});
});
...
```
You can now rerun `./scripts/e2e-test.sh` or refresh the browser tab with the end-to-end test
runner to see the tests run, or you can see them running on [Angular's server](http://angular.github.com/angular-phonecat/step-8/test/e2e/runner.html).
You can now rerun `npm run protractor` to see the tests run.
# Experiments
* Using the {@link guide/dev_guide.e2e-testing Angular's end-to-end test runner API}, write a test
that verifies that we display 4 thumbnail images on the Nexus S details page.
* Using the [Protractor API](https://github.com/angular/protractor/blob/master/docs/api.md),
write a test that verifies that we display 4 thumbnail images on the Nexus S details page.
# Summary
+12 -13
View File
@@ -5,20 +5,13 @@
<ul doc-tutorial-nav="9"></ul>
In this step you will learn how to create your own custom display filter.
<div doc-tutorial-reset="9"></div>
Navigate to one of the detail pages.
In the previous step, the details page displayed either "true" or "false" to indicate whether
* In the previous step, the details page displayed either "true" or "false" to indicate whether
certain phone features were present or not. We have used a custom filter to convert those text
strings into glyphs: ✓ for "true", and ✘ for "false". Let's see what the filter code looks like.
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-8...step-9):
<div doc-tutorial-reset="9"></div>
## Custom Filter
@@ -46,7 +39,7 @@ __`app/js/app.js`:__
```js
...
angular.module('phonecatApp', ['ngRoute','phonecatControllers','phonecatFilters']).
angular.module('phonecatApp', ['ngRoute','phonecatControllers','phonecatFilters']);
...
```
@@ -116,9 +109,13 @@ for this test run.
Note that we call the helper function, `inject(function(checkmarkFilter) { ... })`, to get
access to the filter that we want to test. See {@link angular.mock.inject angular.mock.inject()}.
Notice that the suffix 'Filter' is appended to your filter name when injected.
See the {@link guide/filter#using-filters-in-controllers-services-and-directives Filter Guide}
section where this is outlined.
You should now see the following output in the Karma tab:
Chrome 22.0: Executed 4 of 4 SUCCESS (0.034 secs / 0.012 secs)
<pre>Chrome 22.0: Executed 4 of 4 SUCCESS (0.034 secs / 0.012 secs)</pre>
# Experiments
@@ -131,9 +128,11 @@ following bindings to `index.html`:
* `{{ 1304375948024 | date:"MM/dd/yyyy @ h:mma" }}`
* We can also create a model with an input element, and combine it with a filtered binding. Add
the following to index.html:
the following to index.html:
<input ng-model="userInput"> Uppercased: {{ userInput | uppercase }}
```html
<input ng-model="userInput"> Uppercased: {{ userInput | uppercase }}
```
# Summary
+9 -15
View File
@@ -8,16 +8,11 @@
In this step, you will add a clickable phone image swapper to the phone details page.
<div doc-tutorial-reset="10"></div>
The phone details view displays one large image of the current phone and several smaller thumbnail
* The phone details view displays one large image of the current phone and several smaller thumbnail
images. It would be great if we could replace the large image with any of the thumbnails just by
clicking on the desired thumbnail image. Let's have a look at how we can do this with Angular.
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-9...step-10):
<div doc-tutorial-reset="10"></div>
## Controller
@@ -90,23 +85,22 @@ __`test/e2e/scenarios.js`:__
...
it('should display the first phone image as the main phone image', function() {
expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg');
expect(element(by.css('img.phone')).getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/);
});
it('should swap main image if a thumbnail image is clicked on', function() {
element('.phone-thumbs li:nth-child(3) img').click();
expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.2.jpg');
element(by.css('.phone-thumbs li:nth-child(3) img')).click();
expect(element(by.css('img.phone')).getAttribute('src')).toMatch(/img\/phones\/nexus-s.2.jpg/);
element('.phone-thumbs li:nth-child(1) img').click();
expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg');
element(by.css('.phone-thumbs li:nth-child(1) img')).click();
expect(element(by.css('img.phone')).getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/);
});
});
});
```
You can now rerun `./scripts/e2e-test.sh` or refresh the browser tab with the end-to-end test
runner to see the tests run, or you can see them running on [Angular's server](http://angular.github.com/angular-phonecat/step-10/test/e2e/runner.html).
You can now rerun `npm run protractor` to see the tests run.
# Experiments
+78 -22
View File
@@ -6,37 +6,74 @@
<ul doc-tutorial-nav="11"></ul>
In this step, you will improve the way our app fetches data.
In this step, you will change the way our app fetches data.
* We defined a custom service that represents a [RESTful][restful] client. Using this client we
can make requests to the server for data in an easier way, without having to deal with the
lower-level {@link ng.$http $http} API, HTTP methods and URLs.
<div doc-tutorial-reset="11"></div>
## Dependencies
The next improvement we will make to our app is to define a custom service that represents a [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) client. Using this client we
can make XHR requests for data in an easier way, without having to deal with the lower-level {@link
ng.$http $http} API, HTTP methods and URLs.
The RESTful functionality is provided by Angular in the `ngResource` module, which is distributed
separately from the core Angular framework.
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-10...step-11):
We are using [Bower][bower] to install client side dependencies. This step updates the
`bower.json` configuration file to include the new dependency:
```
{
"name": "angular-seed",
"description": "A starter project for AngularJS",
"version": "0.0.0",
"homepage": "https://github.com/angular/angular-seed",
"license": "MIT",
"private": true,
"dependencies": {
"angular": "1.2.x",
"angular-mocks": "~1.2.x",
"bootstrap": "~3.1.1",
"angular-route": "~1.2.x",
"angular-resource": "~1.2.x"
}
}
```
The new dependency `"angular-resource": "~1.2.x"` tells bower to install a version of the
angular-resource component that is compatible with version 1.2.x. We must ask bower to download
and install this dependency. We can do this by running:
```
npm install
```
<div class="alert alert-info">
If you have bower installed globally then you can run `bower install` but for this project we have
preconfigured `npm install` to run bower for us.
</div>
## Template
The custom service is defined in `app/js/services.js` so we need to include this file in our layout
template. Additionally, we also need to load the `angular-resource.js` file, which contains the
{@link api/ngResource ngResource} module and in it the {@link api/ngResource.$resource $resource}
service, that we'll soon use:
Our custom resource service will be defined in `app/js/services.js` so we need to include this file
in our layout template. Additionally, we also need to load the `angular-resource.js` file, which
contains the {@link api/ngResource ngResource} module:
__`app/index.html`.__
```html
...
<script src="bower_components/angular-resource/angular-resource.js"></script>
<script src="js/services.js"></script>
<script src="lib/angular/angular-resource.js"></script>
...
```
## Service
We create our own service to provide access to the phone data on the server:
__`app/js/services.js`.__
```js
@@ -52,13 +89,12 @@ phonecatServices.factory('Phone', ['$resource',
We used the module API to register a custom service using a factory function. We passed in the name
of the service - 'Phone' - and the factory function. The factory function is similar to a
controller's constructor in that both can declare dependencies via function arguments. The Phone
service declared a dependency on the `$resource` service.
controller's constructor in that both can declare dependencies to be injected via function
arguments. The Phone service declared a dependency on the `$resource` service.
The {@link ngResource.$resource `$resource`} service makes it easy to create a
[RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) client with just a few
lines of code. This client can then be used in our application, instead of the lower-level {@link
ng.$http $http} service.
[RESTful][restful] client with just a few lines of code. This client can then be used in our
application, instead of the lower-level {@link ng.$http $http} service.
__`app/js/app.js`.__
@@ -128,6 +164,22 @@ we require, so in these cases, we can add a callback to process the server respo
## Test
Because we're now using the {@link ngResource ngResource} module, it's necessary to also need to
update the Karma config file with angular-resource so the new tests will pass.
__`test/karma.conf.js`:__
```js
files : [
'app/bower_components/angular/angular.js',
'app/bower_components/angular-route/angular-route.js',
'app/bower_components/angular-resource/angular-resource.js',
'app/bower_components/angular-mocks/angular-mocks.js',
'app/js/**/*.js',
'test/unit/**/*.js'
],
```
We have modified our unit tests to verify that our new service is issuing HTTP requests and
processing them as expected. The tests also check that our controllers are interacting with the
service correctly.
@@ -135,13 +187,13 @@ service correctly.
The {@link ngResource.$resource $resource} service augments the response object
with methods for updating and deleting the resource. If we were to use the standard `toEqual`
matcher, our tests would fail because the test values would not match the responses exactly. To
solve the problem, we use a newly-defined `toEqualData` [Jasmine matcher](https://github.com/pivotal/jasmine/wiki/Matchers). When the
`toEqualData` matcher compares two objects, it takes only object properties into account and
solve the problem, we use a newly-defined `toEqualData` [Jasmine matcher][jasmine-matchers]. When
the `toEqualData` matcher compares two objects, it takes only object properties into account and
ignores methods.
__`test/unit/controllersSpec.js`:__
```js
describe('PhoneCat controllers', function() {
@@ -166,12 +218,12 @@ describe('PhoneCat controllers', function() {
respond([{name: 'Nexus S'}, {name: 'Motorola DROID'}]);
scope = $rootScope.$new();
ctrl = $controller(PhoneListCtrl, {$scope: scope});
ctrl = $controller('PhoneListCtrl', {$scope: scope});
}));
it('should create "phones" model with 2 phones fetched from xhr', function() {
expect(scope.phones).toEqual([]);
expect(scope.phones).toEqualData([]);
$httpBackend.flush();
expect(scope.phones).toEqualData(
@@ -201,7 +253,7 @@ describe('PhoneCat controllers', function() {
$routeParams.phoneId = 'xyz';
scope = $rootScope.$new();
ctrl = $controller(PhoneDetailCtrl, {$scope: scope});
ctrl = $controller('PhoneDetailCtrl', {$scope: scope});
}));
@@ -217,7 +269,7 @@ describe('PhoneCat controllers', function() {
You should now see the following output in the Karma tab:
Chrome 22.0: Executed 4 of 4 SUCCESS (0.038 secs / 0.01 secs)
<pre>Chrome 22.0: Executed 4 of 4 SUCCESS (0.038 secs / 0.01 secs)</pre>
# Summary
@@ -227,3 +279,7 @@ learn how to improve this application with animations.
<ul doc-tutorial-nav="11"></ul>
[restful]: http://en.wikipedia.org/wiki/Representational_State_Transfer
[jasmine-matchers]: https://github.com/pivotal/jasmine/wiki/Matchers
[bower]: http://bower.io/
+136 -47
View File
@@ -9,21 +9,60 @@
In this final step, we will enhance our phonecat web application by attaching CSS and JavaScript
animations on top of the template code we created before.
* Used the `ngAnimate` to enable animations throughout the application.
* Common `ng` directives automatically trigger hooks for animations to tap into.
* When an animation is found then the animation will run in between the standard DOM operation that
is being issued on the element at the given time (e.g. inserting and removing nodes on
{@link api/ng.directive:ngRepeat `ngRepeat`} or adding and removing classes on
{@link api/ng.directive:ngClass `ngClass`}).
<div doc-tutorial-reset="12"></div>
## Dependencies
Now that everything is set in place for a fully functional web application, we can attach CSS and JavaScript
animations to common directives that are used to render our application. AngularJS comes bundled with an
additional JavaScript file called `angular-animate.js` which, when included into the website and set as
a dependency with the application module, will enable animations throughout the application.
The animation functionality is provided by Angular in the `ngAnimate` module, which is distributed
separately from the core Angular framework. In addition we will use `JQuery` in this project to do
extra JavaScript animations.
Common `ng` directives automatically trigger hooks for animations to tap into. When an animation is found
then the animation will run in between the standard DOM operation that is being issued on the element at
the given time (e.g. inserting and removing nodes on ngRepeat or adding and removing classes on ngClass).
We are using [Bower][bower] to install client side dependencies. This step updates the
`bower.json` configuration file to include the new dependency:
The most important changes are listed below. You can see the full diff on
[GitHub](https://github.com/angular/angular-phonecat/compare/step-11...step-12):
```
{
"name": "angular-seed",
"description": "A starter project for AngularJS",
"version": "0.0.0",
"homepage": "https://github.com/angular/angular-seed",
"license": "MIT",
"private": true,
"dependencies": {
"angular": "1.2.x",
"angular-mocks": "~1.2.x",
"bootstrap": "~3.1.1",
"angular-route": "~1.2.x",
"angular-resource": "~1.2.x",
"jquery": "1.10.2",
"angular-animate": "~1.2.x"
}
}
```
* `"angular-animate": "~1.2.x"` tells bower to install a version of the
angular-animate component that is compatible with version 1.2.x.
* `"jquery": "1.10.2"` tells bower to install the 1.10.2 version of JQuery. Note that this is not an
Angular library, it is the standard JQuery library. We can use bower to install a wide range of 3rd
party libraries.
We must ask bower to download and install this dependency. We can do this by running:
```
npm install
```
<div class="alert alert-info">
If you have bower installed globally then you can run `bower install` but for this project we have
preconfigured `npm install` to run bower for us.
</div>
## How Animations work with `ngAnimate`
@@ -34,32 +73,40 @@ To get an idea of how animations work with AngularJS, please read the
## Template
The changes required within the HTML template code is to link the asset files which define the animations as well
as the `angular-animate.js` file. The animation module, known as `ngAnimate`, is defined within
`angular-animate.js` and contains the code necessary to make your application become animation aware.
The changes required within the HTML template code is to link the asset files which define the animations as
well as the `angular-animate.js` file. The animation module, known as {@link api/ngAnimate `ngAnimate`}, is
defined within `angular-animate.js` and contains the code necessary to make your application become animation
aware.
Here's what needs to changed in the index file:
Here's what needs to be changed in the index file:
__`app/index.html`.__
```html
...
<!-- for CSS Transitions and/or Keyframe Animations -->
<link rel="stylesheet" href="css/animations.css">
...
<!-- jQuery is used for JavaScript animations (include this before angular.js) -->
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="bower_components/jquery/jquery.js"></script>
...
<!-- required module to enable animation support in AngularJS -->
<script src="lib/angular/angular-animate.js"></script>
<script src="bower_components/angular-animate/angular-animate.js"></script>
<!-- for JavaScript Animations -->
<script src="js/animations.js"></script>
<!-- for CSS Transitions and/or Keyframe Animations -->
<link rel="stylesheet" href="css/animations.css">
...
```
<div class="alert alert-error">
**Important:** Be sure to use jQuery version `1.10.x`. AngularJS does not yet support jQuery `2.x`.
Be sure to load jQuery before all AngularJS scripts, otherwise AngularJS won't detect jQuery and
animations will not work as expected.
</div>
Animations can now be created within the CSS code (`animations.css`) as well as the JavaScript code (`animations.js`).
@@ -71,7 +118,7 @@ with `ngResource`.
__`app/js/animations.js`.__
```js
angular.module('phonecatAnimations', ['ngAnimate']).
angular.module('phonecatAnimations', ['ngAnimate']);
// ...
// this module will later be used to define animations
// ...
@@ -90,7 +137,7 @@ angular.module('phonecatApp', [
'phonecatControllers',
'phonecatFilters',
'phonecatServices',
]).
]);
// ...
```
@@ -197,7 +244,7 @@ which are described in detail below.
## Animating `ngView` with CSS Keyframe Animations
Next let's add an animation for transitions between route changes in `ngView`.
Next let's add an animation for transitions between route changes in {@link api/ngRoute.directive:ngView `ngView`}.
To start, let's add a new CSS class to our HTML like we did in the example above.
This time, instead of the `ng-repeat` element, let's add it to the element containing the ng-view directive.
@@ -278,18 +325,22 @@ __`app/css/animations.css`.__
/* don't forget about the vendor-prefixes! */
```
Nothing crazy here! Just a simple fade in and fade out effect between pages. The only out of the ordinary thing
here is that we're using absolute positioning to position the next page (identified via `ng-enter`) on top of the
previous page (the one that has the `ng-leave` class) while performing a cross fade animation in between. So
as the previous page is just about to be removed, it fades out while the new page fades in right on top of it.
Once the leave animation is over then element is removed and once the enter animation is complete then the
`ng-enter` and `ng-enter-active` CSS classes are removed from the element rendering it to be position itself
with its default CSS code (so no more absolute positioning once the animation is over). This works fluidly so
that pages flow naturally between route changes without anything jumping around.
Nothing crazy here! Just a simple fade in and fade out effect between pages. The only out of the
ordinary thing here is that we're using absolute positioning to position the next page (identified
via `ng-enter`) on top of the previous page (the one that has the `ng-leave` class) while performing
a cross fade animation in between. So as the previous page is just about to be removed, it fades out
while the new page fades in right on top of it.
The CSS classes applied (the start and end classes) are much the same as with `ng-repeat`. Each time a new page is
loaded the ng-view directive will create a copy of itself, download the template and append the contents. This
ensures that all views are contained within a single HTML element which allows for easy animation control.
Once the leave animation is over then element is removed and once the enter animation is complete
then the `ng-enter` and `ng-enter-active` CSS classes are removed from the element rendering it to
be position itself with its default CSS code (so no more absolute positioning once the animation is
over). This works fluidly so that pages flow naturally between route changes without anything
jumping around.
The CSS classes applied (the start and end classes) are much the same as with `ng-repeat`. Each time
a new page is loaded the ng-view directive will create a copy of itself, download the template and
append the contents. This ensures that all views are contained within a single HTML element which
allows for easy animation control.
For more on CSS animations, see the
[Web Platform documentation](http://docs.webplatform.org/wiki/css/properties/animations).
@@ -301,14 +352,14 @@ Let's add another animation to our application. Switching to our `phone-detail.h
we see that we have a nice thumbnail swapper. By clicking on the thumbnails listed on the page,
the profile phone image changes. But how can we change this around to add animations?
Let's think about it first. Basically, when you click on a thumbnail image, you're changing the state of the profile image to reflect the newly
selected thumbnail image.
Let's think about it first. Basically, when you click on a thumbnail image, you're changing the
state of the profile image to reflect the newly selected thumbnail image.
The best way to specify state changes within HTML is to use classes.
Much like before, how we used a CSS class to specify
an animation, this time the animation will occur whenever the CSS class itself changes.
Much like before, how we used a CSS class to specify an animation, this time the animation will
occur whenever the CSS class itself changes.
Whenever a new phone thumbnail is selected, the state changes and the `.active` CSS class is added to the matching
profile image and the animation plays.
Whenever a new phone thumbnail is selected, the state changes and the `.active` CSS class is added
to the matching profile image and the animation plays.
Let's get started and tweak our HTML code on the `phone-detail.html` page first:
@@ -334,18 +385,54 @@ __`app/partials/phone-detail.html`.__
</ul>
```
Just like with the thumbnails, we're using a repeater to display **all** the profile images as a list, however we're
not animating any repeat-related animations. Instead, we're keeping our eye on the ng-class directive since whenever
the `active` class is true then it will be applied to the element and will render as visible. Otherwise, the profile image
is hidden. In our case, there is always one element that has the active class, and, therefore, there will always
be one phone profile image visible on screen at all times.
Just like with the thumbnails, we're using a repeater to display **all** the profile images as a
list, however we're not animating any repeat-related animations. Instead, we're keeping our eye on
the ng-class directive since whenever the `active` class is true then it will be applied to the
element and will render as visible. Otherwise, the profile image is hidden. In our case, there is
always one element that has the active class, and, therefore, there will always be one phone profile
image visible on screen at all times.
When the active class is added to the element, the `active-add` and the `active-add-active` classes
are added just before to signal AngularJS to fire off an animation. When removed, the
`active-remove` and the `active-remove-active` classes are applied to the element which in turn
trigger another animation.
To ensure that the phone images are displayed correctly when the page is first loaded we also tweak
the detail page CSS styles:
__`app/css/app.css`__
```css
.phone-images {
background-color: white;
width: 450px;
height: 450px;
overflow: hidden;
position: relative;
float: left;
}
...
img.phone {
float: left;
margin-right: 3em;
margin-bottom: 2em;
background-color: white;
padding: 2em;
height: 400px;
width: 400px;
display: none;
}
img.phone:first-child {
display: block;
}
```
When the active class is added to the element, the `active-add` and the `active-add-active` classes are added just before
to signal AngularJS to fire off an animation. When removed, the `active-remove` and the `active-remove-active` classes
are applied to the element which in turn trigger another animation.
You may be thinking that we're just going to create another CSS-enabled animation.
Although we could do that, let's take the opportunity to learn how to create JavaScript-enabled animations with the `animation()` module method.
Although we could do that, let's take the opportunity to learn how to create JavaScript-enabled
animations with the `animation()` module method.
__`app/js/animations.js`.__
@@ -409,7 +496,7 @@ isn't required to do JavaScript animations with AngularJS, but we're going to us
your own JavaScript animation library is beyond the scope of this tutorial. For more on
`jQuery.animate`, see the [jQuery documentation](http://api.jquery.com/animate/).
The `addClass` and `removeClass` callback functions are called whenever an a class is added or removed
The `addClass` and `removeClass` callback functions are called whenever a class is added or removed
on the element that contains the class we registered, which is in this case `.phone`. When the `.active`
class is added to the element (via the `ng-class` directive) the `addClass` JavaScript callback will
be fired with `element` passed in as a parameter to that callback. The last parameter passed in is the
@@ -438,3 +525,5 @@ There you have it! We have created a web app in a relatively short amount of ti
the_end closing notes} we'll cover where to go from here.
<ul doc-tutorial-nav="12"></ul>
[bower]: http://bower.io/
+5 -5
View File
@@ -6,7 +6,7 @@ var basePackage = require('./config');
module.exports = function(config) {
var cdnUrl = "//ajax.googleapis.com/ajax/libs/angularjs/" + versionInfo.currentPackage.cdnVersion;
var cdnUrl = "//ajax.googleapis.com/ajax/libs/angularjs/" + versionInfo.cdnVersion;
var getVersion = function(component, sourceFolder, packageFile) {
sourceFolder = sourceFolder || '../bower_components';
@@ -62,7 +62,7 @@ module.exports = function(config) {
'js/docs.js'
],
stylesheets: [
'components/bootstrap-' + getVersion('bootstrap') + '/dist/css/bootstrap.css',
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/docs.css',
@@ -97,7 +97,7 @@ module.exports = function(config) {
'js/docs.js'
],
stylesheets: [
'components/bootstrap-' + getVersion('bootstrap') + '/dist/css/bootstrap.min.css',
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/docs.css',
@@ -136,7 +136,7 @@ module.exports = function(config) {
'js/docs.js'
],
stylesheets: [
'components/bootstrap-' + getVersion('bootstrap') + '/dist/css/bootstrap.min.css',
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/docs.css',
@@ -171,7 +171,7 @@ module.exports = function(config) {
'js/docs.js'
],
stylesheets: [
'components/bootstrap-' + getVersion('bootstrap') + '/dist/css/bootstrap.min.css',
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/docs.css',
+5 -5
View File
@@ -2,7 +2,7 @@ var gulp = require('gulp');
var concat = require('gulp-concat');
var jshint = require('gulp-jshint');
var bower = require('bower');
var docGenerator = require('dgeni');
var dgeni = require('dgeni');
var merge = require('event-stream').merge;
var path = require('canonical-path');
@@ -38,19 +38,19 @@ gulp.task('build-app', function() {
gulp.task('assets', ['bower'], function() {
return merge(
gulp.src(['app/assets/**/*']).pipe(gulp.dest(outputFolder)),
copyComponent('bootstrap'),
copyComponent('bootstrap', '/dist/**/*'),
copyComponent('open-sans-fontface'),
copyComponent('lunr.js','/*.js'),
copyComponent('google-code-prettify'),
copyComponent('jquery'),
copyComponent('jquery', '/jquery.*'),
copyComponent('marked', '/**/*.js', '../node_modules', 'package.json')
);
});
gulp.task('doc-gen', function() {
return docGenerator('docs.config.js')
.generateDocs()
var generateDocs = dgeni.generator('docs.config.js');
return generateDocs()
.catch(function(error) {
process.exit(1);
});
+1 -1
View File
@@ -5,7 +5,7 @@
]>
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
x="0px" y="0px" width="687px" height="176px" viewBox="0 0 687 176" overflow="visible" enable-background="new 0 0 687 176"
x="0" y="0" width="687px" height="176px" viewBox="0 0 687 176" overflow="visible" enable-background="new 0 0 687 176"
xml:space="preserve">
<defs>
</defs>

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 56 KiB

+42 -30
View File
@@ -33,6 +33,8 @@
goog.provide('goog.i18n.currency');
goog.provide('goog.i18n.currency.CurrencyInfo');
goog.provide('goog.i18n.currency.CurrencyInfoTier2');
/**
@@ -46,7 +48,7 @@ goog.i18n.currency.PRECISION_MASK_ = 0x07;
* Whether the currency sign should be positioned after the number.
* @private
*/
goog.i18n.currency.POSITION_FLAG_ = 0x08;
goog.i18n.currency.POSITION_FLAG_ = 0x10;
/**
@@ -56,6 +58,13 @@ goog.i18n.currency.POSITION_FLAG_ = 0x08;
goog.i18n.currency.SPACE_FLAG_ = 0x20;
/**
* Whether tier2 was enabled already by calling addTier2Support().
* @private
*/
goog.i18n.currency.tier2Enabled_ = false;
/**
* This function will add tier2 currency support. Be default, only tier1
* (most popular currencies) are supported. If an application really needs
@@ -63,9 +72,13 @@ goog.i18n.currency.SPACE_FLAG_ = 0x20;
* before any other functions in this namespace.
*/
goog.i18n.currency.addTier2Support = function() {
for (var key in goog.i18n.currency.CurrencyInfoTier2) {
goog.i18n.currency.CurrencyInfo[key] =
goog.i18n.currency.CurrencyInfoTier2[key];
// Protection from executing this these again and again.
if (!goog.i18n.currency.tier2Enabled_) {
for (var key in goog.i18n.currency.CurrencyInfoTier2) {
goog.i18n.currency.CurrencyInfo[key] =
goog.i18n.currency.CurrencyInfoTier2[key];
}
goog.i18n.currency.tier2Enabled_ = true;
}
};
@@ -244,7 +257,7 @@ goog.i18n.currency.adjustPrecision = function(pattern, currencyCode) {
* the currency sign should be positioned after the number. Valid values are 0
* (before the number) or 16 (after the number). The space flag indicates
* whether a space should be inserted between the currency sign and number.
* Valid values are 0 (no space) and 24 (space).
* Valid values are 0 (no space) and 32 (space).
*
* The number in the array is calculated by adding together the mask and flag
* values. For example:
@@ -252,52 +265,67 @@ goog.i18n.currency.adjustPrecision = function(pattern, currencyCode) {
* 0: no precision (0), currency sign first (0), no space (0)
* 2: two decimals precision (2), currency sign first (0), no space (0)
* 18: two decimals precision (2), currency sign last (16), no space (0)
* 42: two decimals precision (2), currency sign last (16), space (24)
* 50: two decimals precision (2), currency sign last (16), space (32)
*
* @type {!Object.<!Array>}
*/
goog.i18n.currency.CurrencyInfo = {
'AED': [2, 'dh', '\u062f.\u0625.', 'DH'],
'ALL': [0, 'Lek', 'Lek'],
'AUD': [2, '$', 'AU$'],
'BDT': [2, '\u09F3', 'Tk'],
'BGN': [2, 'lev', 'lev'],
'BRL': [2, 'R$', 'R$'],
'CAD': [2, '$', 'C$'],
'CDF': [2, 'FrCD', 'CDF'],
'CHF': [2, 'CHF', 'CHF'],
'CLP': [0, '$', 'CL$'],
'CNY': [2, '¥', 'RMB¥'],
'COP': [0, '$', 'COL$'],
'CRC': [0, '\u20a1', 'CR\u20a1'],
'CZK': [2, 'K\u010d', 'K\u010d'],
'CZK': [50, 'K\u010d', 'K\u010d'],
'DKK': [18, 'kr', 'kr'],
'DOP': [2, '$', 'RD$'],
'EGP': [2, '£', 'LE'],
'EUR': [18, '', ''],
'ETB': [2, 'Birr', 'Birr'],
'EUR': [2, '€', '€'],
'GBP': [2, '£', 'GB£'],
'HKD': [2, '$', 'HK$'],
'HRK': [2, 'kn', 'kn'],
'HUF': [0, 'Ft', 'Ft'],
'IDR': [0, 'Rp', 'Rp'],
'ILS': [2, '\u20AA', 'IL\u20AA'],
'INR': [2, '\u20B9', 'Rs'],
'IRR': [0, 'Rial', 'IRR'],
'ISK': [0, 'kr', 'kr'],
'JMD': [2, '$', 'JA$'],
'JPY': [0, '¥', 'JP¥'],
'KRW': [0, '\u20A9', 'KR₩'],
'LKR': [2, 'Rs', 'SLRs'],
'LTL': [2, 'Lt', 'Lt'],
'LVL': [2, 'Ls', 'Ls'],
'MNT': [0, '\u20AE', 'MN₮'],
'MXN': [2, '$', 'Mex$'],
'MYR': [2, 'RM', 'RM'],
'NOK': [18, 'kr', 'NOkr'],
'NOK': [50, 'kr', 'NOkr'],
'PAB': [2, 'B/.', 'B/.'],
'PEN': [2, 'S/.', 'S/.'],
'PHP': [2, '\u20B1', 'Php'],
'PKR': [0, 'Rs', 'PKRs.'],
'RUB': [42, 'руб.', 'руб.'],
'PLN': [50, 'z\u0142', 'z\u0142'],
'RON': [2, 'RON', 'RON'],
'RSD': [0, 'din', 'RSD'],
'RUB': [50, 'руб.', 'руб.'],
'SAR': [2, 'Rial', 'Rial'],
'SEK': [2, 'kr', 'kr'],
'SGD': [2, '$', 'S$'],
'THB': [2, '\u0e3f', 'THB'],
'TRY': [2, 'TL', 'YTL'],
'TWD': [2, 'NT$', 'NT$'],
'TZS': [0, 'TSh', 'TSh'],
'UAH': [2, '\u20B4', 'UAH'],
'USD': [2, '$', 'US$'],
'UYU': [2, '$', 'UY$'],
'UYU': [2, '$', '$U'],
'VND': [0, '\u20AB', 'VN\u20AB'],
'YER': [0, 'Rial', 'Rial'],
'ZAR': [2, 'R', 'ZAR']
@@ -309,16 +337,14 @@ goog.i18n.currency.CurrencyInfo = {
* @type {!Object.<!Array>}
*/
goog.i18n.currency.CurrencyInfoTier2 = {
'AFN': [16, 'Af.', 'AFN'],
'ALL': [0, 'Lek', 'Lek'],
'AFN': [48, 'Af.', 'AFN'],
'AMD': [0, 'Dram', 'dram'],
'AOA': [2, 'Kz', 'Kz'],
'ARS': [2, '$', 'AR$'],
'AWG': [2, 'Afl.', 'Afl.'],
'AZN': [2, 'man.', 'man.'],
'BAM': [18, 'KM', 'KM'],
'BAM': [2, 'KM', 'KM'],
'BBD': [2, '$', 'Bds$'],
'BGN': [2, 'lev', 'lev'],
'BHD': [3, 'din', 'din'],
'BIF': [0, 'FBu', 'FBu'],
'BMD': [2, '$', 'BD$'],
@@ -329,14 +355,12 @@ goog.i18n.currency.CurrencyInfoTier2 = {
'BWP': [2, 'P', 'pula'],
'BYR': [0, 'BYR', 'BYR'],
'BZD': [2, '$', 'BZ$'],
'CDF': [2, 'FrCD', 'CDF'],
'CUC': [1, '$', 'CUC$'],
'CUP': [2, '$', 'CU$'],
'CVE': [2, 'CVE', 'Esc'],
'DJF': [0, 'Fdj', 'Fdj'],
'DZD': [2, 'din', 'din'],
'ERN': [2, 'Nfk', 'Nfk'],
'ETB': [2, 'Birr', 'Birr'],
'FJD': [2, '$', 'FJ$'],
'FKP': [2, '£', 'FK£'],
'GEL': [2, 'GEL', 'GEL'],
@@ -347,12 +371,8 @@ goog.i18n.currency.CurrencyInfoTier2 = {
'GTQ': [2, 'Q', 'GTQ'],
'GYD': [0, '$', 'GY$'],
'HNL': [2, 'L', 'HNL'],
'HRK': [2, 'kn', 'kn'],
'HTG': [2, 'HTG', 'HTG'],
'HUF': [0, 'Ft', 'Ft'],
'IDR': [0, 'Rp', 'Rp'],
'IQD': [0, 'din', 'IQD'],
'IRR': [0, 'Rial', 'IRR'],
'JOD': [3, 'din', 'JOD'],
'KES': [2, 'Ksh', 'Ksh'],
'KGS': [2, 'KGS', 'KGS'],
@@ -366,8 +386,6 @@ goog.i18n.currency.CurrencyInfoTier2 = {
'LBP': [0, 'L£', 'LBP'],
'LRD': [2, '$', 'L$'],
'LSL': [2, 'LSL', 'LSL'],
'LTL': [2, 'Lt', 'Lt'],
'LVL': [2, 'Ls', 'Ls'],
'LYD': [3, 'din', 'LD'],
'MAD': [2, 'dh', 'MAD'],
'MDL': [2, 'MDL', 'MDL'],
@@ -386,11 +404,8 @@ goog.i18n.currency.CurrencyInfoTier2 = {
'NZD': [2, '$', 'NZ$'],
'OMR': [3, 'Rial', 'OMR'],
'PGK': [2, 'PGK', 'PGK'],
'PLN': [2, 'z\u0142', 'z\u0142'],
'PYG': [0, 'Gs', 'PYG'],
'QAR': [2, 'Rial', 'QR'],
'RON': [2, 'RON', 'RON'],
'RSD': [0, 'din', 'RSD'],
'RWF': [0, 'RF', 'RF'],
'SBD': [2, '$', 'SI$'],
'SCR': [2, 'SCR', 'SCR'],
@@ -400,16 +415,13 @@ goog.i18n.currency.CurrencyInfoTier2 = {
'SOS': [0, 'SOS', 'SOS'],
'SRD': [2, '$', 'SR$'],
'STD': [0, 'Db', 'Db'],
'SYP': [16, '£', 'SY£'],
'SYP': [0, '£', 'SY£'],
'SZL': [2, 'SZL', 'SZL'],
'TJS': [2, 'Som', 'TJS'],
'TND': [3, 'din', 'DT'],
'TOP': [2, 'T$', 'T$'],
'TTD': [2, '$', 'TT$'],
'TZS': [0, 'TSh', 'TSh'],
'UAH': [2, '\u20B4', 'UAH'],
'UGX': [0, 'UGX', 'UGX'],
'UYU': [1, '$', '$U'],
'UZS': [0, 'so\u02bcm', 'UZS'],
'VEF': [2, 'Bs', 'Bs'],
'VUV': [0, 'VUV', 'VUV'],
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -33,7 +33,7 @@ describe("convertDatetimeData", function() {
AMPMS: ['AM', 'PM'],
DATEFORMATS: ['a', 'b', 'c', 'd'],
TIMEFORMATS: ['e', 'f', 'g', 'h'] };
it('should convert empty datetime obj', function() {
var processedData = convert(dataObj);
expect(processedData.MONTH).toEqual(['Enero', 'Pebrero']);
+57 -9
View File
@@ -87,7 +87,11 @@ function pluralExtractor(content, localeInfo) {
continue;
}
var temp = goog.i18n.pluralRules.select.toString().
replace(/goog.i18n.pluralRules.Keyword/g, 'PLURAL_CATEGORY').replace(/\n/g, '');
replace(/goog\.i18n\.pluralRules\.Keyword/g, 'PLURAL_CATEGORY').
replace(/goog\.i18n\.pluralRules\.get_vf_/g, 'getVF').
replace(/goog\.i18n\.pluralRules\.get_wt_/g, 'getWT').
replace(/goog\.i18n\.pluralRules\.decimals_/g, 'getDecimals').
replace(/\n/g, '');
///@@ is a crazy place holder to be replaced before writing to file
localeInfo[localeIds[i]].pluralCat = "@@" + temp + "@@";
@@ -158,15 +162,39 @@ function outputLocale(localeInfo, localeID) {
}
localeObj.id = correctedLocaleId(localeID);
var prefix =
"'use strict';\n" +
'angular.module("ngLocale", [], ["$provide", function($provide) {\n' +
'var PLURAL_CATEGORY = {' +
'ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"' +
'};\n' +
'$provide.value("$locale", ';
var getDecimals = [
'function getDecimals(n) {',
' n = n + \'\';',
' var i = n.indexOf(\'.\');',
' return (i == -1) ? 0 : n.length - i - 1;',
'}', '', ''
].join('\n');
var suffix = ');\n}]);';
var getVF = [
'function getVF(n, opt_precision) {',
' var v = opt_precision;', '',
' if (undefined === v) {',
' v = Math.min(getDecimals(n), 3);',
' }', '',
' var base = Math.pow(10, v);',
' var f = ((n * base) | 0) % base;',
' return {v: v, f: f};',
'}', '', ''
].join('\n');
var getWT =
[
'function getWT(v, f) {',
' if (f === 0) {',
' return {w: 0, t: 0};',
' }', '',
' while ((f % 10) === 0) {',
' f /= 10;',
' v--;',
' }', '',
' return {w: v, t: f};',
'}', '', ''
].join('\n');
localeObj = {
DATETIME_FORMATS: localeObj.DATETIME_FORMATS,
@@ -176,6 +204,26 @@ function outputLocale(localeInfo, localeID) {
};
var content = serializeContent(localeInfo[localeID]);
if (content.indexOf('getVF(') < 0) {
getVF = '';
}
if (content.indexOf('getWT(') < 0) {
getWT = '';
}
if (!getVF && content.indexOf('getDecimals(') < 0) {
getDecimals = '';
}
var prefix =
"'use strict';\n" +
'angular.module("ngLocale", [], ["$provide", function($provide) {\n' +
'var PLURAL_CATEGORY = {' +
'ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"' +
'};\n' +
getDecimals + getVF + getWT +
'$provide.value("$locale", ';
var suffix = ');\n}]);';
return prefix + content + suffix;
}
+7 -5
View File
@@ -7,9 +7,11 @@ cd $BASE_DIR
set -x # Trace commands as they're executed.
I18N_BASE="https://raw.githubusercontent.com/google/closure-library/master/closure/goog/i18n"
# use the github repo as it is more up to date than the svn repo
curl https://closure-library.googlecode.com/git/closure/goog/i18n/currency.js > closure/currencySymbols.js
curl https://closure-library.googlecode.com/git/closure/goog/i18n/datetimesymbols.js > closure/datetimeSymbols.js
curl https://closure-library.googlecode.com/git/closure/goog/i18n/datetimesymbolsext.js > closure/datetimeSymbolsExt.js
curl https://closure-library.googlecode.com/git/closure/goog/i18n/numberformatsymbols.js > closure/numberSymbols.js
curl https://closure-library.googlecode.com/git/closure/goog/i18n/pluralrules.js > closure/pluralRules.js
curl "$I18N_BASE/currency.js" > closure/currencySymbols.js
curl "$I18N_BASE/datetimesymbols.js" > closure/datetimeSymbols.js
curl "$I18N_BASE/datetimesymbolsext.js" > closure/datetimeSymbolsExt.js
curl "$I18N_BASE/numberformatsymbols.js" > closure/numberSymbols.js
curl "$I18N_BASE/pluralrules.js" > closure/pluralRules.js
+4 -2
View File
@@ -15,7 +15,7 @@ module.exports = function(config, specificOptions) {
testName: specificOptions.testName || 'AngularJS',
startConnect: true,
options: {
'selenium-version': '2.37.0'
'selenium-version': '2.41.0'
}
},
@@ -32,7 +32,8 @@ module.exports = function(config, specificOptions) {
customLaunchers: {
'SL_Chrome': {
base: 'SauceLabs',
browserName: 'chrome'
browserName: 'chrome',
version: '34'
},
'SL_Firefox': {
base: 'SauceLabs',
@@ -112,6 +113,7 @@ module.exports = function(config, specificOptions) {
config.logLevel = config.LOG_DEBUG;
config.transports = ['websocket', 'xhr-polling'];
config.captureTimeout = 0; // rely on SL timeout
config.browserStack.build = buildLabel;
config.browserStack.startTunnel = false;
+1 -1
View File
@@ -35,7 +35,7 @@ module.exports = function(grunt) {
grunt.registerTask('docs', 'create angular docs', function(){
var gruntProc = shelljs.exec('node_modules/gulp/bin/gulp.js --gulpfile docs/gulpfile.js');
var gruntProc = shelljs.exec('"node_modules/.bin/gulp" --gulpfile docs/gulpfile.js');
if (gruntProc.code !== 0) {
throw new Error('doc generation failed');
}
+3 -3
View File
@@ -136,7 +136,7 @@ module.exports = {
.replace(/\\/g, '\\\\')
.replace(/'/g, "\\'")
.replace(/\r?\n/g, '\\n');
js = "!angular.$$csp() && angular.element(document).find('head').prepend('<style type=\"text/css\">" + css + "</style>');";
js = "!window.angular.$$csp() && window.angular.element(document).find('head').prepend('<style type=\"text/css\">" + css + "</style>');";
state.js.push(js);
return state;
@@ -149,9 +149,9 @@ module.exports = {
.replace(/"NG_VERSION_FULL"/g, NG_VERSION.full)
.replace(/"NG_VERSION_MAJOR"/, NG_VERSION.major)
.replace(/"NG_VERSION_MINOR"/, NG_VERSION.minor)
.replace(/"NG_VERSION_DOT"/, NG_VERSION.dot)
.replace(/"NG_VERSION_DOT"/, NG_VERSION.patch)
.replace(/"NG_VERSION_CDN"/, NG_VERSION.cdn)
.replace(/"NG_VERSION_CODENAME"/, NG_VERSION.codename);
.replace(/"NG_VERSION_CODENAME"/, NG_VERSION.codeName);
if (strict !== false) processed = this.singleStrict(processed, '\n\n', true);
return processed;
},

Some files were not shown because too many files have changed in this diff Show More