Compare commits

..

277 Commits

Author SHA1 Message Date
Matias Niemelä f7622dcc0d docs(CHANGELOG): add changes for 1.3.18 2015-08-18 15:14:56 -07:00
Matias Niemelä 2c03a35743 fix($animate): clear class animations cache if animation is not started
Closes #12604
Closes #12603
2015-08-17 17:09:00 -07:00
Matias Niemelä 6b72598b87 fix($animate): do not throw errors if element is removed before animation starts
Closes #10205
2015-08-17 17:06:54 -07:00
Rouven Weßling 51e24d75c3 refactor(): remove more bits and pieces related to Internet Explorer 8
Closes #12407
2015-08-08 18:08:47 +02:00
Martin Staffa 64a142b58e fix(ngModel): correct minErr usage for correct doc creation
Remove the `new` from the minErr assignment, so the closure compiler
can detect the errors correctly. Also removes the leading $ from the
variable name to be consistent with the Angular.js file.

Closes #12386
Closes #12416
2015-08-08 18:08:15 +02:00
Martin Staffa 0026ebf2de docs($rootScope.Scope): remove obsolete line, and link to guide
The removed line pointed to a removed example. Re-adding the example
would have been of questionable value, as it introduced several
concepts without context. It's therefore better to link to the guide,
which provides a better introduction.

Closes #12167
2015-08-03 22:07:59 +02:00
Eric Adams a4f73c9f99 docs(guide/Dependency Injection): fix angular.injector arguments list
The original file included a code sample using `angular.injector(['myModule', 'ng'])`,
which appears to be incorrect when trying to retrieve anything attached to `myModule`.
Reversing the args fixes this.

Closes #12292
2015-08-03 22:06:35 +02:00
Satish Maurya bd5c4e5f0e docs(guide/Forms): display scope form / master data in examples
It will be good to have the binding results in the CSS classes /
binding to form / control state example, similar to the Simple Form
example.

Closes #12326
2015-08-03 22:06:27 +02:00
Steven 9742565d61 docs(guide): Facebook was mispelled as Faceb0ok
Fixes typo :>

Closes #12470
2015-08-03 22:05:45 +02:00
Strikeskids 6f33dfa8cc docs($rootScope.Scope): improve clarity describing $watch with no listener
The previous explanation in parentheses created a bit of confusion because the documentation stated to leave off the `listener`, but then said "be prepared for multiple calls to your listener". The new explanation clarifies that it is indeed the `watchExpression` that will be executed multiple times.

Closes #12429
2015-08-03 22:05:36 +02:00
Laisky.Cai 96f0c8df17 docs(guide/expression): replace tt by code
Replaces <tt> elements with <code> in expressions guide. Looks identical
in Chromium

Closes #12437

Conflicts:
	docs/content/guide/expression.ngdoc
2015-08-03 22:04:40 +02:00
Martin Staffa 53fb534889 docs(ngOptions): remove obsolete trkslct error page
Closes #12417
2015-08-03 22:02:33 +02:00
Blake Johnston 6271ac064c docs($compile): pluralize DOM element
Previous description includes singular `collection of DOM element`. Current change revises `element` to be plural.

Closes #12431
2015-08-03 22:02:25 +02:00
ColinFletch 2d71b5b053 docs(guide/Controllers): Syntax adjustments.
Closes #12379
2015-08-03 22:02:13 +02:00
Jesse Mandel a547dff09b docs(guide/module): fixed link to blog post 2015-07-19 16:38:37 +02:00
Andrew Passanisi 3f9517ea5b docs(error/ctrlfmt): fixed a small typo in ctrl error message
Closes #12320
2015-07-16 22:45:47 +02:00
Mohamed Samy 8b4ffdde7a docs(tutorial/7 - Routing): fix matching in test
It is corrected in github, but not in the angular.org site.
Copied it from https://github.com/angular/angular-phonecat/compare/step-6...step-7

Closes #12314
2015-07-16 22:45:40 +02:00
Nabil Kadimi e57240d87d docs(guide/Dependency Injection): minor punctuation fixes
Closes #12268
2015-07-16 22:45:29 +02:00
shoja a51b4e6dc4 docs($sce): correct typos
Line 548: Remove duplicate 'not' and clarify wording
Line 556: Remove period within parenthetical statement
Line 560: Clarify wording
Line 570: Capitalize 'E.g.' at the start of a sentence

Closes #12252
2015-07-16 22:45:21 +02:00
Steve Mao 215be0b0ab docs(CONTRIBUTING): revert is a modifier
EG: https://github.com/angular/angular.js/commit/462f444b06ae5cad3ccb761b1dba7131df01a655

Closes #12032
2015-07-13 13:26:45 +01:00
Steve Mao 7c5880f998 docs(CONTRIBUTING): state what is mandatory or optional
Closes #12032
2015-07-13 13:25:45 +01:00
Steve Mao 0d941a986a docs(CONTRIBUTING): how to write a breaking change
Closes #12032
2015-07-13 13:25:45 +01:00
Peter Bacon Darwin 8714cabdb7 docs(guide/controller): add a line about controller as 2015-07-13 13:22:28 +01:00
Peter Bacon Darwin cbab2923ef docs(guide/controller): add a line about controller as 2015-07-13 13:20:28 +01:00
niteshthakur dba7e20e96 docs(guide/controller): clarify that controllers are defined **by** a constructor function
A controller is a instantiated object created **from** a constructor function.
It was not accurate to describe a Controller **as** a constructor function.

Closes #11888
2015-07-13 13:20:28 +01:00
Peter Bacon Darwin 24dd9ea649 docs($routeChangeSuccess): note that resolve values are available on current route
Closes #11413
2015-07-13 13:11:15 +01:00
Rouven Weßling 8bd59a593b refactor(ngCsp): use document.head
The `head` property is available from IE9 onwards.

Closes #11905
2015-07-13 10:07:52 +01:00
Martin Staffa 9a1349d2e0 docs(CHANGLOG): add changes for 1.3.17 2015-07-06 22:22:45 +02:00
Raphael Jamet 7e6155a6f1 refactor($templateRequest): Remove useless dependencies in tests 2015-07-01 12:16:14 -07:00
Raphael Jamet 0f034444c3 docs($templateRequest): update the description with caching changes
The previous changes to $templateRequest were not documented, they now are.
2015-07-01 12:16:03 -07:00
Raphael Jamet 74ecea9f2d refactor($templateRequest): move $sce checks and trust the cache
Move all the calls to $sce.getTrustedUrl inside $templateRequest, and
also trust the contents of the cache. This allows prefetching templates
and to bypass the checks on things where they make no sense, like
templates specified in script tags.

Closes #12219
Closes #12220
Closes #12240
2015-07-01 12:15:57 -07:00
Peter Bacon Darwin 8d5c08c6b8 chore(doc-gen): update to dgeni-packages 0.10.17
Make proper use of the new `git` package in dgeni-packages
2015-06-23 05:10:21 -07:00
Tsuyoshi Yoshizawa 0bb57d538f fix($location): allow navigating outside the original base URL
Previously, if you navigated outside of the current base URL angular
crashed with a `Cannot call method 'charAt' of undefined` error.

Closes #11302
Closes #4776
2015-06-19 18:41:47 +01:00
Peter Bacon Darwin 61a3fb676a fix($browser): prevent infinite digest if changing hash when there is no hashPrefix
The `window.location.hash' setter will strip of a single leading hash character
if it exists from the fragment  before joining the fragment value to the href
with a new hash character.

This meant that if we want the fragment to lead with a hash character, we
must do `window.location.hash = '##test'` to ensure that the first hash
character in the fragment is not lost.

The `$location.hash` setter works differently and assumes that the value
passed is the the full fragment, i.e. it does not attempt to strip off a
single leading hash character.

Previously, if you called, `$location.hash('#test')`, the leading hash was
being stripped and the resulting url fragment did not contain this hash:
`$location.hash()`, then became 'test' rather than `#test`, which led to
an infinite digest.

Closes #10423
Closes #12145
2015-06-17 14:02:31 +01:00
Peter Bacon Darwin f486ebe80b fix($location): do not get caught in infinite digest in IE9
Thanks to @hamfastgamgee for getting this fix in place.

Closes #11439
Closes #11675
Closes #11935
Closes #12083
2015-06-12 14:42:40 +01:00
Peter Bacon Darwin 03190fd896 chore(ngMock): check that ngMock.$browser.pollFns exists before emptying 2015-06-12 14:42:30 +01:00
Caitlin Potter 340e4da2eb test($location): ensure mock window can be wrapped by jqLite
Fixin da build

Closes #12086
2015-06-12 14:37:54 +01:00
Peter Bacon Darwin 46e4fa87fb test($locationSpec): refactor and clean up tests 2015-06-12 14:37:33 +01:00
Peter Bacon Darwin 45d43b9234 test($logSpec): don't pollute the global namespace with helpers 2015-06-11 14:32:35 +01:00
Conny Sjöblom 6b28aef1c5 fix(linky): allow case insensitive scheme detection
Closes #12073
Closes #12074
2015-06-11 12:16:07 +01:00
Matias Niemelä 5a1b4a06f4 docs(CHANGELOG): add changes for 1.3.16 2015-06-05 13:29:27 -07:00
Matias Niemelä 12f08c5e14 docs(CHANGELOG): update with 1.4.0 2015-06-05 13:19:37 -07:00
Chris Akers 706a93ab69 fix($cookies): update $cookies to prevent duplicate cookie writes and play nice with external code
Update the ngCookies service to prevent repetitive writes via $browser.cookies()
Also it is possible for $cookies to get confused about cookies modified outside of $cookies and
see those changes as user changes via the $cookies service which would then be set again. This
unnecessary setting of cookies can duplicate or overwrite depending on the original cookie's
domain. This update prevents that scenario.

Closes #11490
Closes #11515
2015-06-04 22:46:10 -07:00
Henry Zhu c4ae7d261a chore(jscs): remove .jscs.json.todo, rename config to .jscsrc
Closes #11993
2015-06-02 10:32:24 +01:00
Matias Niemelä 0adc036426 fix(core): ensure that multiple requests to requestAnimationFrame are buffered
IE11 (and maybe some other browsers) do not optimize multiple calls to
rAF. This code makes that happen internally within the $$rAF service
before the next frame kicks in.

Closes #11791
2015-06-01 07:42:05 -07:00
Ron Tsui 1ee58612a2 docs(README): improve unusual phrasing
Closes #11958
2015-06-01 08:01:53 +01:00
Yi EungJun ddc7f85493 docs(guide/Directives): use more standard data-ng-model in example
Use data-ng-model instead of data-ng:model which is accepted for legacy reason.
The next "Normalization" section is saying:

> Best Practice: Prefer using the dash-delimited format (e.g. ng-bind for
> ngBind). If you want to use an HTML validating tool, you can instead use the
> data-prefixed version (e.g. data-ng-bind for ngBind). The other forms
> shown above are accepted for legacy reasons but we advise you to avoid
> them.

Closes #11960
2015-06-01 07:59:26 +01:00
Daniel 90621a6c89 docs(numberFilter): update to match handling of null and undefined
This changed in 2ae10f67fc, where null and
undefined are passed through.

Closes #11963
2015-06-01 07:57:16 +01:00
Michael Watts 0c59599a45 docs(guide/Scopes): capitalisation of word scope
Closes #11970
2015-06-01 07:50:27 +01:00
Peter Bacon Darwin 4a4db1e7e6 docs(README.closure.md): clarify sentence
Closes #11979
2015-06-01 07:48:50 +01:00
pholly 10d8b010d3 docs(guide/Expressions): added special case for one-time binding of object literals under Value stabilization algorithm
One time binding of object literals are treated differently than simple expressions. Added a link to Ben Nadel's article describing how object literals's keys are checked for undefined.

Closes #11982
2015-06-01 07:42:46 +01:00
Matias Niemelä 3881831906 revert: fix(ngAnimate): throw an error if a callback is passed to animate methods
This reverts commit 39b078bad4.
2015-05-21 12:09:31 -07:00
Peter Bacon Darwin 9e3f82bbaf fix(select): prevent unknown option being added to select when bound to null property
If a select directive was bound, using ng-model, to a property with a value of null this would
result in an unknown option being added to the select element with the value "? object:null ?".
This change prevents a null value from adding an unknown option meaning that the extra option is
not added as a child of the select element.

Since select (without ngOptions) can only have string value options then `null` was never a
viable option value, so this is not a breaking change.

Closes #11872
Closes #11875
2015-05-18 22:42:16 +01:00
Peter Bacon Darwin 05156143c8 docs(error//nocb): add error doc for invalid parameter 2015-05-14 14:01:27 -07:00
Peter Bacon Darwin 39b078bad4 fix(ngAnimate): throw an error if a callback is passed to animate methods
As of bf0f550 (released in 1.3.0) it is no longer
valid to pass a callback to the following functions: `enter`, `move`, `leave`, `addClass`,
`removeClass`, `setClass` and `animate`.

To prevent confusing error messages, this change asserts that this parameter is
not a function.

Closes #11826
Closes #11713
2015-05-14 14:01:22 -07:00
Peter Bacon Darwin 9055b4e041 docs(CHANGELOG): update with 1.4.0-rc.2 2015-05-12 19:53:44 +01:00
Martin Staffa 6224a3eefb Revert "perf(ngStyleDirective): use $watchCollection"
This reverts commit 4c8d8ad508,
because it broke lazy one-time binding for object literals.

Closes #11613
2015-05-08 18:54:35 +02:00
Martin Staffa a45a34c261 test(ngStyle): ensure lazy one-time binding is supported
Closes #11405
2015-05-08 18:54:34 +02:00
Peter Bacon Darwin ceeeb6b4b1 docs(angular.element): clarify when jquery must be loaded for Angular to use it
Closes #3716
2015-05-05 20:03:44 +01:00
Nick Anderson cbc5f1c114 test(ngRepeat): fix test setup for ngRepeat stability test
The repeated template contained `{{key}}:{{val}}` but the repeat expression
was `"item in items"`, so `key` and `val` were not actually available.

The tests were passing anyway, since they did not rely upon the actual
text content of the template.

Closes #11761
2015-05-05 19:58:38 +01:00
Peter Bacon Darwin 4dfb80d96f docs($location): fix trailing whitespace
Closes #11741
Closes #11744
2015-05-05 19:54:35 +01:00
Damien Nozay 368039b881 docs($location): explain difference between $location.host() and location.host.
Closes #11741
Closes #11744
2015-05-05 19:52:10 +01:00
Rich Snapp 647f3f55eb fix(jqLite): check for "length" in obj in isArrayLike to prevent iOS8 JIT bug from surfacing
Closes #11508
2015-05-05 17:55:20 +01:00
Peter Bacon Darwin c8de0e425f docs($injector): add array annotation to all injectable parameters
Closes #11507
2015-05-05 15:05:03 +01:00
Kevin Brogan 9717c8fe1f docs($provide): add array annotation type to $provide.decorator parameter
The $provide.decorator function, as per the documentation, "is called using
the auto.injector.invoke method and is therefore fully injectable."

The current @param contradicts this by stating that only a functions may
be used as an argument.

Closes #11507
2015-05-05 15:04:53 +01:00
Martin Staffa fee437f9cf docs(changelog): wrap jqLite example containing html with code block
This prevents the markdown parser from garbling the input and putting
out broken html.

Closes #11778
Fixes #11777
Fixes #11539
2015-05-01 21:00:44 +01:00
Leonardo Braga 861b1a3d9d docs(ngModel): improve formatting of $modelValue
Closes #11483
2015-04-30 22:55:21 +02:00
Rodrigo Parra 72ff49f40f docs(ngSwitch): Replace tt tag with code tag
Use of tt is discouraged, see:
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tt
http://www.w3.org/wiki/HTML/Elements/tt

Closes #11509
2015-04-30 22:55:21 +02:00
Jeff Wesson 7a529992c8 docs(form): replace obsolete tt element
Removes the [**obsolete** HTML Teletype Text Element `<tt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tt)
and replaces it with [`<code>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code).
This adds more semanticity and is part of the [HTML5 specification](http://www.w3.org/TR/html5/text-level-semantics.html#the-code-element).

Closes #11570
2015-04-30 22:55:21 +02:00
Steve Mao e89d6829bc docs(ngCloak): remove information for ie7
IE7 is not supported. Also change `#template2` text to `'world'`.

Closes #11661
2015-04-30 22:55:20 +02:00
Ron Tsui 1b343e7bb6 style(docs): improve formatting in code comment
Closes #11674
2015-04-30 22:55:20 +02:00
thatType b3c022d672 docs(contribute): transpose "however" and "it's"
Transpose "however" and "it's" on line 156 for slightly better readability

Closes #11686
2015-04-30 22:55:20 +02:00
Georgios Kalpakas 9dd0fe35d1 fix(filterFilter): fix matching against null/undefined
Included fixes:

* Do not convert `null`/`undefined` to strings for substring matching in
  non-strict comparison mode. Prevents `null`/`undefined` from being matched
  against e.g. 'u'.
* Let `null` (as a top-level filter expression) match "deeply" (as do booleans,
  numbers and strings).
  E.g. let `filterFilter(arr, null)` match an item like `{someProp: null}`.

Fixes #11573

Closes #11617
2015-04-28 19:52:28 +02:00
Mike Calvanese 7560a8d2d6 fix(ngTouch): check undefined tagName for SVG event target
When target click element is an SVG, event.target.tagName and event.target.blur are undefined in Chrome v40 on iOS 8.1.3
2015-04-27 22:05:26 +01:00
gonengar c68357dbf8 docs(guide/Unit Testing): fixing the example for testing filter.
Hi there,
It seems that in the example which starts at line 256 there needs to
be an injection for $filter as in the previous example.

Closes #11410
2015-04-27 22:41:37 +02:00
Logesh Paul eaf6981499 docs(*): definition list readability improvement
Closes #11398
Closes #11187
2015-04-27 22:41:36 +02:00
Viktor Zozulyak 1cc24f3c4f docs(angular.injector): missing optional parameter mark
Closes #11528
2015-04-27 22:41:35 +02:00
yankee42 9e3e26328e docs(ngModel): use arguments.length instead of angular.isDefined(newName) to distinguish getter/setter usage
Closes #11604
2015-04-27 22:41:34 +02:00
Adam 82b2961868 docs(angular.element): css() api incompatibility.
"When a number is passed as the value, jQuery will convert it to a string and add px to the end of that string."
http://api.jquery.com/css/#css2

jqLite does not appear to do this.

I can submit if fix desired.

Closes #11614
2015-04-27 22:41:33 +02:00
Bruno Coelho 3cb10edca0 docs(guide/Scopes): remove unnecessary parenthesis
Closes #11645
2015-04-27 22:41:32 +02:00
Martin Staffa b64519fea7 fix(ngModel): allow setting model to NaN when asyncValidator is present
Closes #11315
Closes #11411
2015-04-03 07:12:39 +01:00
Peter Bacon Darwin 830c81d0f1 chore(dependencies): general update (including new dgeni-packages)
Closes #11095
2015-04-03 06:03:55 +01:00
Peter Bacon Darwin 66650bfb36 docs(toJson): improve option param documentation
With an upgrade to dgeni-packages 0.10.13, this style of optional param
is rendered more correctly.

See #11095
2015-04-03 06:03:44 +01:00
Peter Bacon Darwin 900b3a416c style($browserSpec): fix typo 2015-04-02 22:50:59 +01:00
Georgios Kalpakas f40252205e test(browerTrigger): ensure touch events initialize correctly on touch enabled Chrome
On certain browsers (e.g. on desktop Chrome with touch-events enabled),
using the `initTouchEvent()` method (introduced in 06a9f0a) did not
correctly initialize the event, nor did the event get dispatched on
the target element.

Using the `Event` constructor and manually attaching a `TouchList`,
works around the issue (although not a proper fix).

Fixes #11471
Closes #11493
2015-04-02 21:24:12 +01:00
Michał Gołębiowski 40441f6dfc fix(ngTouch): register touches properly when jQuery is used
If jQuery was used with Angular the touch logic was looking for touches
under the original event object. However, jQuery wraps all events, keeping
the original one under the originalEvent property and copies/normalizes some
of event properties. Not all properties are copied, e.g. touches which caused
them to not be recognized properly.

Thanks to @mcmar & @pomerantsev for original patch ideas.

Fixes #4001
Closes #8584
Closes #10797
Closes #11488
2015-04-02 14:06:08 +01:00
Michał Gołębiowski 1f65087126 feat(travis): run unit tests on iOS 8
Refs #11471
Closes #11479
2015-04-02 13:27:23 +01:00
Matias Niemelä d5c99ea42b fix(ngAnimate): ensure that minified repaint code isn't removed
Closes #9936
2015-03-31 14:04:00 -07:00
Peter Bacon Darwin bbc5fdde50 style(): remove unused property 2015-03-26 13:12:03 +00:00
Fred Sauer b5685e23f0 docs($route): add param info for $routeUpdate event
Closes #11419
2015-03-25 14:42:10 +00:00
Peter Bacon Darwin 5b26521de2 docs(filters): clarify filter name restrictions
See #10122
2015-03-23 11:59:38 +00:00
Martin Staffa abfbfd6c1c docs($compile): clarify link fn's controller argument
Also add "bindToController" to exampe directive definition object.

Closes #10815

Conflicts:
	src/ng/compile.js
2015-03-22 18:16:36 +01:00
Bradley Price a0e91c4ef7 docs($http): remove trailing comma
Remove trailing comma to keep the same flow with all other code examples on page.
2015-03-22 18:13:48 +01:00
wiseleo 3353fb84aa docs(orderBy): replace operator = with ===
Fix documentation error on line 20 incorrectly mentioning
an assignment operator in a comparison operation.
Code on line 235 uses strict comparison operator.

Closes #11392
Closes #11393
2015-03-22 18:13:48 +01:00
Yuvraj Patil 3a22c6461d docs(guide/Unit Testing): update Jasmine's description
Jasmine is a "behavior-driven development framework",
not a "test-driven development framework"

Closes #11383
2015-03-21 14:39:44 +01:00
Martin Staffa 54f5d82d4f docs(guide/scope): fix grammar
Closes #9829
2015-03-21 14:39:43 +01:00
Martin Staffa e80434c93f docs(guide/direcive): don't use shorthand in ddo
All the other examples use the full syntax.
Closes #11180
2015-03-21 14:39:43 +01:00
RaphStein 7dd5d7a523 docs(ngAria): change aria-live attribute value from polite to assertive
For ngMessages directive ngAria generates aria-live with value assertive and not polite.

Closes #11280
2015-03-21 14:39:43 +01:00
Wesley Cho 6198c0d9c0 docs($httpBackend): change to more friendly language
- Change s**t to more neutral word

Closes #11380
Closes #11364
2015-03-20 10:56:48 +00:00
Julie Ralph 1acde764d5 chore(ci): fix location of print logs from wait_for_browser_provider 2015-03-19 14:41:35 -07:00
Julie Ralph 3bdb39f667 chore(test): bump Protractor version to 2.0.0 2015-03-19 13:29:55 -07:00
Peter Bacon Darwin f231dda29d docs(input[number]): clarify that model must be of type number
The docs also now link through to the error doc, which contains a runnable
example of how to work around this restriction.

Closes #11157
Closes #11334
2015-03-19 14:35:16 +00:00
Peter Bacon Darwin 03f858ea5c chore(docs): improve error doc layout and linking
You can now link to an error by its name, namespace:name or error:namespace:name.
For example these would all link to https://docs.angularjs.org/error/$compile/ctreq

```
{@link ctreq}
{@link $compile:ctreq}
{@link error:$compile:ctreq}
```
2015-03-19 14:35:16 +00:00
Peter Bacon Darwin 4a26249946 docs(error/ngModel/numfmt): provide documentation for this error
See: https://github.com/angular/angular.js/commit/db044c408a7f8082758b96ab739348810c36e15a#commitcomment-7577199

Closes #11157
Closes #11334
2015-03-19 14:35:16 +00:00
Julie Ralph 06364c8cdc chore(ci): force travis to print logs after driver provider timeout
Travis does not do the after_script step if before_script fails,
so wait_for_browser_provider.sh was not printing out logs.
Force it to print them manually.
2015-03-17 16:58:19 -07:00
Martin Staffa 1c282af5ab fix(ngAria): handle elements with role="checkbox/menuitemcheckbox"
Fixes #11317
Closes #11321
2015-03-17 21:43:19 +00:00
Julie Ralph 45f006f6fb chore(ci): make wait_for_browser_provider time out after 2 minutes
Before, if something went wrong, wait_for_browser_provider.sh would
wait indefinitely, and logs would never get printed. Now, we'll bail
early, and get some actual logs on what the problem was.

Closes #11350
2015-03-17 20:40:22 +00:00
Peter Bacon Darwin 731e1f6534 fix($http): throw error if success and error methods do not receive a function
Closes #11330
Closes #11333
2015-03-17 19:14:28 +00:00
Wesley Cho 634e467172 fix($compile): throw error on invalid directive name
Directive names must start with a lower case letter.
Previously the compiler would quietly fail.
This change adds an assertion that fails if this is not the case.

Closes #11281
Closes #11109
2015-03-17 13:53:24 +00:00
Peter Bacon Darwin 2ca34a0cd3 docs(CHANGELOG): add changes for 1.4.0-beta.6 and 1.3.15 2015-03-17 12:08:27 +00:00
robiferentz 181e5ebc3f fix(jqLite): attr should ignore comment, text and attribute nodes
Follow jQuery handling of the `attr` function

Close #11038
2015-03-17 11:47:37 +00:00
svershin 874392464b docs(misc/Downloading): update o latest stable version
Updated the CDN link and description to 1.3.14

Closes #11327
2015-03-15 21:01:49 +00:00
rodyhaddad 7e7244402d chore(security): add warning banner to top of security sensitive files 2015-03-15 20:42:43 +00:00
Julie Ralph 4b94b9e34f chore(ci): bump sc version to 4.3.7 2015-03-13 09:36:28 -07:00
Izhaki a2e7f54320 docs(guide/directives): add some extra sub-headings for clarity
Added `Normalization` and `Directive types`` subheadings
to the `Matching directive` heading
2015-03-12 22:37:51 +01:00
Ciro Nunes 9b2e11b6fa docs($templateCache): highlight the $templateCache service
Closes #11294
2015-03-12 22:37:50 +01:00
Devyn Stott 2114a50c9a docs($log): Add debug button to example
Add debug button to example. It was in teh docs but not the example.

No breaking changes.
2015-03-12 22:37:49 +01:00
Marcin Wosinek 38f92c3a27 docs(ngMessage): move up ngMessages link 2015-03-12 22:37:47 +01:00
Amy 7dbf1ef2d1 docs(guide/Conceptual Overview): add a hyphen for clarity
Minor change, but the heading for "View independent business logic..."
would be more clear if it had a hyphen, "View-independent".
Perhaps it's because I'm new to javascript and its terminology,
but I read "View" as a verb rather than a noun on the first pass and
had to read on a bit to understand that it was, instead,
referring to The View. If you go just by grammar rules,
making "view independent" into the compound adjective,
"view-independent", makes it clear that it is modifying "business logic".
(http://www.grammarbook.com/punctuation/hyphens.asp - see Rule 1).

Conflicts:
	docs/content/guide/concepts.ngdoc
2015-03-12 22:37:46 +01:00
Peter Bacon Darwin e13aae1ed0 test(ngMock): test shallow copy of mock controller bindings
See #11239
2015-03-12 19:59:09 +00:00
Julie Ralph 36eacb172a chore(ci): turn off verbose logging for sauce connect
We have not used the verbose data from these logs for months,
and it makes the Travis UI very slow.
2015-03-11 10:55:02 -07:00
Peter Bacon Darwin f2683f956f fix(date filter): display localised era for G format codes
This implementation is limited to displaying only AD (CE) years correctly,
since we do not support the `u` style year format that can be used to represent
dates before 1 AD.

Closes #10503
Closes #11266
2015-03-11 12:19:00 +00:00
Peter Bacon Darwin 1a670aa466 chore(ngLocale): regenerate locale files to include ERA info 2015-03-11 12:18:42 +00:00
Peter Bacon Darwin 578425303f fix(ng/$locale): add ERA info in generic locale
This change also updates the closure i18n converter to pull in the ERA
info for generated locale files.
2015-03-11 12:18:17 +00:00
Rouven Weßling 528cf09e3f fix(rootScope): prevent memory leak when destroying scopes
Closes #11173
Closes #11169
2015-03-09 14:56:45 +00:00
bborowin c849098fbf docs($compile): clarify when require will throw
To make things less confusing, explicitly state that require
WILL NOT throw a compile error if a link function is not specified.

Closes #11206
stackoverflow.com/questions/28730346/require-ddo-option-of-angular-directive-does-not-throw-an-error-when-it-should
2015-03-08 20:42:39 +01:00
Edward Delaporte 145d397988 docs(orderBy): Start with a simpler example.
Per the question raised at this Stack Overflow question:
http://stackoverflow.com/questions/24048590/angularjs-ng-repeat-orderby-date-not-working

The first example on this page is too complex to convey
the simplest possible case for using this function.

Closes #11144
2015-03-08 18:54:40 +01:00
Elliot Bentley 26d4d0dc22 docs(ngDisabled): Clarify "incorrect" example
Add obvious label to example of incorrect usage.
To a user scanning the docs (ie. me) it's easy to miss the fact
that this top example doesn't actually work.

Closes #11192
2015-03-08 18:54:27 +01:00
jmarkevicius 64a9faaf8e docs(guide/Animations): change *then* to *than* 2015-03-08 14:00:46 +01:00
Anthony Zotti b7aba16839 docs(form): Add comma to line 319 for readability
Line 319 is hard to read on the first glimpse.

Currently the sentence reads:
"In Angular forms can be nested"

The sentence should read either
"In Angular, forms can be nested"
or
"Forms can be nested in angular"

I changed it to the former in this pull request.

Closes #11271
2015-03-08 14:00:46 +01:00
b0ri5 a72e1c4767 docs(tutorial/0 - Bootstrapping): Add a "the" before "imperative / manual way"
I'm assuming "imperative / manual" is modifying "way" in which case I think "the" is needed.

I don't really know grammar, but as a native speaker it sounds odd without "the".

Closes #11269
2015-03-08 14:00:46 +01:00
Martin Staffa 6545212d24 docs(tutorial/0 - Bootstrapping): clarify where the callback is registered
Closes #11270
2015-03-08 14:00:45 +01:00
Peter Bacon Darwin 3fabbdb804 docs(isNumber): fix link to isFinite 2015-03-06 11:58:11 +00:00
Peter Bacon Darwin ce8be9c47f docs(isNumber): add info about using isFinite to exclude NaN
Closes #11230
2015-03-06 11:36:26 +00:00
Caitlin Potter b3878a36d9 feat(ngMock): allow mock $controller service to set up controller bindings
Adds a new mock for the $controller service, in order to simplify testing using the
bindToController feature.

```js
var dictionaryOfControllerBindings = {
  data: [
    { id: 0, phone: '...', name: '...' },
    { id: 1, phone: '...', name: '...' },
  ]
};

// When the MyCtrl constructor is called, `this.data ~= dictionaryOfControllerBindings.data`
$controller(MyCtrl, myLocals, dictionaryOfControllerBindings);
```

Closes #9425
Closes #11239
2015-03-06 10:55:33 +00:00
gdi2290 ebd84e8008 fix($animate): applyStyles from options on leave
Closes #10068
2015-03-04 14:26:38 +00:00
Peter Bacon Darwin e721169738 chore(privateMocks): use global angular to access helpers in they
When using `they` in modules such as `ngMessages` we do not have access to
the internal helper functions.
2015-03-04 13:13:34 +00:00
Matias Niemelä cdfbe25c00 chore(privateMocks): replace multiple occurrences of $prop for they() 2015-03-04 12:28:07 +00:00
Peter Bacon Darwin 0d4b15a4c9 chore(gruntFile): add tthey and xthey to ddescribe-iit check 2015-03-04 12:28:07 +00:00
Peter Bacon Darwin 0dd061c239 style(privateMocks): remove unnecessary comment 2015-03-04 12:28:06 +00:00
Peter Bacon Darwin 7288be25a7 feat(ngMock): add they helpers for testing multiple specs
There are now three new test helpers: `they`, `tthey` and `xthey`, which
will create multiple `it`, `iit` and `xit` blocks, respectively, parameterized
by each item in a collection that is passed.

(with tests and ammendments by @petebacondarwin)

Closes #10864
2015-03-04 12:27:45 +00:00
Steve Mao 2b279dd8a1 docs(CONTRIBUTING): add whitespaces for consistent styling
Closes #11214
2015-03-04 12:26:46 +00:00
Casey Howard 63b9956faf fix(filterFilter): Fix filtering using an object expression when the filter value is undefined
Fixes #10419
Closes #10424
2015-03-02 22:19:14 +00:00
Caitlin Potter 92767c098f fix($browser): don't crash if history.state access causes error in IE
Reportedly, MSIE can throw under certain conditions when fetching this attribute.
We don't have a reliable reproduction for this but it doesn't do any real harm
to wrap access to this variable in a try-catch block.

Fixes #10367
Closes #10369
2015-03-02 20:30:06 +00:00
Brian Ford 2fe9b1dd9e docs(TRIAGING.md): improve process around PRs plz! label
Closes #10375
2015-03-02 19:43:46 +00:00
Marcy Sutton b9ad91cf1e feat(ngAria): add button role to ngClick
Closes #9254
Closes #10318
2015-03-02 13:49:17 +00:00
Peter Bacon Darwin 40752a520a test(aria): clean up test style and rename helper
Also removes unnecessary calls to `$apply`
2015-03-02 13:49:17 +00:00
Marcy Sutton 21369943fa feat(ngAria): add roles to custom inputs
This change adds the missing roles: `slider`, `radio`, `checkbox`

Closes #10012
Closes #10318
2015-03-02 13:49:17 +00:00
Jason Bedard 190ea883c5 fix(form): allow dynamic form names which initially evaluate to blank
Conflicts:
	src/ng/directive/form.js

Closes #11096
2015-03-01 16:35:04 +01:00
Josh Kramer 3a093123ef docs(ngModel): fix contenteditable description
contenteditable is supported in many more browsers than Angular itself is.

http://caniuse.com/#feat=contenteditable

Closes #11172
2015-02-28 18:05:42 +01:00
Pawel Kozlowski b8e8f9af78 fix(Angular): properly compare RegExp with other objects for equality
Fixes #11204

Closes #11205
2015-02-28 10:58:41 +01:00
Dav 01161a0e9f fix(filterFilter): do not throw an error if property is null when comparing objects
Closes #10991
Closes #10992
Closes #11116
2015-02-27 21:47:46 +00:00
Adam Bradley 75abbd525f fix(templateRequest): avoid throwing syntax error in Android 2.3
Android 2.3 throws an `Uncaught SyntaxError: Unexpected token finally`
pointing at this line. Change `.finally` to bracket notation.

Fixes #11089
Closes #11051
Closes #11088
2015-02-25 16:40:19 +00:00
Julie Ralph 01a725a769 chore(ci): update Karma to 0.12.32-beta.0
This will hopefully make the CI more stable because of its updated
version of socket.io.
2015-02-24 13:16:06 -08:00
Peter Bacon Darwin f5781dbb60 docs(CHANGELOG): add changes for 1.4.0-beta.5 and 1.3.14 2015-02-24 17:52:09 +00:00
Peter Bacon Darwin 59205d7809 chore(bower/publish): run local precommit script if available
Closes #11164
2015-02-24 17:22:45 +00:00
Martin Staffa 0cf170df16 chore(grunt): use path.normalize in grunt shell:npm-install
This makes the command runnable on Windows clients.
2015-02-23 20:51:37 +01:00
Tero Parviainen 05a3b088fd docs(ngRepeat): extend description of tracking and duplicates
Add a section to the documentation on how tracking between items and DOM
elements is done, and why duplicates are not allowed in the collection.

Describe how the default tracking behaviour can be substituted with track by.

Tweak the wording in the `track by` section to discuss “tracking expressions”
instead of “tracking functions”.

Closes #8153
2015-02-23 20:17:09 +01:00
Martin Staffa 65df5da07a docs(ngDisabled): clarify the explanation of attributes & interpolation
Closes #11032
Closes #11133
2015-02-22 22:17:22 +01:00
Igor Minar 0689c3697f chore(npm): update dependencies 2015-02-21 19:03:50 -08:00
Igor Minar 0f53c29954 chore(travis,grunt): extract the npm install and cache busting logic into install-dependencies.sh
So now we are DRY.

Added extra error checking and improved the grunt file init setup so that stdio is visible in console.

Closes #11110
2015-02-21 18:55:19 -08:00
Igor Minar 11bfe85598 chore(grunt): blow away cached node_modules when npm-shrinkwrap.json changes
this replicates the travis setup in grunt from the previous commit

the reason why we duplicate this rather than having just a single place for this code is so that
we can individually time the actions on travis
2015-02-21 18:55:07 -08:00
Igor Minar 93c503fa16 chore(travis): don't break the build when travis cache is empty
`du` returns error code 2 when any of the directories don't exist which breaks the build.

this scenario is common when the cache was emptied or when travis is building forks that don't have travis cache enabled
2015-02-21 18:54:55 -08:00
Igor Minar 9d4e948e82 chore(travis): blow away cached node_modules when npm-shrinkwrap.json changes
`npm install` blindly accepts the node_modules cache and doesn't verify if it matches requirements in the current npm-shrinkwrap.json.

This means that if we are using travis cache and npm-shrinkwrap.json changes npm will keep on using the old dependencies, in spite of the guarantees that shrinkwrap claims to offer.

https://github.com/angular/angular.js/pull/11110#issuecomment-75302946

With this change, we will blow away the node_modules directory if the shrinkwrap changes compared to the one
used to populate node_modules.
2015-02-21 18:54:24 -08:00
Igor Minar 26f19d0399 chore(karma): use karma 0.12.31 instead of a custom fork
Previously Vojta set us up to use a custom fork of Karma that used socket.io 1.3.4. This change moves us to an official release of Karma but downgrades socket.io back to 0.9.16.

We now need to hurry up and finish the socket.io upgrade in karma which was blocked on shrinkwrap issues in Angular that are resolved with the previous few commits in this PR.

Conflicts:
	npm-shrinkwrap.clean.json
	npm-shrinkwrap.json
2015-02-21 18:54:08 -08:00
Igor Minar 5cd2e2291b chore(clean-shrinkwrap): drop from property from the clean shrinkwrap
it usually contains urls to temp directories which are not interesting, the info
we do want to preserve is in the `resolved` property and we do keep that one.

Conflicts:
	npm-shrinkwrap.clean.json
2015-02-21 18:41:10 -08:00
Igor Minar 69bbbe675e chore(clean-shrinkwrap): preserve git+https:// resolved property
previously we thought that git:// was enough, but we also want git+https:// otherwise we can miss important info
in clean shrinkwrap file

Conflicts:
	npm-shrinkwrap.clean.json
2015-02-21 18:40:11 -08:00
Igor Minar 825de1cdf2 chore(npm/travis): upgrade to npm 2.5 and require it via package.json
currently karma's dependencies don't install on node 0.12 or io.js so we'll just update npm in hope that that
this will mitigate "cb() never called!" erorrs. See: https://travis-ci.org/angular/angular.js/jobs/51474043#L2181
2015-02-21 18:40:11 -08:00
Igor Minar e5318c61ee chore(npm): don't clean npm-shrinkwrap.json instead generate npm-shrinkwrap.clean.json
Previously we would clean up npm-shrinkwarp.json file in order to achieve serialization
stability, which would then allow us to create human readable diffs that allow code reviews
of npm-shrinkwrap to be meaningful.

This cleanup process does have an impact on the functionality of npm which was only recently
discovered by Vojta, when we tried to update to new Karma version. See: Automattic/engine.io-client#370

According to Julie, the root cause of these issues is npm/npm/#3581.

The workaround implemented in this commit is not to interfere with npm-shrinkwrap.json file, but instead
preserve the cleaned up version of its content in npm-shrinkwrap.clean.json which can then be used to
produce human readable diffs for code reviews of npm dependency updates.
2015-02-21 18:37:56 -08:00
Igor Minar 67b40a19fb chore(travis): turn on caching for node_modules and bower_components directories
The cache behavior is documented at http://docs.travis-ci.com/user/caching/

This commit also disabled our custom caching via npm-bundler-deps.sh
2015-02-21 18:36:37 -08:00
Martin Staffa 5fbac749c9 docs($resource): fix list level
Closes #11055
2015-02-13 23:54:11 +01:00
Julie Ralph 0daadeb3d3 chore(ci): make all browserstack tests allowed failures 2015-02-13 10:20:34 -08:00
Peter Bacon Darwin 6b7625a095 fix(ngModel): fix issues when parserName is same as validator key
For $validate(), it is necessary to store the parseError state
in the controller. Otherwise, if the parser name equals a validator
key, $validate() will assume a parse error occured if the validator
is invalid.

Also, setting the validity for the parser now happens after setting
validity for the validator key. Otherwise, the parse key is set,
and then immediately afterwards the validator key is unset
(because parse errors remove all other validations).

Fixes #10698
Closes #10850
Closes #11046
2015-02-13 12:34:44 +00:00
Rouven Weßling ab7e0cd100 refactor(ngSanitize): remove workarounds for IE8
Closes #10758
2015-02-13 11:52:05 +00:00
Peter Bacon Darwin dee5f7fea5 test(ngSanitize): add tests for decodeEntities 2015-02-13 11:52:05 +00:00
Julie Ralph 140d149cee chore(ci): update to use the latest sauce connect (4.3 to 4.3.6) 2015-02-12 13:19:42 -08:00
Peter Bacon Darwin 4d65ddddf8 docs(FAQ): update the zipped file size 2015-02-12 11:59:53 +00:00
Tobyee abfce5327c fix(input): create max and/or min validator regardless of initial value
Also adds corresponding tests for ngMin / ngMax.

Fixes #10307
Closes #10327
2015-02-10 23:39:49 +01:00
Martin Staffa 944c150e6c fix(ngAria): correctly set "checked" attr for checkboxes and radios
Make sure the checked attribute is set correctly for:
- checkboxes with string and integer models using ngTrueValue /
ngFalseValue
- radios with integer models
- radios with boolean models using ngValue

Fixes #10389
Fixes #10212
2015-02-10 22:35:36 +01:00
Peter Bacon Darwin d8dc53d215 chore(CHANGELOG): add release name! 2015-02-09 11:16:46 +00:00
Peter Bacon Darwin 0ec206f5a6 chore(CHANGELOG): update to 1.4.0-beta.4 and 1.3.13 2015-02-09 10:39:03 +00:00
Lucas Galfaso ce49d4d61b fix(sanitize): handle newline characters inside <script> for IE
Tweak the regex used match characters inside <script> and <style> to a IE
compatible regex.
2015-02-07 19:21:53 +01:00
Marcy Sutton 69ee593fd2 fix(ngAria): ensure native controls fire a single click
Closes #10388
Closes #10766
2015-02-07 10:27:29 +00:00
Shahar Talmi 39ddef6829 fix(ngMock): handle cases where injector is created before tests
This caused an exception for people who created an injector before the tests actually began to run. Since the array was initialized only in beforeEach, anyone accessing it before that would throw. This is solved easily but initializing the array immediately.

Closes #10967
2015-02-06 03:47:32 +02:00
Lucas Galfaso 11aedbd741 fix(sanitize): handle newline characters inside special tags
Handle newlines characters when they are present inside <script> and <style> tags.

Closes #10943
2015-02-04 15:28:49 +01:00
Lukas Elmer e4d1e12f7f chore(docs): fix nav scrolling for non-mobile screens
Closes #10936
2015-02-04 13:37:28 +00:00
Morris Singer 9c2b32d6f3 docs(guide/forms): improve wording
Closes #01937
2015-02-04 13:17:07 +00:00
dtritus 4b3a590b00 fix($location): prevent page reload if initial url has empty hash at the end
If initial url has empty hash at the end, $location replaces it with url
without hash causing an unwanted page reload.

Closes #10397
Closes #10960
2015-02-04 12:49:20 +00:00
Peter Bacon Darwin 7f50e97628 docs(CHANGELOG): update changelog for 1.4.0-beta.3 and 1.3.12 2015-02-03 20:53:22 +00:00
Peter Bacon Darwin e5ee6123fc docs(CHANGELOG): update changelog for 1.4.0-beta.3 and 1.3.12 2015-02-02 20:26:52 +00:00
Chris 923b6aba0d docs(guide): update "AngularJS" book link
Added latest 2014 revision "AngularJS: Up and Running" with Amazon link.
"AngularJS" was previous 2013 version.

Closes #10920
2015-02-02 14:03:17 +00:00
Hannah Howard 955e20eb61 fix ($compile): keep prototype properties for template URL directives
Previously, if a directive definition object was defined with methods like `compile`
provided on the prototype rather than the instance, the Angular compiler failed
to use these methods when the directive had a `templateURL`. This change ensures
that these prototypical methods are not lost.

This enables developers to define their directives using "classes" such as
in CoffeeScript or ES6.

Closes #10926
2015-02-02 12:01:31 +00:00
Henry Zhu 286a40751c style(*): add jscs rule disallowKeywordsOnNewLine: "else" 2015-01-30 12:47:37 +00:00
Henry Zhu eca0535457 style(*): add jscs rule requireSpaceBeforeKeywords
Closes #10772
2015-01-30 12:47:37 +00:00
Henry Zhu 7ace77a5d7 style(*): add jscs rule requireSpacesInForStatement
Closes #10772
2015-01-30 12:47:36 +00:00
Henry Zhu 7f362af153 style(*): add jscs rule disallowSpacesInCallExpression
Closes #10772
2015-01-30 12:47:36 +00:00
Henry Zhu 35dee2abac style(*): add jscs rule disallowKeywordsOnNewLine: "else"
Closes #10772
2015-01-30 12:47:36 +00:00
Peter Bacon Darwin 29c926201d chore(npm): update grunt-jscs to 1.2.0 (jscs to 1.10.0), fix styles
Closes #10772
2015-01-30 12:47:36 +00:00
Peter Bacon Darwin 9b6852a8c9 chore(doc-gen): update to dgeni-packages 0.10.8
Closes https://github.com/angular/dgeni-packages/pull/105
2015-01-30 11:03:39 +00:00
Rohit Kandhal 53efc8d5d0 docs(tutorial/step-11): update link to Jasmine matchers
Closes #10909
2015-01-29 23:01:34 +00:00
Martin Staffa 0b8461c9cb test(validators): minlength and required must use viewValue in $isEmpty 2015-01-29 23:30:29 +01:00
Martin Staffa abd8e2a9eb fix(validators): maxlength should use viewValue for $isEmpty
Closes #10898
2015-01-29 23:30:28 +01:00
Ian Young bc4dadc894 docs(ngHide): use proper selector when overriding the CSS
The animation selector gives the default styles greater specificity that
should be matched when overriding.

Closes #10902
Closes #10913
2015-01-29 22:13:21 +00:00
Caitlin Potter ec53089bb1 chore($controller): don't use new for minErr instance
minErr creates a new error anyways, it's not meant to be called as a constructor.
2015-01-29 16:27:57 -05:00
Martin Staffa 7bb50e2823 docs(guide/production): clarification in disabling debug data
Closes #10762
2015-01-29 20:01:40 +01:00
Caitlin Potter 632b2ddd34 fix($controller): throw better error when controller expression is bad
Previously, the error was a JS runtime error when trying to access a property of `null`. But, it's
a bit nicer to throw a real error and provide a description of how to fix it. Developer ergonomics
and all that.

Closes #10875
Closes #10910
2015-01-29 12:52:45 -05:00
Vojta Jina 7caad2205a fix($parse): remove references to last arguments to a fn call
This can be an issue if running (and killing) multiple apps/injectors on
the same page. The `args` array holds references to all previous arguments
to a function call and thus they cannot be garbage-collected.

In a regular (one app/injector on a page) app, this is not an issue.

Closes #10894
2015-01-29 14:53:54 +00:00
Wes Alvaro 9bf5f89659 routeParams are {!Object<string, string>}
Calling Object.keys on null is a TypeError. And the mapping should be string -> string.

Closes #10896
2015-01-29 14:40:02 +00:00
Owen Smith f41ca4a53e fix(ngRoute): dont duplicate optional params into query
When calling updateParams with properties which were optional, but
previously undefined, they would be duplicated into the query params as
well as into the path.

Closes #10689
2015-01-29 10:36:03 +00:00
marc 0bcd0872d8 fix(ngScenario): Allow ngScenario to handle lazy-loaded and manually bootstrapped applications
I know protractor is preferred, and ngScenario is only in maintenance mode. But, we are limited to
ngScenario based on the devices/browsers we are targeting (no web-driver available). So, we need
to address the bug where ngScenario does not work with manual bootstrap and also has issues if
angular.resumeBootstrap is not yet defined (race condition when lazy-loading).

Closes #10723
2015-01-29 10:15:54 +00:00
Shahar Talmi 6ec5946094 feat(ngMocks): cleanup $inject annotations after each test
this will help in detecting unannotated functions both in apps and angular core in case only part of the tests are run with strictDi
2015-01-28 14:09:41 +00:00
Agrumas 784ea8e160 chore(i18n): regenerate locales due to closure library update
This includes change to the currency symbol of Lithuania.

Closes #10855
Closes #10856
2015-01-27 10:31:37 +00:00
Peter Bacon Darwin 6ec53bdfd3 chore(i18n): update closure library to latest
This includes changed to Lithuanian currency and Mynamar Burmese date formats

Closes #10855
Closes #10856
2015-01-27 10:31:37 +00:00
Caitlin Potter d1b6480dcf docs(CHANGELOG.md): remove reverted form change 2015-01-26 18:58:27 -05:00
Jeff Cross ef6fed3ef8 revert: fix(form): ignore properties in $error prototype chain
This reverts commit adf91fe6ee.
2015-01-26 14:20:52 -08:00
Caitlin Potter 473dee5786 docs(CHANGELOG.md): add changelog notes for v1.3.11 and v1.4.0-beta.2
Closes #10876
2015-01-26 16:12:24 -05:00
Caitlin Potter 779e3f6b5f fix(htmlAnchorDirective): remove "element !== target element" check
It's not really needed due to the way click events are dispatched and propagated

Closes #10866
2015-01-25 23:42:25 -05:00
Vinti Maheshwari 71bca00651 chore(gruntFile): ensure build is run before test:modules
Closes #10188
2015-01-25 02:33:29 +00:00
Peter Bacon Darwin 9a9fce0abc test($location): ensure that link rewriting is actually being tested
If the link URL is not within the given base URL then the link would not
be rewritten anyway.

See https://github.com/angular/angular.js/pull/9906/files#r19813651
2015-01-25 00:40:57 +00:00
Peter Bacon Darwin 939ca37cfe fix($location): don't rewrite when link is shift-clicked
Closes #9904
Closes #9906
2015-01-25 00:36:44 +00:00
Peter Bacon Darwin 4ae8a2a4b6 docs(input): update example to use ngModel best practices
Update the rest of the directives to use object properties for models.

Closes #10851
2015-01-24 22:42:50 +00:00
Mark Hoffmeyer 113d3954b9 docs(input[checkbox]): update example to use ngModel best practices
It's not required for the example to function, but it prevents scope weirdness/unexpected
behavior when using directives (especially with ngTransclude!). I think it's a good pattern
to encourage and might prevent a bug down the road for for people who just scan for the
monospace font. See
[Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
for mgModel best practices.

Closes #10851
2015-01-24 22:41:55 +00:00
samilamti 7cb5983750 docs(guide/Introduction): define CRUD and add punctuation
Added description of what CRUD means.
Improved readability: Ensured that colons were followed by a capital letter
and added some sprinkled commas.

Closes #10804
2015-01-24 10:57:53 +00:00
Caitlin Potter 2b149ca6d4 fix(openPlunkr): enable cmd+click for us mac users :> 2015-01-23 16:49:00 -05:00
Mike Haggerty e77866c18c feat(openPlunkr): enable ctrl+click
This change allows users to ctrl+click on the "Edit in Plunker"
button which will set the posted form's target attribute to
"_blank" instead of "_self" which is the default.

Closes #10641
Closes #10826
2015-01-23 16:49:00 -05:00
Rouven Weßling 0dc6418d20 test($rootScope) test the correct setting of the constructor in Internet Explorer 11
Closes #10759
2015-01-23 21:33:15 +00:00
Caitlin Potter 837a077578 fix(htmlAnchorDirective): don't add event listener if replaced, ignore event if target is different element
Previously, when an `a` tag element used a directive with a replacing template, and did not include an `href` or `name` attribute
before linkage, the anchor directive would always prevent default.

Now, the anchor directive will not register an event listener at all if the original directive is replaced with a non-anchor, and
will ignore events which do not target the linked element.

Closes #4262
Closes #10849
2015-01-23 15:40:23 -05:00
Caitlin Potter adf91fe6ee fix(form): ignore properties in $error prototype chain
Closes #10469
Closes #10727
2015-01-23 14:37:48 +00:00
Boshen Chen dea1c0d34c docs(ngInit): fix code block not being displayed in the note section
Closes #10791
2015-01-20 23:23:43 +01:00
Pawel Kozlowski f533acc9aa docs(CHANGELOG): add changes for 1.3.10 and 1.4.0-beta.1 2015-01-20 20:48:20 +01:00
Alexandr Subbotin d01cae2a0d fix(ngRepeat) do not allow $id and $root as aliases
Currently user can use `$id` or `$root` as alias in ng-repeat directive that leads to rewriting
these scope-related variables. This commit fixes this behavior by throwing an error when user try
to use these values.

Closes #10778
2015-01-20 19:31:56 +01:00
Pawel Kozlowski d015c8a80b fix(ngController): allow bound constructor fns as controllers
Fixes #10784
Closes #10790
2015-01-20 19:31:47 +01:00
Alexandr Subbotin 8d2717146b refactor($templateRequest): remove repeated decrementation and unnecessary local variable
Closes #10780
2015-01-20 19:31:35 +01:00
Pawel Kozlowski 3d598dae64 docs(ngClass): fix jscs style errors 2015-01-20 19:31:23 +01:00
Evan Spiler 0a58986f52 docs(ngClass): fix formatting
Closes #10793
2015-01-20 19:31:12 +01:00
thorn0 6e69b85f9a docs(angular.bootstrap): passed fns are called on config stage
Closes #10789
2015-01-20 19:31:02 +01:00
Caitlin Potter 7a9e336028 fix($compile): support class directives on SVG elements
Closes #10736
Closes #10756
2015-01-20 19:18:33 +01:00
Matias Niemelä 9b8df52aa9 fix($animate): ensure no transitions are applied when an empty inline style object is provided
Closes #10613
Closes #10770
2015-01-19 14:38:24 +00:00
Peter Bacon Darwin 7fab29fbe1 docs(ngRepeat): document the sorting of object keys
See #6210
2015-01-19 09:18:53 +00:00
Peter Bacon Darwin 1a47fcbb8b chore(travis): update browsers to the latest version
Update the used browsers to the latest versions available

Closes #10620
2015-01-19 08:50:39 +00:00
Peter Bacon Darwin ffd4dab611 test(privateMocks): fix for the latest version of Safari 2015-01-19 08:50:39 +00:00
Martin Staffa 13edaa95c7 test(form): test if $pending inputs are correctly removed
It's a separate test because $pending behaves differently from $error
- the property is completely removed when no pending inputs / forms
are left.
2015-01-16 21:50:24 +01:00
Martin Staffa 47b1f54bba refactor(ngModel): clarify the arguments of $setValidity 2015-01-16 21:50:23 +01:00
Martin Staffa cdc7280dd3 fix(form): clean up success state of controls when they are removed
Fixes #10509
2015-01-16 21:50:23 +01:00
Martin Staffa 0bb282bc6d docs(migration): in 1.3, global controllers are disabled by default
Closes #10775
2015-01-16 21:35:21 +01:00
Sekib Omazic fdb09ef858 docs($rootScope.Scope): Simple typo
Closes 10767
2015-01-16 21:35:21 +01:00
Sekib Omazic 5e69cb2f9f docs(ngInclude): Typo fixed
Typo fixed
2015-01-16 21:35:21 +01:00
Sekib Omazic 5c2da38e3f docs(ngMessages): --typos.length
Merci~

Closes #10769
2015-01-15 15:46:58 -05:00
Marcin Wosinek 3a799df0f1 docs(design): highlight source button when focused 2015-01-15 14:01:21 +00:00
Kiran Rao 511c765a44 docs(CONTRIBUTING): add colons for consistent styling
Closes #10744
2015-01-15 13:54:53 +00:00
Martin Mouterde bf55d76d27 docs(date): fix milliseconds syntax description
There is no need to prefix 'sss' with ',' or '.'.

Closes #10680
2015-01-15 13:42:42 +00:00
anyong c9efc80cd0 docs(tutorial/0): remind users to refresh page
On line 32-34 after reverting to step-0 and starting the webserver, the
browser may have already cached the master branch of the app and the user
will see the master version in their browser. I just added a reminder to
tell them to refresh the page if this happens!

Closes #10615
2015-01-15 13:16:14 +00:00
Kok-Hou Chia fa15f2a6df docs(tutorial/7): correct typos
Closes #10587
2015-01-15 13:16:14 +00:00
Tyler Morgan c023a0bfbb docs(guide/Directives): demonstrate how to pass data from isolate to parent scope
It looks like this used to be in the Angular docs as per this thread:
https://groups.google.com/d/msg/angular/3CHdR_THaNw/AxqKwUw5t0oJ. I recently
spent some time trying to get this to work and was very frustrated by lack of
documentation.

Closes #10567
2015-01-15 13:16:14 +00:00
Olivier Giulieri b470e005e8 docs(css): fix position and size of Table of Contents "close" button
Closes #10555
2015-01-15 13:16:14 +00:00
Jonathan Gruber c959191882 docs(tutorial/step_10): Added missing semicolon
Added a missing semicolon in definition of $scope.setImage.

Closes #10752
2015-01-14 09:42:58 -05:00
Caitlin Potter e4adebd07a revert: chore(npm): Make require()-able as part of publish script
This reverts commit c5686c5271.

(We wanted to get some feedback before doin this)
2015-01-13 14:29:29 -05:00
Peter Bacon Darwin ac94f6125f docs(CHANGELOG): add changes for 1.3.9 and 1.4.0-beta.0 2015-01-13 00:58:04 +00:00
Ben Clinkinbeard c5686c5271 chore(npm): Make require()-able as part of publish script
(This has not been tested locally with browserify --- but it should work!
If it doesn't, please file a bug rather than just leaving a comment on this
commit :)

Closes #10731
2015-01-12 19:09:46 -05:00
Julie Ralph 7fdb54d12b chore(testing): bump protractor to version 1.6.0 2015-01-12 11:47:47 -08:00
Jason Bedard 869008140a fix($parse): allow use of locals in assignments Fixes #4664 2015-01-12 13:38:34 +00:00
Julie Ralph 26ee32ec79 chore(travis): split out the docs e2e tests into their own travis job
Previously, they were in the 'unit' job to save travis VMs, but this
was confusing and made it more difficult to track down errors easily.
2015-01-09 14:29:26 -08:00
Julie Ralph a06193f97b chore(travis): make browserstack unit tests allowed failures 2015-01-09 10:45:23 -08:00
Uri Goldshtein ba9dee170c docs(guide/index): add angular-easyfb with Facebook login to login libraries
Merci~

Closes #5792
2015-01-06 13:41:45 -05:00
Andrey Pushkarev d4b60ada1e fix(filterFilter): use isArray() to determine array type
In JavaScript, an array is a special type of object, therefore typeof [] returns object.
Added corresponding unit tests.

Changed condition for array type to isArray.

Closes #10621
2015-01-02 13:26:51 -05:00
Rus1 b839f73ad0 docs(ngInclude): replace <tt> with <code>
Using obsolete <tt> HTML tag may not be good for Angular examples

Closes #10594
2014-12-30 15:48:19 -05:00
Peter Bacon Darwin aee32931fd test(input): split tests into smaller files
This is complement to the previous commit.
It also refactors the input compile helpers to make it cleaner and more
consistent.
2014-12-24 23:26:34 +00:00
Peter Bacon Darwin 7ee5f46bbc refact(input): split input.js into smaller files
The input.js file is unnecessarily large, containing many directives including the
vast `ngModel`. This change moves ngModel and a few other directives into their
own files, which will make maintenance easier.
2014-12-24 13:01:03 +00:00
David Souther 2b97854bf4 feat(ngMock/$exceptionHandler): log errors when rethrowing
Now the `rethrow` mode will also record a log of the error in the same
way as the `log` mode.

Closes #10540
Closes #10564
2014-12-23 18:35:49 +00:00
David Souther 316ee8f7ca test($exceptionHandlerProvider): call inject() to run tests
In the current angular-mocksSpec, the tests for $exceptionHandlerProvider
call `module` to run tests on `$exceptionHandlerProvider.mode()`, but do
not call `inject()` to pump the module definitions.

Closes #10563
2014-12-23 18:11:21 +00:00
gokulkrishh 2e18f44fcd docs(guide/*): spelling/grammar improvements
Closes #10552
2014-12-22 10:44:54 -05:00
Caitlin Potter c85d064ecf docs($rootScope): remove erroneous closing parenthesis
Closes #10549
2014-12-22 08:34:29 -05:00
Kevin Primat 7b9b82281a docs(guide/location): add missing definite article
The sentence was missing a definite article so was unclear. Added one to clarify.

Closes #10547
2014-12-22 08:25:24 -05:00
Olivier Giulieri aab632b330 docs(guide): fix spaces
Closes #10539
2014-12-22 01:08:06 +00:00
gdi2290 4c8d8ad508 perf(ngStyleDirective): use $watchCollection
Since we are simply watching a flat object collection it is more performant
to use $watchCollection than a deepWatch...

Closes #10535
2014-12-22 00:58:45 +00:00
Dan Cancro 3a8f3dc9ea docs(guide/index): Link to starter options
Add a link to a comparison spreadsheet of alternative generators, examples,
tutorials and seeds that one can use to get started on a new Angular project.

Closes #10526
2014-12-22 00:30:30 +00:00
Robert Haritonov c139e68d99 docs(tutorial/12): fix path to jquery in bower
Closes #10504
2014-12-22 00:21:27 +00:00
1114 changed files with 26964 additions and 78795 deletions
-5
View File
@@ -1,5 +0,0 @@
# Auto detect text files and perform LF normalization
* text=auto
# JS files must always use LF for tools to work
*.js eol=lf
-27
View File
@@ -1,27 +0,0 @@
***Note*: for support questions, please use one of these channels: https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#question. This repository's issues are reserved for feature requests and bug reports.**
**Do you want to request a *feature* or report a *bug*?**
**What is the current behavior?**
**If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar (template: http://plnkr.co/edit/tpl:yBpEi4).**
**What is the expected behavior?**
**What is the motivation / use case for changing the behavior?**
**Which versions of Angular, and which browser / OS are affected by this issue? Did this work in previous versions of Angular? Please also test with the latest stable and snapshot (https://code.angularjs.org/snapshot/) versions.**
**Other information (e.g. stacktraces, related issues, suggestions how to fix)**
-23
View File
@@ -1,23 +0,0 @@
**What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)**
**What is the current behavior? (You can also link to an open issue here)**
**What is the new behavior (if this is a feature change)?**
**Does this PR introduce a breaking change?**
**Please check if the PR fulfills these requirements**
- [ ] The commit message follows our guidelines: https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#commit-message-format
- [ ] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been added / updated (for bug fixes / features)
**Other information**:
+23 -20
View File
@@ -1,7 +1,6 @@
language: node_js
sudo: false
node_js:
- '4.4'
- '0.10'
cache:
directories:
@@ -15,26 +14,28 @@ branches:
env:
matrix:
- JOB=ci-checks
- JOB=unit BROWSER_PROVIDER=saucelabs
- JOB=docs-e2e BROWSER_PROVIDER=saucelabs
- JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=saucelabs
- JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=saucelabs
- JOB=unit BROWSER_PROVIDER=browserstack
- JOB=docs-e2e BROWSER_PROVIDER=browserstack
- JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=browserstack
- JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=browserstack
global:
- CXX=g++-4.8 # node 4 likes the G++ v4.8 compiler
- SAUCE_USERNAME=angular-ci
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
- BROWSER_STACK_USERNAME=VojtaJina
- BROWSER_STACK_ACCESS_KEY=QCQJ1ZpWXpBkSwEdD8ev
- LOGS_DIR=/tmp/angular-build/logs
- BROWSER_PROVIDER_READY_FILE=/tmp/browsersprovider-tunnel-ready
# node 4 likes the G++ v4.8 compiler
# see https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
matrix:
allow_failures:
- env: "JOB=unit BROWSER_PROVIDER=browserstack"
- env: "JOB=docs-e2e BROWSER_PROVIDER=browserstack"
- env: "JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=browserstack"
- env: "JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=browserstack"
install:
# Check the size of caches
@@ -44,25 +45,27 @@ install:
- npm config set spin false
# Log HTTP requests
- npm config set loglevel http
#- npm install -g npm@2.5
# Install npm dependencies and ensure that npm cache is not stale
- npm install
- npm install -g npm@2.5
# Instal npm dependecies and ensure that npm cache is not stale
- scripts/npm/install-dependencies.sh
before_script:
- ./scripts/travis/before_build.sh
- mkdir -p $LOGS_DIR
- ./scripts/travis/start_browser_provider.sh
- npm install -g grunt-cli
- grunt package
- ./scripts/travis/wait_for_browser_provider.sh
script:
- ./scripts/travis/build.sh
after_script:
- ./scripts/travis/tear_down_browser_provider.sh
- ./scripts/travis/print_logs.sh
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/d2120f3f2bb39a4531b2
- http://104.197.9.155:8484/hubot/travis/activity #hubot-server
on_success: always # options: [always|never|change] default: always
on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: always # default: false
on_start: false # default: false
+56 -2227
View File
File diff suppressed because it is too large Load Diff
+6 -15
View File
@@ -19,7 +19,7 @@ Help us keep Angular open and inclusive. Please read and follow our [Code of Con
## <a name="question"></a> Got a Question or Problem?
If you have questions about how to use AngularJS, please direct these to the [Google Group][groups]
discussion list or [StackOverflow][stackoverflow]. We are also available on [IRC][irc] and [Gitter][gitter].
discussion list or [StackOverflow][stackoverflow]. We are also available on [IRC][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
@@ -71,7 +71,7 @@ chances of your issue being dealt with quickly:
* **Angular Version(s)** - is it a regression?
* **Browsers and Operating System** - is this a problem with all browsers or only IE8?
* **Reproduce the Error** - provide a live example (using [Plunker][plunker] or
[JSFiddle][jsfiddle]) or an unambiguous set of steps.
[JSFiddle][jsfiddle]) or a unambiguous set of steps.
* **Related Issues** - has a similar issue been reported before?
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
causing the problem (line of code or commit)
@@ -123,19 +123,13 @@ Before you submit your pull request consider the following guidelines:
* If we suggest changes then:
* Make the required updates.
* Re-run the Angular test suite to ensure tests are still passing.
* Commit your changes to your branch (e.g. `my-fix-branch`).
* Push the changes to your GitHub repository (this will update your Pull Request).
If the PR gets too outdated we may ask you to rebase and force push to update the PR:
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
```shell
git rebase master -i
git push origin my-fix-branch -f
```
*WARNING. Squashing or reverting commits and forced push thereafter may remove GitHub comments
on code that were previously made by you and others in your commits.*
That's it! Thank you for your contribution!
#### After your pull request is merged
@@ -193,8 +187,6 @@ We have very precise rules over how our git commit messages can be formatted. T
readable messages** that are easy to follow when looking through the **project history**. But also,
we use the git commit messages to **generate the AngularJS change log**.
The commit message formatting can be added using a typical git workflow or through the use of a CLI wizard ([Commitizen](https://github.com/commitizen/cz-cli)). To use the wizard, run `npm run commit` in your terminal after staging your changes in git.
### Commit Message Format
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
format that includes a **type**, a **scope** and a **subject**:
@@ -210,7 +202,7 @@ format that includes a **type**, a **scope** and a **subject**:
The **header** is mandatory and the **scope** of the header is optional.
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
to read on GitHub as well as in various git tools.
to read on github as well as in various git tools.
### Revert
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
@@ -223,7 +215,7 @@ Must be one of the following:
* **docs**: Documentation only changes
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
semi-colons, etc)
* **refactor**: A code change that neither fixes a bug nor adds a feature
* **refactor**: A code change that neither fixes a bug or adds a feature
* **perf**: A code change that improves performance
* **test**: Adding missing tests
* **chore**: Changes to the build process or auxiliary tools and libraries such as documentation
@@ -241,7 +233,7 @@ The subject contains succinct description of the change:
* no dot (.) at the end
### Body
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes"
The body should include the motivation for the change and contrast this with previous behavior.
### Footer
@@ -276,7 +268,6 @@ You can find out more detailed information about contributing in the
[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
[gitter]: https://gitter.im/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
+14 -28
View File
@@ -115,7 +115,7 @@ module.exports = function(grunt) {
files: { src: 'test/**/*.js' },
},
ng: {
files: { src: files['angularSrc'].concat('!src/angular.bind.js') },
files: { src: files['angularSrc'] },
},
ngAnimate: {
files: { src: 'src/ngAnimate/**/*.js' },
@@ -126,9 +126,6 @@ module.exports = function(grunt) {
ngLocale: {
files: { src: 'src/ngLocale/**/*.js' },
},
ngMessageFormat: {
files: { src: 'src/ngMessageFormat/**/*.js' },
},
ngMessages: {
files: { src: 'src/ngMessages/**/*.js' },
},
@@ -156,13 +153,9 @@ module.exports = function(grunt) {
},
jscs: {
src: [
'src/**/*.js',
'test/**/*.js',
'!src/angular.bind.js' // we ignore this file since contains an early return statement
],
src: ['src/**/*.js', 'test/**/*.js'],
options: {
config: '.jscsrc'
config: ".jscsrc"
}
},
@@ -207,10 +200,6 @@ module.exports = function(grunt) {
dest: 'build/angular-resource.js',
src: util.wrap(files['angularModules']['ngResource'], 'module')
},
messageformat: {
dest: 'build/angular-message-format.js',
src: util.wrap(files['angularModules']['ngMessageFormat'], 'module')
},
messages: {
dest: 'build/angular-messages.js',
src: util.wrap(files['angularModules']['ngMessages'], 'module')
@@ -231,9 +220,9 @@ module.exports = function(grunt) {
dest: 'build/angular-aria.js',
src: util.wrap(files['angularModules']['ngAria'], 'module')
},
'promises-aplus-adapter': {
"promises-aplus-adapter": {
dest:'tmp/promises-aplus-adapter++.js',
src:['src/ng/q.js', 'lib/promises-aplus/promises-aplus-test-adapter.js']
src:['src/ng/q.js','lib/promises-aplus/promises-aplus-test-adapter.js']
}
},
@@ -243,7 +232,6 @@ module.exports = function(grunt) {
animate: 'build/angular-animate.js',
cookies: 'build/angular-cookies.js',
loader: 'build/angular-loader.js',
messageformat: 'build/angular-message-format.js',
messages: 'build/angular-messages.js',
touch: 'build/angular-touch.js',
resource: 'build/angular-resource.js',
@@ -253,7 +241,7 @@ module.exports = function(grunt) {
},
'ddescribe-iit': {
"ddescribe-iit": {
files: [
'src/**/*.js',
'test/**/*.js',
@@ -274,7 +262,7 @@ module.exports = function(grunt) {
}
},
'merge-conflict': {
"merge-conflict": {
files: [
'src/**/*',
'test/**/*',
@@ -304,11 +292,11 @@ module.exports = function(grunt) {
},
shell: {
'npm-install': {
command: 'node scripts/npm/check-node-modules.js'
"npm-install": {
command: path.normalize('scripts/npm/install-dependencies.sh')
},
'promises-aplus-tests': {
"promises-aplus-tests": {
options: {
stdout: false,
stderr: true,
@@ -339,10 +327,8 @@ module.exports = function(grunt) {
grunt.task.run('shell:npm-install');
}
//alias tasks
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', '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', ['build', 'tests:modules']);
@@ -352,11 +338,11 @@ module.exports = function(grunt) {
grunt.registerTask('test:travis-protractor', 'Run the end to end tests with Protractor for Travis CI builds', ['connect:testserver', 'protractor:travis']);
grunt.registerTask('test:ci-protractor', 'Run the end to end tests with Protractor for Jenkins CI builds', ['webdriver', 'connect:testserver', 'protractor:jenkins']);
grunt.registerTask('test:e2e', 'Alias for test:protractor', ['test:protractor']);
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter', 'shell:promises-aplus-tests']);
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter','shell:promises-aplus-tests']);
grunt.registerTask('minify', ['bower', 'clean', 'build', 'minall']);
grunt.registerTask('minify', ['bower','clean', 'build', 'minall']);
grunt.registerTask('webserver', ['connect:devserver']);
grunt.registerTask('package', ['bower', 'validate-angular-files', 'clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
grunt.registerTask('package', ['bower','clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint', 'jscs']);
grunt.registerTask('default', ['package']);
};
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2010-2016 Google, Inc. http://angularjs.org
Copyright (c) 2010-2014 Google, Inc. http://angularjs.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+3 -26
View File
@@ -18,15 +18,15 @@ piece of cake. Best of all?? It makes development fun!
* Developer Guide: http://docs.angularjs.org/guide
* Contribution guidelines: [CONTRIBUTING.md](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md)
* Dashboard: http://dashboard.angularjs.org
Building AngularJS
---------
[Once you have your environment set up](http://docs.angularjs.org/misc/contribute) just run:
[Once you have your environment setup](http://docs.angularjs.org/misc/contribute) just run:
grunt package
Running tests
Running Tests
-------------
To execute all unit tests, use:
@@ -43,26 +43,3 @@ To learn more about the grunt tasks, run `grunt --help` and also read our
[![Analytics](https://ga-beacon.appspot.com/UA-8594346-11/angular.js/README.md?pixel)](https://github.com/igrigorik/ga-beacon)
What to use AngularJS for and when to use it
---------
AngularJS is the next generation framework where each component is designed to work with every other component in an interconnected way like a well-oiled machine. AngularJS is JavaScript MVC made easy and done right. (Well it is not really MVC, read on, to understand what this means.)
#### MVC, no, MV* done the right way!
MVC, short for Model-View-Controller, is a design pattern, i.e. how the code should be organized and how the different parts of an application separated for proper readability and debugging. Model is the data and the database. View is the user interface and what the user sees. Controller is the main link between Model and View. These are the three pillars of major programming frameworks present on the market today. On the other hand AngularJS works on MV*, short for Model-View-_Whatever_. The _Whatever_ is AngularJS's way of telling that you may create any kind of linking between the Model and the View here.
Unlike other frameworks in any programming language, where MVC, the three separate components, each one has to be written and then connected by the programmer, AngularJS helps the programmer by asking him/her to just create these and everything else will be taken care of by AngularJS.
#### Interconnection with HTML at the root level
AngularJS uses HTML to define the user's interface. AngularJS also enables the programmer to write new HTML tags (AngularJS Directives) and increase the readability and understandability of the HTML code. Directives are AngularJSs way of bringing additional functionality to HTML. Directives achieve this by enabling us to invent our own HTML elements. This also helps in making the code DRY (Don't Repeat Yourself), which means once created, a new directive can be used anywhere within the application.
#### Data Handling made simple
Data and Data Models in AngularJS are plain JavaScript objects and one can add and change properties directly on it and loop over objects and arrays at will.
#### Two-way Data Binding
One of AngularJS's strongest features. Two-way Data Binding means that if something changes in the Model, the change gets reflected in the View instantaneously, and the same happens the other way around. This is also referred to as Reactive Programming, i.e. suppose `a = b + c` is being programmed and after this, if the value of `b` and/or `c` is changed then the value of `a` will be automatically updated to reflect the change. AngularJS uses its "scopes" as a glue between the Model and View and makes these updates in one available for the other.
#### Less Written Code and Easily Maintainable Code
Everything in AngularJS is created to enable the programmer to end up writing less code that is easily maintainable and readable by any other new person on the team. Believe it or not, one can write a complete working two-way data binded application in less than 10 lines of code. Try and see for yourself!
#### Testing Ready
AngularJS has Dependency Injection, i.e. it takes care of providing all the necessary dependencies to its controllers whenever required. This helps in making the AngularJS code ready for unit testing by making use of mock dependencies created and injected. This makes AngularJS more modular and easily testable thus in turn helping a team create more robust applications.
+1 -1
View File
@@ -20,7 +20,7 @@ The following is done automatically so you don't have to worry about it:
This process based on the idea of minimizing user pain
[from this blog post](http://www.lostgarden.com/2008/05/improving-bug-triage-with-user-pain.html).
1. Open the list of [non triaged issues](https://github.com/angular/angular.js/issues?q=is%3Aopen+sort%3Acreated-desc+no%3Amilestone)
1. Open the list of [non triaged issues](https://github.com/angular/angular.js/issues?direction=desc&milestone=none&page=1&sort=created&state=open)
* Sort by submit date, with the newest issues first
* You don't have to do issues in order; feel free to pick and choose issues as you please.
* You can triage older issues as well
+11 -41
View File
@@ -14,15 +14,13 @@ var angularFiles = {
'src/ng/anchorScroll.js',
'src/ng/animate.js',
'src/ng/animateRunner.js',
'src/ng/animateCss.js',
'src/ng/asyncCallback.js',
'src/ng/browser.js',
'src/ng/cacheFactory.js',
'src/ng/compile.js',
'src/ng/controller.js',
'src/ng/document.js',
'src/ng/exceptionHandler.js',
'src/ng/forceReflow.js',
'src/ng/http.js',
'src/ng/httpBackend.js',
'src/ng/interpolate.js',
@@ -34,7 +32,6 @@ var angularFiles = {
'src/ng/q.js',
'src/ng/raf.js',
'src/ng/rootScope.js',
'src/ng/rootElement.js',
'src/ng/sanitizeUri.js',
'src/ng/sce.js',
'src/ng/sniffer.js',
@@ -43,7 +40,6 @@ var angularFiles = {
'src/ng/timeout.js',
'src/ng/urlUtils.js',
'src/ng/window.js',
'src/ng/cookieReader.js',
'src/ng/filter.js',
'src/ng/filter/filter.js',
@@ -69,7 +65,6 @@ var angularFiles = {
'src/ng/directive/ngList.js',
'src/ng/directive/ngModel.js',
'src/ng/directive/ngNonBindable.js',
'src/ng/directive/ngOptions.js',
'src/ng/directive/ngPluralize.js',
'src/ng/directive/ngRepeat.js',
'src/ng/directive/ngShowHide.js',
@@ -79,43 +74,21 @@ var angularFiles = {
'src/ng/directive/script.js',
'src/ng/directive/select.js',
'src/ng/directive/style.js',
'src/ng/directive/validators.js',
'src/angular.bind.js',
'src/publishExternalApis.js',
'src/ngLocale/angular-locale_en-us.js'
'src/ng/directive/validators.js'
],
'angularLoader': [
'src/stringify.js',
'stringify.js',
'src/minErr.js',
'src/loader.js'
],
'angularModules': {
'ngAnimate': [
'src/ngAnimate/shared.js',
'src/ngAnimate/rafScheduler.js',
'src/ngAnimate/animateChildrenDirective.js',
'src/ngAnimate/animateCss.js',
'src/ngAnimate/animateCssDriver.js',
'src/ngAnimate/animateJs.js',
'src/ngAnimate/animateJsDriver.js',
'src/ngAnimate/animateQueue.js',
'src/ngAnimate/animation.js',
'src/ngAnimate/ngAnimateSwap.js',
'src/ngAnimate/module.js'
'src/ngAnimate/animate.js'
],
'ngCookies': [
'src/ngCookies/cookies.js',
'src/ngCookies/cookieStore.js',
'src/ngCookies/cookieWriter.js'
],
'ngMessageFormat': [
'src/ngMessageFormat/messageFormatCommon.js',
'src/ngMessageFormat/messageFormatSelector.js',
'src/ngMessageFormat/messageFormatInterpolationParts.js',
'src/ngMessageFormat/messageFormatParser.js',
'src/ngMessageFormat/messageFormatService.js'
'src/ngCookies/cookies.js'
],
'ngMessages': [
'src/ngMessages/messages.js'
@@ -185,16 +158,16 @@ var angularFiles = {
'bower_components/jquery/dist/jquery.js',
'test/jquery_remove.js',
'@angularSrc',
'src/publishExternalApis.js',
'@angularSrcModules',
'@angularScenario',
'@angularTest'
'@angularTest',
],
'karmaExclude': [
'test/jquery_alias.js',
'src/angular-bootstrap.js',
'src/ngScenario/angular-bootstrap.js',
'src/angular.bind.js'
'src/ngScenario/angular-bootstrap.js'
],
'karmaScenario': [
@@ -205,10 +178,8 @@ var angularFiles = {
"karmaModules": [
'build/angular.js',
'@angularSrcModules',
'test/modules/no_bootstrap.js',
'src/ngScenario/browserTrigger.js',
'test/helpers/*.js',
'test/ngMessageFormat/*.js',
'test/ngMock/*.js',
'test/ngCookies/*.js',
'test/ngRoute/**/*.js',
@@ -222,22 +193,21 @@ var angularFiles = {
'bower_components/jquery/dist/jquery.js',
'test/jquery_alias.js',
'@angularSrc',
'src/publishExternalApis.js',
'@angularSrcModules',
'@angularScenario',
'@angularTest'
'@angularTest',
],
'karmaJqueryExclude': [
'src/angular-bootstrap.js',
'src/ngScenario/angular-bootstrap.js',
'test/jquery_remove.js',
'src/angular.bind.js'
'test/jquery_remove.js'
]
};
angularFiles['angularSrcModules'] = [].concat(
angularFiles['angularModules']['ngAnimate'],
angularFiles['angularModules']['ngMessageFormat'],
angularFiles['angularModules']['ngMessages'],
angularFiles['angularModules']['ngCookies'],
angularFiles['angularModules']['ngResource'],
+12 -35
View File
@@ -8,20 +8,17 @@
Large table rendered with AngularJS
</p>
<div><label><input type="radio" ng-model="benchmarkType" value="none">none: </label></div>
<div><label><input type="radio" ng-model="benchmarkType" value="baselineBinding">baseline binding: </label></div>
<div><label><input type="radio" ng-model="benchmarkType" value="baselineInterpolation">baseline interpolation: </label></div>
<div><label><input type="radio" ng-model="benchmarkType" value="ngBind">ngBind: </label></div>
<div><label><input type="radio" ng-model="benchmarkType" value="ngBindOnce">ngBindOnce: </label></div>
<div><label><input type="radio" ng-model="benchmarkType" value="interpolation">interpolation: </label></div>
<div><label><input type="radio" ng-model="benchmarkType" value="bindOnceInterpolation">interpolation + bind-once: </label></div>
<div><label><input type="radio" ng-model="benchmarkType" value="interpolationAttr">attribute interpolation: </label></div>
<div><label><input type="radio" ng-model="benchmarkType" value="ngBindFn">ngBind + fnInvocation: </label></div>
<div><label><input type="radio" ng-model="benchmarkType" value="interpolationFn">interpolation + fnInvocation: </label></div>
<div><label><input type="radio" ng-model="benchmarkType" value="ngBindFilter">ngBind + filter: </label></div>
<div><label><input type="radio" ng-model="benchmarkType" value="interpolationFilter">interpolation + filter: </label></div>
<div><label><input type="radio" ng-model="benchmarkType" value="ngModelConstName">ngModel (const name): </label></div>
<div><label><input type="radio" ng-model="benchmarkType" value="ngModelInterpName">ngModel (interp name): </label></div>
<div>none: <input type="radio" ng-model="benchmarkType" value="none"></div>
<div>baseline binding: <input type="radio" ng-model="benchmarkType" value="baselineBinding"></div>
<div>baseline interpolation: <input type="radio" ng-model="benchmarkType" value="baselineInterpolation"></div>
<div>ngBind: <input type="radio" ng-model="benchmarkType" value="ngBind"></div>
<div>ngBindOnce: <input type="radio" ng-model="benchmarkType" value="ngBindOnce"></div>
<div>interpolation: <input type="radio" ng-model="benchmarkType" value="interpolation"></div>
<div>attribute interpolation: <input type="radio" ng-model="benchmarkType" value="interpolationAttr"></div>
<div>ngBind + fnInvocation: <input type="radio" ng-model="benchmarkType" value="ngBindFn"></div>
<div>interpolation + fnInvocation: <input type="radio" ng-model="benchmarkType" value="interpolationFn"></div>
<div>ngBind + filter: <input type="radio" ng-model="benchmarkType" value="ngBindFilter"></div>
<div>interpolation + filter: <input type="radio" ng-model="benchmarkType" value="interpolationFilter"></div>
<ng-switch on="benchmarkType">
<baseline-binding-table ng-switch-when="baselineBinding">
@@ -38,7 +35,7 @@
</div>
<div ng-switch-when="ngBindOnce">
<h2>baseline binding once</h2>
<div ng-repeat="row in ::data">
<div ng-repeat="row in data">
<span ng-repeat="column in ::row">
<span ng-bind="::column.i"></span>:<span ng-bind="::column.j"></span>|
</span>
@@ -50,12 +47,6 @@
<span ng-repeat="column in row">{{column.i}}:{{column.j}}|</span>
</div>
</div>
<div ng-switch-when="bindOnceInterpolation">
<h2>baseline one-time interpolation</h2>
<div ng-repeat="row in ::data">
<span ng-repeat="column in ::row">{{::column.i}}:{{::column.j}}|</span>
</div>
</div>
<div ng-switch-when="interpolationAttr">
<h2>attribute interpolation</h2>
<div ng-repeat="row in data">
@@ -86,20 +77,6 @@
<span ng-repeat="column in row">{{column.i | noop}}:{{column.j | noop}}|</span>
</div>
</div>
<div ng-switch-when="ngModelConstName">
<h2>ngModel (const name)</h2>
<div ng-repeat="row in data">
<input type="text" ng-model="row.i" name="constName" />
<input type="text" ng-model="row.j" />
</div>
</div>
<div ng-switch-when="ngModelInterpName">
<h2>ngModel (interp name)</h2>
<div ng-repeat="(rowIdx, row) in data">
<input type="text" ng-model="row.i" name="input-{{rowIdx}}" />
<input type="text" ng-model="row.j" name="input2-{{rowIdx}}" />
</div>
</div>
</ng-switch>
</div>
</div>
-95
View File
@@ -1,95 +0,0 @@
"use strict";
/* globals angular, benchmarkSteps */
var app = angular.module('ngOptionsBenchmark', []);
app.config(function($compileProvider) {
if ($compileProvider.debugInfoEnabled) {
$compileProvider.debugInfoEnabled(false);
}
});
app.controller('DataController', function($scope, $element) {
$scope.items = [];
$scope.count = 10000;
function changeOptions() {
$scope.items = [];
for (var i = 0; i < $scope.count; ++i) {
$scope.items.push({
id: i,
label: 'item-' + i,
group: 'group-' + i % 100
});
}
}
var selectElement = $element.find('select');
console.log(selectElement);
benchmarkSteps.push({
name: 'add-options',
fn: function() {
$scope.$apply(function() {
$scope.count = 10000;
changeOptions();
});
}
});
benchmarkSteps.push({
name: 'set-model-1',
fn: function() {
$scope.$apply(function() {
$scope.x = $scope.items[1000];
});
}
});
benchmarkSteps.push({
name: 'set-model-2',
fn: function() {
$scope.$apply(function() {
$scope.x = $scope.items[10];
});
}
});
benchmarkSteps.push({
name: 'remove-options',
fn: function() {
$scope.count = 100;
changeOptions();
}
});
benchmarkSteps.push({
name: 'add-options',
fn: function() {
$scope.$apply(function() {
$scope.count = 10000;
changeOptions();
});
}
});
benchmarkSteps.push({
name: 'set-view-1',
fn: function() {
selectElement.val('2000');
selectElement.triggerHandler('change');
}
});
benchmarkSteps.push({
name: 'set-view-2',
fn: function() {
selectElement.val('1000');
selectElement.triggerHandler('change');
}
});
});
-11
View File
@@ -1,11 +0,0 @@
module.exports = function(config) {
config.set({
scripts: [ {
id: 'angular',
src: '/build/angular.js'
},
{
src: 'app.js',
}]
});
};
-10
View File
@@ -1,10 +0,0 @@
<div ng-app="ngOptionsBenchmark" ng-cloak>
<div ng-controller="DataController">
<div class="container-fluid">
<p>
Tests the execution of ng-options for rendering during model and option updates.
</p>
<select ng-model="x" ng-options="a as a.label group by a.group for a in items track by a.id"></select>
</div>
</div>
</div>
-1
View File
@@ -1,6 +1,5 @@
{
"name": "AngularJS",
"license": "MIT",
"devDependencies": {
"jquery": "2.1.1",
"closure-compiler": "https://dl.google.com/closure-compiler/compiler-20140814.zip",
+3 -3
View File
@@ -44,7 +44,7 @@ var noArgs = function (fn) {
var identity = function (i) { return i; };
// like Q.all, but runs the commands in series
// like Q.all, but runs the comands in series
// useful for ensuring env state (like which branch is checked out)
var allInSeries = function (fn) {
return function (args) {
@@ -103,10 +103,10 @@ then(function (tags) {
sort(semver.rcompare);
}).
then(function (tags) {
var major = tags[0].split('.')[0];
var major = tags[0].split('.')[0] + '.x';
return tags.
filter(function (ver) {
return semver(ver).major == major;
return semver.satisfies(ver, major);
});
}).
then(function (tags) {
-8
View File
@@ -9,11 +9,3 @@
ng\:form {
display: block;
}
.ng-animate-shim {
visibility:hidden;
}
.ng-anchor {
position:absolute;
}
+1 -1
View File
@@ -56,7 +56,7 @@ li.doc-example-live {
}
div.syntaxhighlighter {
padding-bottom: 1px !important; /* fix to remove unnecessary scrollbars */
padding-bottom: 1px !important; /* fix to remove unnecessary scrollbars http://is.gd/gSMgC */
}
/* TABS - tutorial environment navigation */
+2 -97
View File
@@ -124,7 +124,7 @@ h1,h2,h3,h4,h5,h6 {
font-size:1.2em;
padding:0;
margin:0;
border-bottom:1px solid #aaa;
border-bottom:1px soild #aaa;
margin-bottom:5px;
}
@@ -315,13 +315,8 @@ iframe.example {
color:white;
}
.search-results-group .search-results {
padding: 0 5px 0;
list-style-type: none;
}
.search-results-frame > .search-results-group:first-child > .search-results {
border-right:1px solid #222;
border-right:1px solid #050505;
}
.search-results-group.col-group-api { width:30%; }
@@ -330,57 +325,10 @@ iframe.example {
.search-results-group.col-group-misc,
.search-results-group.col-group-error { width:15%; float: right; }
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
.search-results-group.col-group-api .search-results {
-moz-column-count: 2;
-ms-column-count: 2;
-webkit-column-count: 2;
column-count: 2;
/* Prevent bullets in the second column from being hidden in Chrome and IE */
-webkit-column-gap: 2em;
-ms-column-gap: 2em;
column-gap: 2em;
}
}
.search-results-group .search-result {
word-wrap: break-word;
-webkit-hyphens: auto;
-moz-hyphens: auto;
-ms-hyphens: auto;
hyphens: auto;
-ms-column-break-inside: avoid;
-webkit-column-break-inside: avoid;
-moz-column-break-inside: avoid; /* Unsupported */
column-break-inside: avoid;
text-indent: -0.65em; /* Make sure line wrapped words are aligned vertically */
}
@supports (-moz-column-count: 2) {
.search-results-group .search-result {
/* Prevents column breaks inside words in FF, but has adverse effects in IE11 and Chrome */
overflow: hidden;
padding-left: 1em; /* In FF the list item bullet is otherwise hidden */
margin-left: -1em; /* offset the padding left */
}
}
.search-result:before {
content: "\002D\00A0"; /* Dash and non-breaking space as List item type */
position: relative;
}
.search-results-group.col-group-api .search-result {
width:48%;
display:inline-block;
padding-left: 12px;
}
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
.search-results-group.col-group-api .search-result {
width:auto;
display: list-item;
}
}
.search-close {
@@ -641,12 +589,6 @@ ul.events > li {
vertical-align: top;
}
.table > tbody > tr.head > td,
.table > tbody > tr.head > th {
border-bottom: 2px solid #ddd;
padding-top: 50px;
}
@media only screen and (min-width: 769px) and (max-width: 991px) {
.main-body-grid {
margin-top: 160px;
@@ -740,11 +682,6 @@ ul.events > li {
padding-bottom:60px;
text-align:left;
}
.search-results-frame > .search-results-group:first-child > .search-results {
border-right: none;
}
.search-results-group {
float:none!important;
display:block!important;
@@ -752,47 +689,15 @@ ul.events > li {
border:0!important;
padding:0!important;
}
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
.search-results-group .search-results {
-moz-column-count: 2;
-ms-column-count: 2;
-webkit-column-count: 2;
column-count: 2;
}
}
.search-results-group .search-result {
display:inline-block!important;
padding:0 5px;
width:auto!important;
text-indent: initial;
margin-left: 0;
}
.search-results-group .search-result:after {
content:", ";
}
.search-results-group .search-result:before {
content: "";
}
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
.search-results-group .search-result {
display: list-item !important;
}
.search-results-group .search-result:after {
content: "";
}
}
#wrapper {
padding-bottom:0px;
}
}
iframe[name="example-anchoringExample"] {
height:400px;
}
+442
View File
@@ -0,0 +1,442 @@
'use strict';
var directive = {};
directive.runnableExample = ['$templateCache', '$document', function($templateCache, $document) {
var exampleClassNameSelector = '.runnable-example-file';
var doc = $document[0];
var tpl =
'<nav class="runnable-example-tabs" ng-if="tabs">' +
' <a ng-class="{active:$index==activeTabIndex}"' +
'ng-repeat="tab in tabs track by $index" ' +
'href="" ' +
'class="btn"' +
'ng-click="setTab($index)">' +
' {{ tab }}' +
' </a>' +
'</nav>';
return {
restrict: 'C',
scope : true,
controller : ['$scope', function($scope) {
$scope.setTab = function(index) {
var tab = $scope.tabs[index];
$scope.activeTabIndex = index;
$scope.$broadcast('tabChange', index, tab);
};
}],
compile : function(element) {
element.html(tpl + element.html());
return function(scope, element) {
var node = element[0];
var examples = node.querySelectorAll(exampleClassNameSelector);
var tabs = [], now = Date.now();
angular.forEach(examples, function(child, index) {
tabs.push(child.getAttribute('name'));
});
if(tabs.length > 0) {
scope.tabs = tabs;
scope.$on('tabChange', function(e, index, title) {
angular.forEach(examples, function(child) {
child.style.display = 'none';
});
var selected = examples[index];
selected.style.display = 'block';
});
scope.setTab(0);
}
}
}
};
}];
directive.dropdownToggle =
['$document', '$location', '$window',
function ($document, $location, $window) {
var openElement = null, close;
return {
restrict: 'C',
link: function(scope, element, attrs) {
scope.$watch(function dropdownTogglePathWatch(){return $location.path();}, function dropdownTogglePathWatchAction() {
close && close();
});
element.parent().on('click', function(event) {
close && close();
});
element.on('click', function(event) {
event.preventDefault();
event.stopPropagation();
var iWasOpen = false;
if (openElement) {
iWasOpen = openElement === element;
close();
}
if (!iWasOpen){
element.parent().addClass('open');
openElement = element;
close = function (event) {
event && event.preventDefault();
event && event.stopPropagation();
$document.off('click', close);
element.parent().removeClass('open');
close = null;
openElement = null;
}
$document.on('click', close);
}
});
}
};
}];
directive.syntax = function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
function makeLink(type, text, link, icon) {
return '<a href="' + link + '" class="btn syntax-' + type + '" target="_blank" rel="nofollow">' +
'<span class="' + icon + '"></span> ' + text +
'</a>';
};
var html = '';
var types = {
'github' : {
text : 'View on Github',
key : 'syntaxGithub',
icon : 'icon-github'
},
'plunkr' : {
text : 'View on Plunkr',
key : 'syntaxPlunkr',
icon : 'icon-arrow-down'
},
'jsfiddle' : {
text : 'View on JSFiddle',
key : 'syntaxFiddle',
icon : 'icon-cloud'
}
};
for(var type in types) {
var data = types[type];
var link = attrs[data.key];
if(link) {
html += makeLink(type, data.text, link, data.icon);
}
};
var nav = document.createElement('nav');
nav.className = 'syntax-links';
nav.innerHTML = html;
var node = element[0];
var par = node.parentNode;
par.insertBefore(nav, node);
}
}
}
directive.tabbable = function() {
return {
restrict: 'C',
compile: function(element) {
var navTabs = angular.element('<ul class="nav nav-tabs"></ul>'),
tabContent = angular.element('<div class="tab-content"></div>');
tabContent.append(element.contents());
element.append(navTabs).append(tabContent);
},
controller: ['$scope', '$element', function($scope, $element) {
var navTabs = $element.contents().eq(0),
ngModel = $element.controller('ngModel') || {},
tabs = [],
selectedTab;
ngModel.$render = function() {
var $viewValue = this.$viewValue;
if (selectedTab ? (selectedTab.value != $viewValue) : $viewValue) {
if(selectedTab) {
selectedTab.paneElement.removeClass('active');
selectedTab.tabElement.removeClass('active');
selectedTab = null;
}
if($viewValue) {
for(var i = 0, ii = tabs.length; i < ii; i++) {
if ($viewValue == tabs[i].value) {
selectedTab = tabs[i];
break;
}
}
if (selectedTab) {
selectedTab.paneElement.addClass('active');
selectedTab.tabElement.addClass('active');
}
}
}
};
this.addPane = function(element, attr) {
var li = angular.element('<li><a href></a></li>'),
a = li.find('a'),
tab = {
paneElement: element,
paneAttrs: attr,
tabElement: li
};
tabs.push(tab);
attr.$observe('value', update)();
attr.$observe('title', function(){ update(); a.text(tab.title); })();
function update() {
tab.title = attr.title;
tab.value = attr.value || attr.title;
if (!ngModel.$setViewValue && (!ngModel.$viewValue || tab == selectedTab)) {
// we are not part of angular
ngModel.$viewValue = tab.value;
}
ngModel.$render();
}
navTabs.append(li);
li.on('click', function(event) {
event.preventDefault();
event.stopPropagation();
if (ngModel.$setViewValue) {
$scope.$apply(function() {
ngModel.$setViewValue(tab.value);
ngModel.$render();
});
} else {
// we are not part of angular
ngModel.$viewValue = tab.value;
ngModel.$render();
}
});
return function() {
tab.tabElement.remove();
for(var i = 0, ii = tabs.length; i < ii; i++ ) {
if (tab == tabs[i]) {
tabs.splice(i, 1);
}
}
};
}
}]
};
};
directive.table = function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
if (!attrs['class']) {
element.addClass('table table-bordered table-striped code-table');
}
}
};
};
var popoverElement = function() {
var object = {
init : function() {
this.element = angular.element(
'<div class="popover popover-incode top">' +
'<div class="arrow"></div>' +
'<div class="popover-inner">' +
'<div class="popover-title"><code></code></div>' +
'<div class="popover-content"></div>' +
'</div>' +
'</div>'
);
this.node = this.element[0];
this.element.css({
'display':'block',
'position':'absolute'
});
angular.element(document.body).append(this.element);
var inner = this.element.children()[1];
this.titleElement = angular.element(inner.childNodes[0].firstChild);
this.contentElement = angular.element(inner.childNodes[1]);
//stop the click on the tooltip
this.element.on('click', function(event) {
event.preventDefault();
event.stopPropagation();
});
var self = this;
angular.element(document.body).on('click',function(event) {
if(self.visible()) self.hide();
});
},
show : function(x,y) {
this.element.addClass('visible');
this.position(x || 0, y || 0);
},
hide : function() {
this.element.removeClass('visible');
this.position(-9999,-9999);
},
visible : function() {
return this.position().y >= 0;
},
isSituatedAt : function(element) {
return this.besideElement ? element[0] == this.besideElement[0] : false;
},
title : function(value) {
return this.titleElement.html(value);
},
content : function(value) {
if(value && value.length > 0) {
value = marked(value);
}
return this.contentElement.html(value);
},
positionArrow : function(position) {
this.node.className = 'popover ' + position;
},
positionAway : function() {
this.besideElement = null;
this.hide();
},
positionBeside : function(element) {
this.besideElement = element;
var elm = element[0];
var x = elm.offsetLeft;
var y = elm.offsetTop;
x -= 30;
y -= this.node.offsetHeight + 10;
this.show(x,y);
},
position : function(x,y) {
if(x != null && y != null) {
this.element.css('left',x + 'px');
this.element.css('top', y + 'px');
}
else {
return {
x : this.node.offsetLeft,
y : this.node.offsetTop
};
}
}
};
object.init();
object.hide();
return object;
};
directive.popover = ['popoverElement', function(popover) {
return {
restrict: 'A',
priority : 500,
link: function(scope, element, attrs) {
element.on('click',function(event) {
event.preventDefault();
event.stopPropagation();
if(popover.isSituatedAt(element) && popover.visible()) {
popover.title('');
popover.content('');
popover.positionAway();
}
else {
popover.title(attrs.title);
popover.content(attrs.content);
popover.positionBeside(element);
}
});
}
}
}];
directive.tabPane = function() {
return {
require: '^tabbable',
restrict: 'C',
link: function(scope, element, attrs, tabsCtrl) {
element.on('$remove', tabsCtrl.addPane(element, attrs));
}
};
};
directive.foldout = ['$http', '$animate','$window', function($http, $animate, $window) {
return {
restrict: 'A',
priority : 500,
link: function(scope, element, attrs) {
var container, loading, url = attrs.url;
if(/\/build\//.test($window.location.href)) {
url = '/build/docs' + url;
}
element.on('click',function() {
scope.$apply(function() {
if(!container) {
if(loading) return;
loading = true;
var par = element.parent();
container = angular.element('<div class="foldout">loading...</div>');
$animate.enter(container, null, par);
$http.get(url, { cache : true }).success(function(html) {
loading = false;
html = '<div class="foldout-inner">' +
'<div calss="foldout-arrow"></div>' +
html +
'</div>';
container.html(html);
//avoid showing the element if the user has already closed it
if(container.css('display') == 'block') {
container.css('display','none');
$animate.addClass(container, 'ng-hide');
}
});
}
else {
container.hasClass('ng-hide') ? $animate.removeClass(container, 'ng-hide') : $animate.addClass(container, 'ng-hide');
}
});
});
}
}
}];
angular.module('bootstrap', [])
.directive(directive)
.factory('popoverElement', popoverElement)
.run(function() {
marked.setOptions({
gfm: true,
tables: true
});
});
+1 -3
View File
@@ -54,9 +54,7 @@ angular.module('ui.bootstrap.dropdown', [])
}
};
var closeDropdown = function(evt) {
if (evt && evt.which === 3) return;
var closeDropdown = function() {
openScope.$apply(function() {
openScope.isOpen = false;
});
+1 -1
View File
@@ -3,7 +3,7 @@
/* global importScripts, onmessage: true, postMessage, lunr */
// Load up the lunr library
importScripts('../components/lunr.js-0.5.12/lunr.min.js');
importScripts('../components/lunr.js-0.4.2/lunr.min.js');
// Create the lunr index - the docs should be an array of object, each object containing
// the path and search terms for a page
+1
View File
@@ -13,6 +13,7 @@ angular.module('docsApp', [
'search',
'tutorials',
'versions',
'bootstrap',
'ui.bootstrap.dropdown'
])
+1 -12
View File
@@ -34,15 +34,4 @@ angular.module('directives', [])
return function(scope, element) {
$anchorScroll.yOffset = element;
};
}])
.directive('table', function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
if (!attrs['class']) {
element.addClass('table table-bordered table-striped code-table');
}
}
};
});
}]);
+4 -2
View File
@@ -1,11 +1,13 @@
angular.module('DocsController', [])
.controller('DocsController', [
'$scope', '$rootScope', '$location', '$window', '$cookies',
'$scope', '$rootScope', '$location', '$window', '$cookies', 'openPlunkr',
'NG_PAGES', 'NG_NAVIGATION', 'NG_VERSION',
function($scope, $rootScope, $location, $window, $cookies,
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.navClass = function(navItem) {
+3 -10
View File
@@ -13,10 +13,10 @@ angular.module('errors', ['ngSanitize'])
};
return function (text, target) {
if (!text) return text;
var targetHtml = target ? ' target="' + target + '"' : '';
if (!text) return text;
return $sanitize(text.replace(LINKY_URL_REGEXP, function (url) {
if (STACK_TRACE_REGEXP.test(url)) {
return url;
@@ -34,10 +34,6 @@ angular.module('errors', ['ngSanitize'])
.directive('errorDisplay', ['$location', 'errorLinkFilter', function ($location, errorLinkFilter) {
var encodeAngleBrackets = function (text) {
return text.replace(/</g, '&lt;').replace(/>/g, '&gt;');
};
var interpolate = function (formatString) {
var formatArgs = arguments;
return formatString.replace(/\{\d+\}/g, function (match) {
@@ -55,15 +51,12 @@ angular.module('errors', ['ngSanitize'])
link: function (scope, element, attrs) {
var search = $location.search(),
formatArgs = [attrs.errorDisplay],
formattedText,
i;
for (i = 0; angular.isDefined(search['p'+i]); i++) {
formatArgs.push(search['p'+i]);
}
formattedText = encodeAngleBrackets(interpolate.apply(null, formatArgs));
element.html(errorLinkFilter(formattedText, '_blank'));
element.html(errorLinkFilter(interpolate.apply(null, formatArgs), '_blank'));
}
};
}]);
+28 -149
View File
@@ -1,55 +1,5 @@
angular.module('examples', [])
.directive('runnableExample', ['$templateCache', '$document', function($templateCache, $document) {
var exampleClassNameSelector = '.runnable-example-file';
var doc = $document[0];
var tpl =
'<nav class="runnable-example-tabs" ng-if="tabs">' +
' <a ng-class="{active:$index==activeTabIndex}"' +
'ng-repeat="tab in tabs track by $index" ' +
'href="" ' +
'class="btn"' +
'ng-click="setTab($index)">' +
' {{ tab }}' +
' </a>' +
'</nav>';
return {
restrict: 'C',
scope : true,
controller : ['$scope', function($scope) {
$scope.setTab = function(index) {
var tab = $scope.tabs[index];
$scope.activeTabIndex = index;
$scope.$broadcast('tabChange', index, tab);
};
}],
compile : function(element) {
element.html(tpl + element.html());
return function(scope, element) {
var node = element[0];
var examples = node.querySelectorAll(exampleClassNameSelector);
var tabs = [], now = Date.now();
angular.forEach(examples, function(child, index) {
tabs.push(child.getAttribute('name'));
});
if(tabs.length > 0) {
scope.tabs = tabs;
scope.$on('tabChange', function(e, index, title) {
angular.forEach(examples, function(child) {
child.style.display = 'none';
});
var selected = examples[index];
selected.style.display = 'block';
});
scope.setTab(0);
}
};
}
};
}])
.factory('formPostData', ['$document', function($document) {
return function(url, newWindow, fields) {
/**
@@ -72,110 +22,29 @@ angular.module('examples', [])
};
}])
.factory('createCopyrightNotice', function() {
var COPYRIGHT = 'Copyright ' + (new Date()).getFullYear() + ' Google Inc. All Rights Reserved.\n'
+ 'Use of this source code is governed by an MIT-style license that\n'
+ 'can be found in the LICENSE file at http://angular.io/license';
var COPYRIGHT_JS_CSS = '\n\n/*\n' + COPYRIGHT + '\n*/';
var COPYRIGHT_HTML = '\n\n<!-- \n' + COPYRIGHT + '\n-->';
return function getCopyright(filename) {
switch (filename.substr(filename.lastIndexOf('.'))) {
case '.html':
return COPYRIGHT_HTML;
case '.js':
case '.css':
return COPYRIGHT_JS_CSS;
case '.md':
return COPYRIGHT;
}
return '';
};
})
.factory('openPlunkr', ['formPostData', '$http', '$q', function(formPostData, $http, $q) {
return function(exampleFolder, clickEvent) {
.directive('plnkrOpener', ['$q', 'getExampleData', 'formPostData', 'createCopyrightNotice', function($q, getExampleData, formPostData, createCopyrightNotice) {
return {
scope: {},
bindToController: {
'examplePath': '@'
},
controllerAs: 'plnkr',
template: '<button ng-click="plnkr.open($event)" class="btn pull-right"> <i class="glyphicon glyphicon-edit">&nbsp;</i> Edit in Plunker</button> ',
controller: [function() {
var ctrl = this;
var exampleName = 'AngularJS Example';
var newWindow = clickEvent.ctrlKey || clickEvent.metaKey;
ctrl.example = {
path: ctrl.examplePath,
manifest: undefined,
files: undefined,
name: 'AngularJS Example'
};
ctrl.prepareExampleData = function() {
if (ctrl.example.manifest) {
return $q.when(ctrl.example);
}
return getExampleData(ctrl.examplePath).then(function(data) {
ctrl.example.files = data.files;
ctrl.example.manifest = data.manifest;
// Build a pretty title for the Plunkr
var exampleNameParts = data.manifest.name.split('-');
exampleNameParts.unshift('AngularJS');
angular.forEach(exampleNameParts, function(part, index) {
exampleNameParts[index] = part.charAt(0).toUpperCase() + part.substr(1);
});
ctrl.example.name = exampleNameParts.join(' - ');
return ctrl.example;
});
};
ctrl.open = function(clickEvent) {
var newWindow = clickEvent.ctrlKey || clickEvent.metaKey;
var postData = {
'tags[0]': "angularjs",
'tags[1]': "example",
'private': true
};
// Make sure the example data is available.
// If an XHR must be made, this might break some pop-up blockers when
// new window is requested
ctrl.prepareExampleData()
.then(function() {
angular.forEach(ctrl.example.files, function(file) {
postData['files[' + file.name + ']'] = file.content + createCopyrightNotice(file.name);
});
postData.description = ctrl.example.name;
formPostData('http://plnkr.co/edit/?p=preview', newWindow, postData);
});
};
// Initialize the example data, so it's ready when clicking the open button.
// Otherwise pop-up blockers will prevent a new window from opening
ctrl.prepareExampleData(ctrl.example.path);
}]
};
}])
.factory('getExampleData', ['$http', '$q', function($http, $q) {
return function(exampleFolder){
// Load the manifest for the example
return $http.get(exampleFolder + '/manifest.json')
$http.get(exampleFolder + '/manifest.json')
.then(function(response) {
return response.data;
})
.then(function(manifest) {
var filePromises = [];
// 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(' - ');
angular.forEach(manifest.files, function(filename) {
filePromises.push($http.get(exampleFolder + '/' + filename, { transformResponse: [] })
.then(function(response) {
@@ -183,7 +52,7 @@ angular.module('examples', [])
// The manifests provide the production index file but Plunkr wants
// a straight index.html
if (filename === "index-production.html") {
filename = "index.html";
filename = "index.html"
}
return {
@@ -192,11 +61,21 @@ angular.module('examples', [])
};
}));
});
return $q.all(filePromises);
})
.then(function(files) {
var postData = {};
return $q.all({
manifest: manifest,
files: $q.all(filePromises)
angular.forEach(files, function(file) {
postData['files[' + file.name + ']'] = file.content;
});
postData['tags[0]'] = "angularjs";
postData['tags[1]'] = "example";
postData.private = true;
postData.description = exampleName;
formPostData('http://plnkr.co/edit/?p=preview', newWindow, postData);
});
};
}]);
}]);
+1 -9
View File
@@ -11,15 +11,7 @@ angular.module('search', [])
var MIN_SEARCH_LENGTH = 2;
if(q.length >= MIN_SEARCH_LENGTH) {
docsSearch(q).then(function(hits) {
// Make sure the areas are always in the same order
var results = {
api: [],
guide: [],
tutorial: [],
error: [],
misc: []
};
var results = {};
angular.forEach(hits, function(hit) {
var area = hit.area;
+2 -2
View File
@@ -35,7 +35,7 @@ angular.module('tutorials', [])
'step': '@docTutorialReset'
},
template:
'<p><button class="btn" ng-click="show=!show">Workspace Reset Instructions ➤</button></p>\n' +
'<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' +
@@ -43,7 +43,7 @@ angular.module('tutorials', [])
'<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}}" title="See diff on Github">GitHub</a>\n' +
'<a ng-href="https://github.com/angular/angular-phonecat/compare/step-{{step ? (step - 1): \'0~1\'}}...step-{{step}}">GitHub</a>\n' +
'</p>'
};
});
+6 -8
View File
@@ -21,13 +21,11 @@ angular.module('versions', [])
};
$scope.jumpToDocsVersion = function(version) {
var currentPagePath = $location.path().replace(/\/$/, ''),
url = '';
if (version.isOldDocsUrl) {
url = version.docsUrl;
}else{
url = version.docsUrl + currentPagePath;
}
$window.location = url;
var currentPagePath = $location.path().replace(/\/$/, '');
// TODO: We need to do some munging of the path for different versions of the API...
$window.location = version.docsUrl + currentPagePath;
};
}]);
-25
View File
@@ -1,25 +0,0 @@
{
"extends": "../../../.jshintrc-base",
"browser": true,
"globals": {
// AngularJS
"angular": false,
// ngMocks
"module": false,
"inject": true,
// Jasmine
"jasmine": false,
"describe": false,
"ddescribe": false,
"xdescribe": false,
"it": false,
"iit": false,
"xit": false,
"beforeEach": false,
"afterEach": false,
"spyOn": false,
"expect": false
}
}
+2 -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', {});
@@ -25,7 +26,7 @@ describe("DocsController", function() {
it("should update the Google Analytics with $location.path if currentPage is missing", inject(function($window, $location) {
$window._gaq = [];
spyOn($location, 'path').and.returnValue('x/y/z');
spyOn($location, 'path').andReturn('x/y/z');
$scope.$broadcast('$includeContentLoaded');
expect($window._gaq.pop()).toEqual(['_trackPageview', 'x/y/z']);
}));
-166
View File
@@ -1,166 +0,0 @@
'use strict';
describe('errors', function() {
// Mock `ngSanitize` module
angular.
module('ngSanitize', []).
value('$sanitize', jasmine.createSpy('$sanitize').and.callFake(angular.identity));
beforeEach(module('errors'));
describe('errorDisplay', function() {
var $sanitize;
var errorLinkFilter;
beforeEach(inject(function(_$sanitize_, _errorLinkFilter_) {
$sanitize = _$sanitize_;
errorLinkFilter = _errorLinkFilter_;
}));
it('should return empty input unchanged', function() {
var inputs = [undefined, null, false, 0, ''];
var remaining = inputs.length;
inputs.forEach(function(falsyValue) {
expect(errorLinkFilter(falsyValue)).toBe(falsyValue);
remaining--;
});
expect(remaining).toBe(0);
});
it('should recognize URLs and convert them to `<a>`', function() {
var urls = [
['ftp://foo/bar?baz#qux'],
['http://foo/bar?baz#qux'],
['https://foo/bar?baz#qux'],
['mailto:foo_bar@baz.qux', null, 'foo_bar@baz.qux'],
['foo_bar@baz.qux', 'mailto:foo_bar@baz.qux', 'foo_bar@baz.qux']
];
var remaining = urls.length;
urls.forEach(function(values) {
var actualUrl = values[0];
var expectedUrl = values[1] || actualUrl;
var expectedText = values[2] || expectedUrl;
var anchor = '<a href="' + expectedUrl + '">' + expectedText + '</a>';
var input = 'start ' + actualUrl + ' end';
var output = 'start ' + anchor + ' end';
expect(errorLinkFilter(input)).toBe(output);
remaining--;
});
expect(remaining).toBe(0);
});
it('should not recognize stack-traces as URLs', function() {
var urls = [
'ftp://foo/bar?baz#qux:4:2',
'http://foo/bar?baz#qux:4:2',
'https://foo/bar?baz#qux:4:2',
'mailto:foo_bar@baz.qux:4:2',
'foo_bar@baz.qux:4:2'
];
var remaining = urls.length;
urls.forEach(function(url) {
var input = 'start ' + url + ' end';
expect(errorLinkFilter(input)).toBe(input);
remaining--;
});
expect(remaining).toBe(0);
});
it('should should set `[target]` if specified', function() {
var url = 'https://foo/bar?baz#qux';
var target = '_blank';
var outputWithoutTarget = '<a href="' + url + '">' + url + '</a>';
var outputWithTarget = '<a target="' + target + '" href="' + url + '">' + url + '</a>';
expect(errorLinkFilter(url)).toBe(outputWithoutTarget);
expect(errorLinkFilter(url, target)).toBe(outputWithTarget);
});
it('should truncate the contents of the generated `<a>` to 60 characters', function() {
var looongUrl = 'https://foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo';
var truncatedUrl = 'https://foooooooooooooooooooooooooooooooooooooooooooooooo...';
var output = '<a href="' + looongUrl + '">' + truncatedUrl + '</a>';
expect(looongUrl.length).toBeGreaterThan(60);
expect(truncatedUrl.length).toBe(60);
expect(errorLinkFilter(looongUrl)).toBe(output);
});
it('should pass the final string through `$sanitize`', function() {
$sanitize.calls.reset();
var input = 'start https://foo/bar?baz#qux end';
var output = errorLinkFilter(input);
expect($sanitize).toHaveBeenCalledTimes(1);
expect($sanitize).toHaveBeenCalledWith(output);
});
});
describe('errorDisplay', function() {
var $compile;
var $location;
var $rootScope;
var errorLinkFilter;
beforeEach(module(function($provide) {
$provide.decorator('errorLinkFilter', function() {
errorLinkFilter = jasmine.createSpy('errorLinkFilter');
errorLinkFilter.and.callFake(angular.identity);
return errorLinkFilter;
});
}));
beforeEach(inject(function(_$compile_, _$location_, _$rootScope_) {
$compile = _$compile_;
$location = _$location_;
$rootScope = _$rootScope_;
}));
it('should set the element\s HTML', function() {
var elem = $compile('<span error-display="bar">foo</span>')($rootScope);
expect(elem.html()).toBe('bar');
});
it('should interpolate the contents against `$location.search()`', function() {
spyOn($location, 'search').and.returnValue({p0: 'foo', p1: 'bar'});
var elem = $compile('<span error-display="foo = {0}, bar = {1}"></span>')($rootScope);
expect(elem.html()).toBe('foo = foo, bar = bar');
});
it('should pass the interpolated text through `errorLinkFilter`', function() {
$location.search = jasmine.createSpy('search').and.returnValue({p0: 'foo'});
var elem = $compile('<span error-display="foo = {0}"></span>')($rootScope);
expect(errorLinkFilter).toHaveBeenCalledTimes(1);
expect(errorLinkFilter).toHaveBeenCalledWith('foo = foo', '_blank');
});
it('should encode `<` and `>`', function() {
var elem = $compile('<span error-display="&lt;xyz&gt;"></span>')($rootScope);
expect(elem.text()).toBe('<xyz>');
});
});
});
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "AngularJS-docs-app",
"dependencies": {
"jquery": "2.1.1",
"lunr.js": "0.5.12",
"lunr.js": "0.4.3",
"open-sans-fontface": "1.0.4",
"google-code-prettify": "1.0.1",
"bootstrap": "3.1.1"
+1 -5
View File
@@ -42,7 +42,7 @@ module.exports = new Package('angularjs', [
readFilesProcessor.basePath = path.resolve(__dirname,'../..');
readFilesProcessor.sourceFiles = [
{ include: 'src/**/*.js', exclude: 'src/angular.bind.js', basePath: 'src' },
{ include: 'src/**/*.js', basePath: 'src' },
{ include: 'docs/content/**/*.ngdoc', basePath: 'docs/content' }
];
@@ -170,8 +170,4 @@ module.exports = new Package('angularjs', [
jqueryDeployment,
productionDeployment
];
})
.config(function(generateKeywordsProcessor) {
generateKeywordsProcessor.docTypesToIgnore = ['componentGroup'];
});
+20 -27
View File
@@ -16,11 +16,9 @@ module.exports = function generateKeywordsProcessor(log, readFilesProcessor) {
ignoreWordsFile: undefined,
areasToSearch: ['api', 'guide', 'misc', 'error', 'tutorial'],
propertiesToIgnore: [],
docTypesToIgnore: [],
$validate: {
ignoreWordsFile: { },
areasToSearch: { presence: true },
docTypesToIgnore: { },
propertiesToIgnore: { }
},
$runAfter: ['memberDocsProcessor'],
@@ -30,7 +28,6 @@ module.exports = function generateKeywordsProcessor(log, readFilesProcessor) {
// Keywords to ignore
var wordsToIgnore = [];
var propertiesToIgnore;
var docTypesToIgnore;
var areasToSearch;
// Keywords start with "ng:" or one of $, _ or a letter
@@ -50,8 +47,6 @@ module.exports = function generateKeywordsProcessor(log, readFilesProcessor) {
areasToSearch = _.indexBy(this.areasToSearch);
propertiesToIgnore = _.indexBy(this.propertiesToIgnore);
log.debug('Properties to ignore', propertiesToIgnore);
docTypesToIgnore = _.indexBy(this.docTypesToIgnore);
log.debug('Doc types to ignore', docTypesToIgnore);
var ignoreWordsMap = _.indexBy(wordsToIgnore);
@@ -83,36 +78,34 @@ module.exports = function generateKeywordsProcessor(log, readFilesProcessor) {
// We are only interested in docs that live in the right area
docs = _.filter(docs, function(doc) { return areasToSearch[doc.area]; });
docs = _.filter(docs, function(doc) { return !docTypesToIgnore[doc.docType]; });
_.forEach(docs, function(doc) {
var words = [];
var keywordMap = _.clone(ignoreWordsMap);
var members = [];
var membersMap = {};
var words = [];
var keywordMap = _.clone(ignoreWordsMap);
var members = [];
var membersMap = {};
// Search each top level property of the document for search terms
_.forEach(doc, function(value, key) {
// Search each top level property of the document for search terms
_.forEach(doc, function(value, key) {
if ( _.isString(value) && !propertiesToIgnore[key] ) {
extractWords(value, words, keywordMap);
}
if ( _.isString(value) && !propertiesToIgnore[key] ) {
extractWords(value, words, keywordMap);
}
if ( key === 'methods' || key === 'properties' || key === 'events' ) {
_.forEach(value, function(member) {
extractWords(member.name, members, membersMap);
});
}
});
if ( key === 'methods' || key === 'properties' || key === 'events' ) {
_.forEach(value, function(member) {
extractWords(member.name, members, membersMap);
});
}
});
doc.searchTerms = {
titleWords: extractTitleWords(doc.name),
keywords: _.sortBy(words).join(' '),
members: _.sortBy(members).join(' ')
};
doc.searchTerms = {
titleWords: extractTitleWords(doc.name),
keywords: _.sortBy(words).join(' '),
members: _.sortBy(members).join(' ')
};
});
@@ -18,6 +18,7 @@ module.exports = function debugDeployment(getVersion) {
'../angular-touch.js',
'../angular-animate.js',
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
'js/angular-bootstrap/bootstrap.js',
'js/angular-bootstrap/dropdown-toggle.js',
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
@@ -18,6 +18,7 @@ module.exports = function defaultDeployment(getVersion) {
'../angular-touch.min.js',
'../angular-animate.min.js',
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
'js/angular-bootstrap/bootstrap.min.js',
'js/angular-bootstrap/dropdown-toggle.min.js',
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
+1
View File
@@ -22,6 +22,7 @@ module.exports = function jqueryDeployment(getVersion) {
'../angular-touch.min.js',
'../angular-animate.min.js',
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
'js/angular-bootstrap/bootstrap.min.js',
'js/angular-bootstrap/dropdown-toggle.min.js',
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
@@ -21,6 +21,7 @@ module.exports = function productionDeployment(getVersion) {
cdnUrl + '/angular-touch.min.js',
cdnUrl + '/angular-animate.min.js',
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
'js/angular-bootstrap/bootstrap.min.js',
'js/angular-bootstrap/dropdown-toggle.min.js',
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
+11 -11
View File
@@ -3,7 +3,7 @@
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="Description"
content="AngularJS is what HTML would have been, had it been designed for building web-apps.
Declarative templates with data-binding, MVC, dependency injection and great
@@ -76,7 +76,7 @@
<div class="row">
<div class="col-md-9 header-branding">
<a class="brand navbar-brand" href="http://angularjs.org">
<img width="117" height="30" class="logo" alt="Link to Angular JS Homepage" ng-src="img/angularjs-for-header-only.svg">
<img width="117" height="30" class="logo" ng-src="img/angularjs-for-header-only.svg">
</a>
<ul class="nav navbar-nav">
<li class="divider-vertical"></li>
@@ -90,7 +90,7 @@
<li class="disabled"><a href="http://angularjs.org/">Why AngularJS?</a></li>
<li><a href="http://www.youtube.com/user/angularjs">Watch</a></li>
<li><a href="tutorial">Tutorial</a></li>
<li><a href="https://www.madewithangular.com/">Case Studies</a></li>
<li><a href="http://builtwith.angularjs.org/">Case Studies</a></li>
<li><a href="https://github.com/angular/angular-seed">Seed App project template</a></li>
<li><a href="misc/faq">FAQ</a></li>
</ul>
@@ -147,13 +147,13 @@
<div class="search-results-container" ng-show="hasResults">
<div class="container">
<div class="search-results-frame">
<div ng-repeat="(key, value) in results track by key" class="search-results-group" ng-class="colClassName + ' col-group-' + key" ng-show="value.length > 0">
<div ng-repeat="(key, value) in results" class="search-results-group" ng-class="colClassName + ' col-group-' + key">
<h4 class="search-results-group-heading">{{ key }}</h4>
<ul class="search-results">
<!-- Do not insert a line break between li and a. Chrome will insert an actual line-break, which breaks the list item view.
TODO: use a html minifier instead -->
<li ng-repeat="item in value" class="search-result"><a ng-click="hideResults()" ng-href="{{ item.path }}">{{ item.name }}</a></li>
</ul>
<div class="search-results">
<div ng-repeat="item in value" class="search-result">
- <a ng-click="hideResults()" ng-href="{{ item.path }}">{{ item.name }}</a>
</div>
</div>
</div>
</div>
<a href="" ng-click="hideResults()" class="search-close">
@@ -220,10 +220,10 @@
<p class="pull-right"><a back-to-top>Back to top</a></p>
<p>
Super-powered by Google ©2010-2016
Super-powered by Google ©2010-2014
( <a id="version"
ng-href="https://github.com/angular/angular.js/blob/master/CHANGELOG.md#{{versionNumber}}"
ng-bind-template="v{{version}}" title="Changelog of this version of Angular JS">
ng-bind-template="v{{version}}">
</a>
)
</p>
-60
View File
@@ -1,60 +0,0 @@
{% macro typeList(types) -%}
{% for typeName in types %}<a href="" class="{$ typeName | typeClass $}">{$ typeName | escape $}</a>{% endfor %}
{%- endmacro -%}
{%- macro paramTable(params) %}
<table class="variables-matrix input-arguments">
<thead>
<tr>
<th>Param</th>
<th>Type</th>
<th>Details</th>
</tr>
</thead>
<tbody>
{% for param in params %}
<tr>
<td>
{$ param.name $}
{% if param.alias %}| {$ param.alias $}{% endif %}
{% if param.optional %}<div><em>(optional)</em></div>{% endif %}
</td>
<td>
{$ typeList(param.typeList) $}
</td>
<td>
{$ param.description | marked $}
{% if param.defaultValue %}<p><em>(default: {$ param.defaultValue $})</em></p>{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endmacro -%}
{%- macro directiveParam(name, type, join, sep) %}
{%- if type.optional %}[{% endif -%}
{$ name | dashCase $}{$ join $}{$ type.name $}{$ sep $}
{%- if type.optional %}]{% endif -%}
{% endmacro -%}
{%- macro functionSyntax(fn) %}
{%- set sep = joiner(', ') -%}
{% marked -%}
`{$ fn.name $}({%- for param in fn.params %}{$ sep() $}
{%- if param.type.optional %}[{% endif -%}
{$ param.name $}
{%- if param.type.optional %}]{% endif -%}
{% endfor %});`
{%- endmarked %}
{% endmacro -%}
{%- macro typeInfo(fn) -%}
<table class="variables-matrix return-arguments">
<tr>
<td>{$ typeList(fn.typeList) $}</td>
<td>{$ fn.description | marked $}</td>
</tr>
</table>
{%- endmacro -%}
@@ -1,8 +1,10 @@
{# Be aware that we need these extra new lines here or marked will not realize that the <div>
{# 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>
<plnkr-opener example-path="{$ doc.path $}"></plnkr-opener>
<a ng-click="openPlunkr('{$ doc.path $}', $event)" class="btn pull-right">
<i class="glyphicon glyphicon-edit">&nbsp;</i>
Edit in Plunker</a>
<div class="runnable-example"
path="{$ doc.example.deployments.default.path $}"
@@ -22,5 +24,5 @@
</div>
</div>
{# Be aware that we need these extra new lines here or marked will not realize that the <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 #}
+1 -1
View File
@@ -140,7 +140,7 @@ or JavaScript callbacks.
{@link ngAnimate#service Services / Factories}
</td>
<td>
Use {@link ng.$animate $animate} to trigger animation operations within your directive code.
Use {@link ngAnimate.$animate $animate} to trigger animation operations within your directive code.
</td>
</tr>
<tr>
+1 -1
View File
@@ -5,4 +5,4 @@
This error occurs when the name of a directive is not valid.
Directives must start with a lowercase character and must not contain leading or trailing whitespaces.
Directives must start with a lowercase character.
+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 an ancestor element, make sure 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 -6
View File
@@ -3,7 +3,7 @@
@fullName Invalid Isolate Scope Definition
@description
When declaring isolate scope the scope definition object must be in specific format which starts with mode character (`@&=<`), after which comes an optional `?`, and it ends with an optional local name.
When declaring isolate scope the scope definition object must be in specific format which starts with mode character (`@&=`) with an optional local name.
```
myModule.directive('directiveName', function factory() {
@@ -12,11 +12,9 @@ myModule.directive('directiveName', function factory() {
scope: {
'attrName': '@', // OK
'attrName2': '=localName', // OK
'attrName3': '<?localName', // OK
'attrName4': ' = name', // OK
'attrName5': 'name', // ERROR: missing mode @&=
'attrName6': 'name=', // ERROR: must be prefixed with @&=
'attrName7': '=name?', // ERROR: ? must come directly after the mode
'attrName3': 'name', // ERROR: missing mode @&=
'attrName4': ' = name', // ERROR: extra spaces
'attrName5': 'name=', // ERROR: must be prefixed with @&=
}
...
}
-12
View File
@@ -1,12 +0,0 @@
@ngdoc error
@name $compile:noctrl
@fullName Controller is required.
@description
When using the `bindToController` feature of AngularJS, a directive is required
to have a Controller. A controller may be specified by adding a "controller"
property to the directive definition object. Its value should be either a
string, or an invokable object (a function, or an array whose last element is a
function).
For more information, see the {@link guide/directive directives guide}.
-71
View File
@@ -1,71 +0,0 @@
@ngdoc error
@name $compile:noident
@fullName Controller identifier is required.
@description
When using the `bindToController` feature of AngularJS, a directive is required
to have a Controller identifier, which is initialized in scope with the value of
the controller instance. This can be supplied using the "controllerAs" property
of the directive object, or alternatively by adding " as IDENTIFIER" to the controller
name.
For example, the following directives are valid:
```js
// OKAY, because controller is a string with an identifier component.
directive("okay", function() {
return {
bindToController: true,
controller: "myCtrl as $ctrl"
scope: {
text: "@text"
}
};
});
// OKAY, because the directive uses the controllerAs property to override
// the controller identifier.
directive("okay2", function() {
return {
bindToController: true,
controllerAs: "$ctrl",
controller: function() {
},
scope: {
text: "@text"
}
};
});
```
While the following are invalid:
```js
// BAD, because the controller property is a string with no identifier.
directive("bad", function() {
return {
bindToController: true,
controller: "noIdentCtrl",
scope: {
text: "@text"
}
};
});
// BAD because the controller is not a string (therefore has no identifier),
// and there is no controllerAs property.
directive("bad2", function() {
return {
bindToController: true,
controller: function noControllerAs() {
},
scope: {
text: "@text"
}
};
});
```
+1 -17
View File
@@ -36,25 +36,9 @@ Following are invalid uses of this directive:
```
To resolve this error, do one of the following options:
- use path expressions with scope properties that are two-way data-bound like so:
To resolve this error, always use path expressions with scope properties that are two-way data-bound:
```
<my-directive bind="some.property">
<my-directive bind="some[3]['property']">
```
- Make the binding optional
```
myModule.directive('myDirective', function factory() {
return {
...
scope: {
localValue: '=?bind' // <-- the '?' makes it optional
}
...
}
});
```
+2 -2
View File
@@ -31,7 +31,7 @@ single root element, like the `div` element in this template:
<div><b>Hello</b> World!</div>
```
An invalid template to be used with this directive is one that defines multiple root nodes or
An an invalid template to be used with this directive is one that defines multiple root nodes or
elements. For example:
```
@@ -43,7 +43,7 @@ well. Consider the following template:
```
<div class='container'>
<div class='wrapper'>
<div class='wrapper>
...
</div> <!-- wrapper -->
</div> <!-- container -->
-45
View File
@@ -1,45 +0,0 @@
@ngdoc error
@name $http:legacy
@fullName The `success` and `error` methods on the promise returned from `$http` have been disabled.
@description
This error occurs when the legacy promise extensions (`success` and `error`)
{@link $httpProvider#useLegacyPromiseExtensions legacy `$http` promise extensions} have been disabled.
To resolve this error, either turn on the legacy extensions by adding
`$httpProvider.useLegacyPromiseExtensions(true);` to your application's configuration; or refactor you
use of `$http` to use `.then()` rather than `.success()` and `.error()`.
For example if you code looked like this:
```js
// Simple GET request example :
$http.get('/someUrl').
success(function(data, status, headers, config) {
// This callback will be called asynchronously
// when the response is available
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
```
then you would change it to look like:
```js
// Simple GET request example :
$http.get('/someUrl').
then(function(response) {
// (The response object contains the data, status, headers and config properties)
// This callback will be called asynchronously
// when the response is available.
}, function(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
```
For more information, see the
{@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`}
documentation.
+1 -4
View File
@@ -6,9 +6,6 @@
This error occurs when a module fails to load due to some exception. The error
message above should provide additional context.
A common reason why the module fails to load is that you've forgotten to
include the file with the defined module or that the file couldn't be loaded.
### Using `ngRoute`
In AngularJS `1.2.0` and later, `ngRoute` has been moved to its own module.
@@ -27,4 +24,4 @@ angular.module('ng').filter('tel', function (){});
Instead create your own module and add it as a dependency to your application's top-level module.
See [#9692](https://github.com/angular/angular.js/issues/9692) and
[#7709](https://github.com/angular/angular.js/issues/7709) for more information
[#7709](https://github.com/angular/angular.js/issues/7709) for more information
-15
View File
@@ -69,18 +69,3 @@ angular.module('myModule', [])
```
Use the `$controller` service if you want to instantiate controllers yourself.
Attempting to inject a scope object into anything that's not a controller or a directive,
for example a service, will also throw an `Unknown provider: $scopeProvider <- $scope` error.
This might happen if one mistakenly registers a controller as a service, ex.:
```
angular.module('myModule', [])
.service('MyController', ['$scope', function($scope) {
// This controller throws an unknown provider error because
// a scope object cannot be injected into a service.
}]);
```
If you encounter this error only with minified code, consider using `ngStrictDi` (see
{@link ng.directive:ngApp ngApp}) to provoke the error with the non-minified source.
@@ -1,6 +0,0 @@
@ngdoc error
@name $interpolate:badexpr
@fullName Expecting end operator
@description
The Angular expression is missing the corresponding closing operator.
@@ -1,11 +0,0 @@
@ngdoc error
@name $interpolate:dupvalue
@fullName Duplicate choice in plural/select
@description
You have repeated a match selection for your plural or select MessageFormat
extension in your interpolation expression. The different choices have to be unique.
For more information about the MessageFormat syntax in interpolation
expressions, please refer to MessageFormat extensions section at
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
@@ -1,12 +0,0 @@
@ngdoc error
@name $interpolate:logicbug
@fullName Bug in ngMessageFormat module
@description
You've just hit a bug in the ngMessageFormat module provided by angular-message-format.min.js.
Please file a github issue for this and provide the interpolation text that caused you to hit this
bug mentioning the exact version of AngularJS used and we will fix it!
For more information about the MessageFormat syntax in interpolation
expressions, please refer to MessageFormat extensions section at
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
@@ -1,17 +0,0 @@
@ngdoc error
@name $interpolate:nochgmustache
@fullName Redefinition of start/endSymbol incompatible with MessageFormat extensions
@description
You have redefined `$interpolate.startSymbol`/`$interpolate.endSymbol` and also
loaded the `ngMessageFormat` module (provided by angular-message-format.min.js)
while creating your injector.
`ngMessageFormat` currently does not support redefinition of the
startSymbol/endSymbol used by `$interpolate`. If this is affecting you, please
file an issue and mention @chirayuk on it. This is intended to be fixed in a
future commit and the github issue will help gauge urgency.
For more information about the MessageFormat syntax in interpolation
expressions, please refer to MessageFormat extensions section at
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
@@ -1,12 +0,0 @@
@ngdoc error
@name $interpolate:reqarg
@fullName Missing required argument for MessageFormat
@description
You must specify the MessageFormat function that you're using right after the
comma following the Angular expression. Currently, the supported functions are
"plural" and "select" (for gender selections.)
For more information about the MessageFormat syntax in interpolation
expressions, please refer to MessageFormat extensions section at
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
@@ -1,11 +0,0 @@
@ngdoc error
@name $interpolate:reqcomma
@fullName Missing comma following MessageFormat plural/select keyword
@description
The MessageFormat syntax requires a comma following the "plural" or "select"
extension keyword in the extended interpolation syntax.
For more information about the MessageFormat syntax in interpolation
expressions, please refer to MessageFormat extensions section at
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
@@ -1,11 +0,0 @@
@ngdoc error
@name $interpolate:reqendbrace
@fullName Unterminated message for plural/select value
@description
The plural or select message for a value or keyword choice has no matching end
brace to mark the end of the message.
For more information about the MessageFormat syntax in interpolation
expressions, please refer to MessageFormat extensions section at
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
@@ -1,6 +0,0 @@
@ngdoc error
@name $interpolate:reqendinterp
@fullName Unterminated interpolation
@description
The interpolation text does not have an ending `endSymbol` ("}}" by default) and is unterminated.
@@ -1,12 +0,0 @@
@ngdoc error
@name $interpolate:reqopenbrace
@fullName An opening brace was expected but not found
@description
The plural or select extension keyword or values (such as "other", "male",
"female", "=0", "one", "many", etc.) MUST be followed by a message enclosed in
braces.
For more information about the MessageFormat syntax in interpolation
expressions, please refer to MessageFormat extensions section at
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
@@ -1,13 +0,0 @@
@ngdoc error
@name $interpolate:reqother
@fullName Required choice "other" for select/plural in MessageFormat
@description
Your interpolation expression with a MessageFormat extension for either
"plural" or "select" (typically used for gender selection) does not contain a
message for the choice "other". Using either select or plural MessageFormat
extensions require that you provide a message for the selection "other".
For more information about the MessageFormat syntax in interpolation
expressions, please refer to MessageFormat extensions section at
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
@@ -1,12 +0,0 @@
@ngdoc error
@name $interpolate:unknarg
@fullName Unrecognized MessageFormat extension
@description
The MessageFormat extensions provided by `ngMessageFormat` are currently
limited to "plural" and "select". The extension that you have used is either
unsupported or invalid.
For more information about the MessageFormat syntax in interpolation
expressions, please refer to MessageFormat extensions section at
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
@@ -1,10 +0,0 @@
@ngdoc error
@name $interpolate:unsafe
@fullName MessageFormat extensions not allowed in secure context
@description
You have attempted to use a MessageFormat extension in your interpolation expression that is marked as a secure context. For security purposes, this is not supported.
Read more about secure contexts at {@link ng.$sce Strict Contextual Escaping
(SCE)} and about the MessageFormat extensions at {@link
guide/i18n#MessageFormat Angular i18n MessageFormat}.
@@ -1,6 +0,0 @@
@ngdoc error
@name $interpolate:untermstr
@fullName Unterminated string literal
@description
The string literal was not terminated in your Angular expression.
@@ -1,8 +0,0 @@
@ngdoc error
@name $interpolate:wantstring
@fullName Expected the beginning of a string
@description
We expected to see the beginning of a string (either a single quote or a double
quote character) in the expression but it was not found. The expression is
invalid. If this is incorrect, please file an issue on github.
+3 -3
View File
@@ -1,6 +1,6 @@
@ngdoc error
@name $location:nobase
@fullName $location in HTML5 mode requires a &lt;base&gt; tag to be present!
@fullName $location in HTML5 mode requires a <base> tag to be present!
@description
If you configure {@link ng.$location `$location`} to use
@@ -15,7 +15,7 @@ $locationProvider.html5Mode({
});
```
Note that removing the requirement for a `<base>` tag will have adverse side effects when resolving
Note that removing the requirement for a <base> tag will have adverse side effects when resolving
relative paths with `$location` in IE9.
The base URL is then used to resolve all relative URLs throughout the application regardless of the
@@ -35,7 +35,7 @@ URL of the subcontext:
```html
<head>
<base href="/subapp/">
<base href="/subapp">
...
</head>
```
@@ -14,32 +14,3 @@ perform this check - it's up to the developer to not expose such sensitive and p
directly on the scope chain.
To resolve this error, avoid Window access.
### Common CoffeeScript Issue
Be aware that if you are using CoffeeScript, it automatically returns the value of the last statement in a
function. So for instance
```coffeescript
scope.foo = ->
window.open 'https://example.com'
```
compiles to something like
```js
scope.foo = function() {
return window.open('https://example.com');
};
```
You can see that this function will return the result of calling `window.open`, which is a `Window`
object.
You can avoid this by explicitly returning something else from the function:
```coffeescript
scope.foo = ->
window.open 'https://example.com'
return true;
```
+3 -3
View File
@@ -100,7 +100,7 @@ To resolve this type of issue, either fix the api to be always synchronous or as
your callback handler to always run asynchronously by using the `$timeout` service.
```
function MyController($scope, $timeout, thirdPartyComponent) {
function MyController($scope, thirdPartyComponent) {
thirdPartyComponent.getData(function(someData) {
$timeout(function() {
$scope.someData = someData;
@@ -161,7 +161,7 @@ In this second scenario, we are already inside a `$digest` when the ngFocus dire
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 an
by using `$timeout(fn, 0, false)`, where the `false` value tells Angular not to wrap this `fn` in a
`$apply` block:
```
@@ -200,7 +200,7 @@ the top of the call stack.
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 an `$apply`/`$digest`. It may be a simple oversight or maybe it fits with the
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
@@ -0,0 +1,11 @@
@ngdoc error
@name $sanitize:badparse
@fullName Parsing Error while Sanitizing
@description
This error occurs when the HTML string passed to '$sanitize' can't be parsed by the sanitizer.
The error contains part of the html string that can't be parsed.
The parser is more strict than a typical browser parser, so it's possible that some obscure input would produce this error despite the string being recognized as valid HTML by a browser.
If a valid html code results in this error, please file a bug.
@@ -1,10 +0,0 @@
@ngdoc error
@name $sanitize:noinert
@fullName Can't create an inert html document
@description
This error occurs when `$sanitize` sanitizer determines that `document.implementation.createHTMLDocument ` api is not supported by the current browser.
This api is necessary for safe parsing of HTML strings into DOM trees and without it the sanitizer can't sanitize the input.
The api is present in all supported browsers including IE 9.0, so the presence of this error usually indicates that Angular's `$sanitize` is being used on an unsupported platform.
-13
View File
@@ -1,13 +0,0 @@
@ngdoc error
@name $sanitize:uinput
@fullName Failed to sanitize html because the input is unstable
@description
This error occurs when `$sanitize` sanitizer tries to check the input for possible mXSS payload and the verification
errors due to the input mutating indefinitely. This could be a sign that the payload contains code exploiting an mXSS
vulnerability in the browser.
mXSS attack exploit browser bugs that cause some browsers parse a certain html strings into DOM, which once serialized
doesn't match the original input. These browser bugs can be exploited by attackers to create payload which looks
harmless to sanitizers, but due to mutations caused by the browser are turned into dangerous code once processed after
sanitization.
-51
View File
@@ -1,51 +0,0 @@
@ngdoc error
@name filter:notarray
@fullName Not an array
@description
This error occurs when {@link ng.filter filter} is not used with an array:
```html
<input ng-model="search">
<div ng-repeat="(key, value) in myObj | filter:search">
{{ key }} : {{ value }}
</div>
```
Filter must be used with an array so a subset of items can be returned.
The array can be initialized asynchronously and therefore null or undefined won't throw this error.
To filter an object by the value of its properties you can create your own custom filter:
```js
angular.module('customFilter',[])
.filter('custom', function() {
return function(input, search) {
if (!input) return input;
if (!search) return input;
var expected = ('' + search).toLowerCase();
var result = {};
angular.forEach(input, function(value, key) {
var actual = ('' + value).toLowerCase();
if (actual.indexOf(expected) !== -1) {
result[key] = value;
}
});
return result;
}
});
```
That can be used as:
```html
<input ng-model="search">
<div ng-repeat="(key, value) in myObj | custom:search">
{{ key }} : {{ value }}
</div>
```
You could as well convert the object to an array using a filter such as
[toArrayFilter](https://github.com/petebacondarwin/angular-toArrayFilter):
```html
<input ng-model="search">
<div ng-repeat="item in myObj | toArray:false | filter:search">
{{ item }}
</div>
```
-16
View File
@@ -1,16 +0,0 @@
@ngdoc error
@name linky:notstring
@fullName Not a string
@description
This error occurs when {@link ngSanitize.linky linky} is used with a non-empty, non-string value:
```html
<div ng-bind-html="42 | linky"></div>
```
`linky` is supposed to be used with string values only, and therefore assumes that several methods
(such as `.match()`) are available on the passed in value.
The value can be initialized asynchronously and therefore null or undefined won't throw this error.
If you want to pass non-string values to `linky` (e.g. Objects whose `.toString()` should be
utilized), you need to manually convert them to strings.
-7
View File
@@ -1,7 +0,0 @@
@ngdoc error
@name ng:cpta
@fullName Copying TypedArray
@description
Copying TypedArray's with a destination is not supported because TypedArray
objects can not be mutated, they are fixed length.
@@ -1,28 +0,0 @@
@ngdoc error
@name ngModel:nopromise
@fullName No promise
@description
The return value of an async validator, must always be a promise. If you want to return a
non-promise value, you can convert it to a promise using {@link ng.$q#resolve `$q.resolve()`} or
{@link ng.$q#reject `$q.reject()`}.
Example:
```
.directive('asyncValidator', function($q) {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ngModel) {
ngModel.$asyncValidators.myAsyncValidation = function(modelValue, viewValue) {
if (/* I don't need to hit the backend API */) {
return $q.resolve(); // to mark as valid or
// return $q.reject(); // to mark as invalid
} else {
// ...send a request to the backend and return a promise
}
};
}
};
})
```
-52
View File
@@ -1,52 +0,0 @@
@ngdoc error
@name orderBy:notarray
@fullName Value is not array-like
@description
This error occurs when {@link ng.orderBy orderBy} is not passed an array-like value:
```html
<div ng-repeat="(key, value) in myObj | orderBy:someProp">
{{ key }} : {{ value }}
</div>
```
`orderBy` must be used with an array-like value so a subset of items can be returned.
The array can be initialized asynchronously and therefore `null` or `undefined` won't throw this error.
To use `orderBy` to order the properties of an object, you can create your own array based on that object:
```js
angular.module('aModule', [])
.controller('aController', function($scope) {
var myObj = {
one: {id: 1, name: 'Some thing'},
two: {id: 2, name: 'Another thing'},
three: {id: 3, name: 'A third thing'}
};
$scope.arrFromMyObj = Object.keys(myObj).map(function(key) {
return myObj[key];
});
});
```
That can be used as:
```html
<label>
Order by:
<select ng-model="orderProp" ng-options="prop for prop in ['id', 'name']"></select>
</label>
<div ng-repeat="item in arrFromMyObj | orderBy:orderProp">
[{{ item.id }}] {{ item.name }}
</div>
```
You could as well convert the object to an array using a filter such as
[toArrayFilter](https://github.com/petebacondarwin/angular-toArrayFilter):
```html
<label>
Order by:
<select ng-model="orderProp" ng-options="prop for prop in ['id', 'name']"></select>
</label>
<div ng-repeat="item in myObj | toArray:false | orderBy:orderProp">
[{{ item.id }}] {{ item.name }}
</div>
```
+14 -31
View File
@@ -165,7 +165,7 @@ encoded.
`$location` service has two configuration modes which control the format of the URL in the browser
address bar: **Hashbang mode** (the default) and the **HTML5 mode** which is based on using the
[HTML5 History API](https://html.spec.whatwg.org/multipage/browsers.html#the-history-interface). Applications use the same API in
HTML5 [History API](http://www.w3.org/TR/html5/introduction.html#history-0). Applications use the same API in
both modes and the `$location` service will work with appropriate URL segments and browser APIs to
facilitate the browser URL change and history management.
@@ -301,7 +301,7 @@ it('should show example', inject(
### Fallback for legacy browsers
For browsers that support the HTML5 history API, `$location` uses the HTML5 history API to write
path and search. If the history API is not supported by a browser, `$location` supplies a Hashbang
path and search. If the history API is not supported by a browser, `$location` supplies a Hasbang
URL. This frees you from having to worry about whether the browser viewing your app supports the
history API or not; the `$location` service makes this transparent to you.
@@ -330,8 +330,8 @@ reload to the original link.
### Relative links
Be sure to check all relative links, images, scripts etc. Angular requires you to specify the url
base in the head of your main html file (`<base href="/my-base/index.html">`) unless `html5Mode.requireBase`
is set to `false` in the html5Mode definition object passed to `$locationProvider.html5Mode()`. With
base in the head of your main html file (`<base href="/my-base">`) unless `html5Mode.requireBase` is
set to `false` in the html5Mode definition object passed to `$locationProvider.html5Mode()`. With
that, relative urls will always be resolved to this base url, even if the initial url of the
document was different.
@@ -339,7 +339,6 @@ There is one exception: Links that only contain a hash fragment (e.g. `<a href="
will only change `$location.hash()` and not modify the url otherwise. This is useful for scrolling
to anchors on the same page without needing to know on which page the user currently is.
### Server side
Using this mode requires URL rewriting on server side, basically you have to rewrite all your links
@@ -347,20 +346,6 @@ to entry point of your application (e.g. index.html). Requiring a `<base>` tag i
this case, as it allows Angular to differentiate between the part of the url that is the application
base and the path that should be handled by the application.
### Base href constraints
The `$location` service is not able to function properly if the current URL is outside the URL given
as the base href. This can have subtle confusing consequencies...
Consider a base href set as follows: `<base href="/base/">` (i.e. the application exists in the "folder"
called `/base`). The URL `/base` is actually outside the application (it refers to the `base` file found
in the root `/` folder).
If you wish to be able to navigate to the application via a URL such as `/base` then you should ensure that
you server is setup to redirect such requests to `/base/`.
See https://github.com/angular/angular.js/issues/14018 for more information.
### Sending links among different browsers
Because of rewriting capability in HTML5 mode, your users will be able to open regular url links in
@@ -371,15 +356,15 @@ legacy browsers and hashbang links in modern browser:
### Example
Here you can see two `$location` instances that show the difference between **Html5 mode** and **Html5 Fallback mode**.
Note that to simulate different levels of browser support, the `$location` instances are connected to
a fakeBrowser service, which you don't have to set up in actual projects.
Here you can see two `$location` instances, both in **Html5 mode**, but on different browsers, so
that you can see the differences. These `$location` services are connected to a fake browsers. Each
input represents the address bar of the browser.
Note that when you type hashbang url into the first browser (or vice versa) it doesn't rewrite /
Note that when you type hashbang url into first browser (or vice versa) it doesn't rewrite /
redirect to regular / hashbang url, as this conversion happens only during parsing the initial URL
= on page reload.
In these examples we use `<base href="/base/index.html" />`. The inputs represent the address bar of the browser.
In these examples we use `<base href="/base/index.html" />`
#### Browser in HTML5 mode
<example module="html5-mode" name="location-html5-mode">
@@ -404,7 +389,6 @@ In these examples we use `<base href="/base/index.html" />`. The inputs represen
<file name="app.js">
angular.module('html5-mode', ['fake-browser', 'address-bar'])
// Configure the fakeBrowser. Do not set these values in actual projects.
.constant('initUrl', 'http://www.example.com/base/path?a=b#h')
.constant('baseHref', '/base/index.html')
.value('$sniffer', { history: true })
@@ -554,7 +538,6 @@ In these examples we use `<base href="/base/index.html" />`. The inputs represen
<file name="app.js">
angular.module('hashbang-mode', ['fake-browser', 'address-bar'])
// Configure the fakeBrowser. Do not set these values in actual projects.
.constant('initUrl', 'http://www.example.com/base/index.html#!/path?a=b#h')
.constant('baseHref', '/base/index.html')
.value('$sniffer', { history: false })
@@ -710,7 +693,7 @@ A path should always begin with forward slash (`/`); the `$location.path()` sett
forward slash if it is missing.
Note that the `!` prefix in the hashbang mode is not part of `$location.path()`; it is actually
`hashPrefix`.
hashPrefix.
## Crawling your app
@@ -786,8 +769,8 @@ then uses the information it obtains to compose hashbang URLs (such as
</tr>
<tr class="head">
<th>Navigation outside the app</td>
<th>Use lower level API</td>
<td>Navigation outside the app</td>
<td>Use lower level API</td>
</tr>
<tr>
@@ -801,8 +784,8 @@ then uses the information it obtains to compose hashbang URLs (such as
</tr>
<tr class="head">
<th>Read access</td>
<th>Change to</td>
<td>Read access</td>
<td>Change to</td>
</tr>
<tr>
+73 -79
View File
@@ -23,9 +23,9 @@ angular.module('myApp', ['ngAria'])...
###Using ngAria
Most of what ngAria does is only visible "under the hood". To see the module in action, once you've
added it as a dependency, you can test a few things:
* Using your favorite element inspector, look for attributes added by ngAria in your own code.
* Using your favorite element inspector, look for ngAria attributes in your own code.
* Test using your keyboard to ensure `tabindex` is used correctly.
* Fire up a screen reader such as VoiceOver or NVDA to check for ARIA support.
* Fire up a screen reader such as VoiceOver to listen for ARIA support.
[Helpful screen reader tips.](http://webaim.org/articles/screenreader_testing/)
##Supported directives
@@ -33,9 +33,6 @@ Currently, ngAria interfaces with the following directives:
* {@link guide/accessibility#ngmodel ngModel}
* {@link guide/accessibility#ngdisabled ngDisabled}
* {@link guide/accessibility#ngrequired ngRequired}
* {@link guide/accessibility#ngvaluechecked ngChecked}
* {@link guide/accessibility#ngvaluechecked ngValue}
* {@link guide/accessibility#ngshow ngShow}
* {@link guide/accessibility#nghide ngHide}
* {@link guide/accessibility#ngclick ngClick}
@@ -44,9 +41,9 @@ Currently, ngAria interfaces with the following directives:
<h2 id="ngmodel">ngModel</h2>
Much of ngAria's heavy lifting happens in the {@link ng.ngModel ngModel}
directive. For elements using ngModel, special attention is paid by ngAria if that element also
has a role or type of `checkbox`, `radio`, `range` or `textbox`.
Most of ngAria's heavy lifting happens in the {@link ngModel ngModel}
directive. For elements using ngModel, special attention is paid by ngAria if that element also
has a a role or type of `checkbox`, `radio`, `range` or `textbox`.
For those elements using ngModel, ngAria will dynamically bind and update the following ARIA
attributes (if they have not been explicitly specified by the developer):
@@ -137,75 +134,37 @@ attributes (if they have not been explicitly specified by the developer):
ngAria will also add `tabIndex`, ensuring custom elements with these roles will be reachable from
the keyboard. It is still up to **you** as a developer to **ensure custom controls will be
accessible**. As a rule, any time you create a widget involving user interaction, be sure to test
it with your keyboard and at least one mobile and desktop screen reader.
<h2 id="ngvaluechecked">ngValue and ngChecked</h2>
To ease the transition between native inputs and custom controls, ngAria now supports
{@link ng.ngValue ngValue} and {@link ng.ngChecked ngChecked}.
The original directives were created for native inputs only, so ngAria extends
support to custom elements by managing `aria-checked` for accessibility.
###Example
```html
<custom-checkbox ng-checked="val"></custom-checkbox>
<custom-radio-button ng-value="val"></custom-radio-button>
```
Becomes:
```html
<custom-checkbox ng-checked="val" aria-checked="true"></custom-checkbox>
<custom-radio-button ng-value="val" aria-checked="true"></custom-radio-button>
```
operable** from the keybard. Think of `ng-click` on a `<div>` or `<md-checkbox>`: you still need
to bind `ng-keypress` to make it fully operable from the keyboard. As a rule, any time you create
a widget involving user interaction, be sure to test it with your keyboard and at least one mobile
and desktop screen reader (preferably more).
<h2 id="ngdisabled">ngDisabled</h2>
The `disabled` attribute is only valid for certain elements such as `button`, `input` and
`textarea`. To properly disable custom element directives such as `<md-checkbox>` or `<taco-tab>`,
using ngAria with {@link ng.ngDisabled ngDisabled} will also
using ngAria with [ngDisabled](https://docs.angularjs.org/api/ng/directive/ngDisabled) will also
add `aria-disabled`. This tells assistive technologies when a non-native input is disabled, helping
custom controls to be more accessible.
###Example
```html
<md-checkbox ng-disabled="disabled"></md-checkbox>
<md-checkbox ng-disabled="disabled">
```
Becomes:
```html
<md-checkbox disabled aria-disabled="true"></md-checkbox>
<md-checkbox disabled aria-disabled="true">
```
>You can check whether a control is legitimately disabled for a screen reader by visiting
[chrome://accessibility](chrome://accessibility) and inspecting [the accessibility tree](http://www.paciellogroup.com/blog/2015/01/the-browser-accessibility-tree/).
<h2 id="ngrequired">ngRequired</h2>
The boolean `required` attribute is only valid for native form controls such as `input` and
`textarea`. To properly indicate custom element directives such as `<md-checkbox>` or `<custom-input>`
as required, using ngAria with {@link ng.ngRequired ngRequired} will also add
`aria-required`. This tells accessibility APIs when a custom control is required.
###Example
```html
<md-checkbox ng-required="val"></md-checkbox>
```
Becomes:
```html
<md-checkbox ng-required="val" aria-required="true"></md-checkbox>
```
[chrome://accessibility](chrome://accessibility).
<h2 id="ngshow">ngShow</h2>
>The {@link ng.ngShow ngShow} directive shows or hides the
>The [ngShow](https://docs.angularjs.org/api/ng/directive/ngShow) directive shows or hides the
given HTML element based on the expression provided to the `ngShow` attribute. The element is
shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
@@ -242,7 +201,7 @@ Becomes:
<h2 id="nghide">ngHide</h2>
>The {@link ng.ngHide ngHide} directive shows or hides the
>The [ngHide](https://docs.angularjs.org/api/ng/directive/ngHide) directive shows or hides the
given HTML element based on the expression provided to the `ngHide` attribute. The element is
shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
@@ -251,25 +210,16 @@ The default CSS for `ngHide`, the inverse method to `ngShow`, makes ngAria redun
`display: none`. See explanation for {@link guide/accessibility#ngshow ngShow} when overriding the default CSS.
<h2><span id="ngclick">ngClick</span> and <span id="ngdblclick">ngDblclick</span></h2>
If `ng-click` or `ng-dblclick` is encountered, ngAria will add `tabindex="0"` to any element not in
a node blacklist:
If `ng-click` or `ng-dblclick` is encountered, ngAria will add `tabindex="0"` if it isn't there
already.
* Button
* Anchor
* Input
* Textarea
* Select
* Details/Summary
To fix widespread accessibility problems with `ng-click` on div elements, ngAria will dynamically
bind keypress by default as long as the element isn't an anchor, button, input or textarea.
You can turn this functionality on or off with the `bindKeypress` configuration option. ngAria
will also add the `button` role to communicate to users of assistive technologies.
To fix widespread accessibility problems with `ng-click` on `div` elements, ngAria will
dynamically bind a keypress event by default as long as the element isn't in the node blacklist.
You can turn this functionality on or off with the `bindKeypress` configuration option.
ngAria will also add the `button` role to communicate to users of assistive technologies. This can
be disabled with the `bindRoleForClick` configuration option.
For `ng-dblclick`, you must still manually add `ng-keypress` and a role to non-interactive elements
such as `div` or `taco-button` to enable keyboard access.
For `ng-dblclick`, you must still manually add `ng-keypress` and role to non-interactive elements such
as `div` or `taco-button` to enable keyboard access.
<h3>Example</h3>
```html
@@ -310,18 +260,62 @@ The attribute magic of ngAria may not work for every scenario. To disable indivi
you can use the {@link ngAria.$ariaProvider#config config} method. Just keep in mind this will
tell ngAria to ignore the attribute globally.
<example module="ngAria_ngClickExample" deps="angular-aria.js">
<example module="ngAria_ngDisabledExample" deps="angular-aria.js">
<file name="index.html">
<div ng-click="someFunction" show-attrs>
&lt;div&gt; with ng-click and bindRoleForClick, tabindex set to false
<style>
[role=checkbox] {
cursor: pointer;
display: inline-block;
}
[role=checkbox] .icon:before {
content: '\2610';
display: inline-block;
font-size: 2em;
line-height: 1;
vertical-align: middle;
speak: none;
}
[role=checkbox].active .icon:before {
content: '\2611';
}
</style>
<form ng-controller="formsController">
<div ng-model="someModel" show-attrs>
Div with ngModel and aria-invalid disabled
</div>
<div role="checkbox" ng-model="checked" ng-class="{active: checked}"
aria-label="Custom Checkbox" ng-click="toggleCheckbox()" some-checkbox show-attrs>
<span class="icon" aria-hidden="true"></span>
Custom Checkbox for comparison
</div>
</form>
<script>
angular.module('ngAria_ngClickExample', ['ngAria'], function config($ariaProvider) {
angular.module('ngAria_ngDisabledExample', ['ngAria'], function config($ariaProvider) {
$ariaProvider.config({
bindRoleForClick: false,
tabindex: false
ariaInvalid: false,
tabindex: true
});
})
.controller('formsController', function($scope){
$scope.checked = false;
$scope.toggleCheckbox = function(){
$scope.checked = !$scope.checked;
}
})
.directive('someCheckbox', function(){
return {
restrict: 'A',
link: function($scope, $el, $attrs) {
$el.on('keypress', function(event){
event.preventDefault();
if(event.keyCode === 32 || event.keyCode === 13){
$scope.toggleCheckbox();
$scope.$apply();
}
});
}
}
})
.directive('showAttrs', function() {
return function(scope, el, attrs) {
var pre = document.createElement('pre');
+3 -171
View File
@@ -6,7 +6,7 @@
# Animations
AngularJS provides animation hooks for common directives such as `ngRepeat`, `ngSwitch`, and `ngView`, as well as custom directives
AngularJS 1.3 provides animation hooks for common directives such as `ngRepeat`, `ngSwitch`, and `ngView`, as well as custom directives
via the `$animate` service. These animation hooks are set in place to trigger animations during the life cycle of various directives and when
triggered, will attempt to perform a CSS Transition, CSS Keyframe Animation or a JavaScript callback Animation (depending on if an animation is
placed on the given directive). Animations can be placed using vanilla CSS by following the naming conventions set in place by AngularJS
@@ -253,7 +253,7 @@ The table below explains in detail which animation events are triggered
| {@link ng.directive:ngClass#animations ngClass or &#123;&#123;class&#125;&#125;} | add and remove |
| {@link ng.directive:ngShow#animations ngShow & ngHide} | add and remove (the ng-hide class value) |
For a full breakdown of the steps involved during each animation event, refer to the {@link ng.$animate API docs}.
For a full breakdown of the steps involved during each animation event, refer to the {@link ngAnimate.$animate API docs}.
## How do I use animations in my own directives?
@@ -274,176 +274,8 @@ myModule.directive('my-directive', ['$animate', function($animate) {
}]);
```
## Animations on app bootstrap / page load
By default, animations are disabled when the Angular app {@link guide/bootstrap bootstraps}. If you are using the {@link ngApp} directive,
this happens in the `DOMContentLoaded` event, so immediately after the page has been loaded.
Animations are disabled, so that UI and content are instantly visible. Otherwise, with many animations on
the page, the loading process may become too visually overwhelming, and the performance may suffer.
Internally, `ngAnimate` waits until all template downloads that are started right after bootstrap have finished.
Then, it waits for the currently running {@link ng.$rootScope.Scope#$digest} and the one after that to finish.
This ensures that the whole app has been compiled fully before animations are attempted.
If you do want your animations to play when the app bootstraps, you can enable animations globally in
your main module's {@link angular.Module#run run} function:
```js
myModule.run(function($animate) {
$animate.enabled(true);
});
```
## How to (selectively) enable, disable and skip animations
There are three different ways to disable animations, both globally and for specific animations.
Disabling specific animations can help to speed up the render performance, for example for large `ngRepeat`
lists that don't actually have animations. Because ngAnimate checks at runtime if animations are present,
performance will take a hit even if an element has no animation.
### In the config: {@link $animateProvider#classNameFilter $animateProvider.classNameFilter()}
This function can be called in the {@link angular.Module#config config} phase of an app. It takes a regex as the only argument,
which will then be matched against the classes of any element that is about to be animated. The regex
allows a lot of flexibility - you can either allow animations only for specific classes (useful when
you are working with 3rd party animations), or exclude specific classes from getting animated.
```js
app.config(function($animateProvider) {
$animateProvider.classNameFilter(/animate-/);
});
```
```css
/&#42; prefixed with animate- &#42;/
.animate-fade-add.animate-fade-add-active {
transition:1s linear all;
opacity:0;
}
```
The classNameFilter approach generally applies the biggest speed boost, because the matching is
done before any other animation disabling strategies are checked. However, that also means it is not
possible to override class name matching with the two following strategies. It's of course still possible
to enable / disable animations by changing an element's class name at runtime.
### At runtime: {@link ng.$animate#enabled $animate.enabled()}
This function can be used to enable / disable animations in two different ways:
With a single `boolean` argument, it enables / disables animations globally: `$animate.enabled(false)`
disables all animations in your app.
When the second argument is a native DOM or jQuery element, the function enables / disables
animations on this element *and all its children*: `$animate.enabled(false, myElement)`. This is the
most flexible way to change the animation state. For example, even if you have used it to disable
animations on a parent element, you can still re-enable it for a child element. And compared to the
`classNameFilter`, you can change the animation status at runtime instead of during the config phase.
Note however that the `$animate.enabled()` state for individual elements does not overwrite disabling
rules that have been set in the {@link $animateProvider#classNameFilter classNameFilter}.
### Via CSS styles: overwriting styles in the `ng-animate` CSS class
Whenever an animation is started, ngAnimate applies the `ng-animate` class to the element for the
whole duration of the animation. By applying CSS transition / animation styling to the class,
you can skip an animation:
```css
.my-class{
transition: transform 2s;
}
.my-class:hover {
transform: translateX(50px);
}
my-class.ng-animate {
transition: 0s;
}
```
By setting `transition: 0s`, ngAnimate will ignore the existing transition styles, and not try to animate them (Javascript
animations will still execute, though). This can be used to prevent {@link guide/animations#preventing-collisions-with-existing-animations-and-third-party-libraries
issues with existing animations interfering with ngAnimate}.
## Preventing flicker before an animation starts
When nesting elements with structural animations such as `ngIf` into elements that have class-based
animations such as `ngClass`, it sometimes happens that before the actual animation starts, there is a brief flicker or flash of content
where the animated element is briefly visible.
To prevent this, you can apply styles to the `ng-[event]-prepare` class, which is added as soon as an animation is initialized,
but removed before the actual animation starts (after waiting for a $digest). This class is only added for *structural*
animations (`enter`, `move`, and `leave`).
Here's an example where you might see flickering:
```html
<div ng-class="{red: myProp}">
<div ng-class="{blue: myProp}">
<div class="message" ng-if="myProp"></div>
</div>
</div>
```
It is possible that during the `enter` event, the `.message` div will be briefly visible before it starts animating.
In that case, you can add styles to the CSS that make sure the element stays hidden before the animation starts:
```css
.message.ng-enter-prepare {
opacity: 0;
}
/* Other animation styles ... */
```
## Preventing Collisions with Existing Animations and Third Party Libraries
By default, any `ngAnimate` enabled directives will assume any transition / animation styles on the
element are part of an `ngAnimate` animation. This can lead to problems when the styles are actually
for animations that are independent of `ngAnimate`.
For example, an element acts as a loading spinner. It has an inifinite css animation on it, and also an
{@link ngIf `ngIf`} directive, for which no animations are defined:
```css
@keyframes rotating {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.spinner {
animation: rotating 2s linear infinite;
}
```
Now, when the `ngIf` changes, `ngAnimate` will see the spinner animation and use
it to animate the `enter`/`leave` event, which doesn't work because
the animation is infinite. The element will still be added / removed after a timeout, but there will be a
noticable delay.
This might also happen because some third-party frameworks place animation duration defaults
across many element or className selectors in order to make their code small and reuseable.
You can prevent this unwanted behavior by adding CSS to the `.ng-animate` class that is added
for the whole duration of an animation. Simply overwrite the transition / animation duration. In the
case of the spinner, this would be:
```css
.spinner.ng-animate {
transition: 0s none;
animation: 0s none;
}
```
If you do have CSS transitions / animations defined for the animation events, make sure they have higher priority
than any styles that are independent from ngAnimate.
You can also use one of the two other {@link guide/animations#how-to-selectively-enable-disable-and-skip-animations strategies to disable animations}.
## More about animations
For a full breakdown of each method available on `$animate`, see the {@link ng.$animate API documentation}.
For a full breakdown of each method available on `$animate`, see the {@link ngAnimate.$animate API documentation}.
To see a complete demo, see the {@link tutorial/step_12 animation step within the AngularJS phonecat tutorial}.
+3 -14
View File
@@ -53,13 +53,13 @@ initialization.
Angular initializes automatically upon `DOMContentLoaded` event or when the `angular.js` script is
evaluated if at that time `document.readyState` is set to `'complete'`. At this point Angular looks
for the {@link ng.directive:ngApp `ngApp`} directive which designates your application root.
If the {@link ng.directive:ngApp `ngApp`} directive is found then Angular will:
for the {@link ng.directive:ngApp `ng-app`} directive which designates your application root.
If the {@link ng.directive:ngApp `ng-app`} directive is found then Angular will:
* load the {@link guide/module module} associated with the directive.
* create the application {@link auto.$injector injector}
* compile the DOM treating the {@link ng.directive:ngApp
`ngApp`} directive as the root of the compilation. This allows you to tell it to treat only a
`ng-app`} directive as the root of the compilation. This allows you to tell it to treat only a
portion of the DOM as an Angular application.
@@ -142,17 +142,6 @@ 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.
## Things to keep in mind
There a few things to keep in mind regardless of automatic or manual bootstrapping:
- While it's possible to bootstrap more than one AngularJS application per page, we don't actively
test against this scenario. It's possible that you'll run into problems, especially with complex apps, so
caution is advised.
- Do not bootstrap your app on an element with a directive that uses {@link ng.$compile#transclusion transclusion}, such as
{@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}.
Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
causing animations to stop working and making the injector inaccessible from outside the app.
## Deferred Bootstrap
File diff suppressed because it is too large Load Diff
-471
View File
@@ -1,471 +0,0 @@
@ngdoc overview
@name Components
@sortOrder 305
@description
# Understanding Components
In Angular, a Component is a special kind of {@link guide/directive directive} that uses a simpler
configuration which is suitable for a component-based application structure.
This makes it easier to write an app in a way that's similar to using Web Components or using Angular
2's style of application architecture.
Advantages of Components:
- simpler configuration than plain directives
- promote sane defaults and best practices
- optimized for component-based architecture
- writing component directives will make it easier to upgrade to Angular 2
When not to use Components:
- for directives that rely on DOM manipulation, adding event listeners etc, because the compile
and link functions are unavailable
- when you need advanced directive definition options like priority, terminal, multi-element
- when you want a directive that is triggered by an attribute or CSS class, rather than an element
## Creating and configuring a Component
Components can be registered using the `.component()` method of an Angular module (returned by {@link module `angular.module()`}). The method takes two arguments:
* The name of the Component (as string).
* The Component config object (note that, unlike the `.directive()` method, this method does **not** take a factory function.
<example name="heroComponentSimple" module="heroApp">
<file name="index.js">
angular.module('heroApp', []).controller('mainCtrl', function() {
this.hero = {
name: 'Spawn'
};
});
</file>
<file name="heroDetail.js">
function HeroDetailController() {
}
angular.module('heroApp').component('heroDetail', {
templateUrl: 'heroDetail.html',
controller: HeroDetailController,
bindings: {
hero: '='
}
});
</file>
<file name="index.html">
<!-- components match only elements -->
<div ng-controller="mainCtrl as ctrl">
<b>Hero</b><br>
<hero-detail hero="ctrl.hero"></hero-detail>
</div>
</file>
<file name="heroDetail.html">
<span>Name: {{$ctrl.hero.name}}</span>
</file>
</example>
It's also possible to add components via {@link $compileProvider#component} in a module's config phase.
### Comparison between Directive definition and Component definition
| | Directive | Component |
|-------------------|----------------------|-----------------|
| bindings | No | Yes (binds to controller) |
| bindToController | Yes (default: false) | No (use bindings instead) |
| compile function | Yes | No |
| controller | Yes | Yes (default `function() {}`) |
| controllerAs | Yes (default: false) | Yes (default: `$ctrl`) |
| link functions | Yes | No |
| multiElement | Yes | No |
| priority | Yes | No |
| require | Yes | Yes |
| restrict | Yes | No (restricted to elements only) |
| scope | Yes (default: false) | No (scope is always isolate) |
| template | Yes | Yes, injectable |
| templateNamespace | Yes | No |
| templateUrl | Yes | Yes, injectable |
| terminal | Yes | No |
| transclude | Yes (default: false) | Yes (default: false) |
## Component-based application architecture
As already mentioned, the component helper makes it easier to structure your application with
a component-based architecture. But what makes a component beyond the options that
the component helper has?
- **Components only control their own View and Data:**
Components should never modify any data or DOM that is out of their own scope. Normally, in Angular
it is possible to modify data anywhere in the application through scope inheritance and watches. This
is practical, but can also lead to problems when it is not clear which part of the application is
responsible for modifying the data. That is why component directives use an isolate scope, so a whole
class of scope manipulation is not possible.
- **Components have a well-defined public API - Inputs and Outputs:**
However, scope isolation only goes so far, because Angular uses two-way binding. So if you pass
an object to a component like this - `bindings: {item: '='}`, and modify one of its properties, the
change will be reflected in the parent component. For components however, only the component that owns
the data should modify it, to make it easy to reason about what data is changed, and when. For that reason,
components should follow a few simple conventions:
- Inputs should be using `<` and `@` bindings. The `<` symbol denotes {@link $compile#-scope- one-way bindings} which are
available since 1.5. The difference to `=` is that the bound properties in the component scope are not watched, which means
if you assign a new value to the property in the component scope, it will not update the parent scope. Note however, that both parent
and component scope reference the same object, so if you are changing object properties or array elements in the
component, the parent will still reflect that change.
The general rule should therefore be to never change an object or array property in the component scope.
`@` bindings can be used when the input is a string, especially when the value of the binding doesn't change.
```js
bindings: {
hero: '<',
comment: '@'
}
```
- Outputs are realized with `&` bindings, which function as callbacks to component events.
```js
bindings: {
onDelete: '&',
onUpdate: '&'
}
```
- Instead of manipulating Input Data, the component calls the correct Output Event with the changed data.
For a deletion, that means the component doesn't delete the `hero` itself, but sends it back to
the owner component via the correct event.
```html
<button ng-click="$ctrl.onDelete({hero: $ctrl.hero})">Delete</button>
```
- That way, the parent component can decide what to do with the event (e.g. delete an item or update the properties)
```js
ctrl.deleteHero(hero) {
$http.delete(...).then(function() {
var idx = ctrl.list.indexOf(hero);
if (idx >= 0) {
ctrl.list.splice(idx, 1);
}
});
}
```
- **An application is a tree of components:**
Ideally, the whole application should be a tree of components that implement clearly defined inputs
and outputs, and minimize two-way data binding. That way, it's easier to predict when data changes and what the state
of a component is.
## Example of a component tree
The following example expands on the simple component example and incorporates the concepts we introduced
above:
Instead of an ngController, we now have a heroList component that holds the data of
different heroes, and creates a heroDetail for each of them.
The heroDetail component now contains new functionality:
- a delete button that calls the bound `onDelete` function of the heroList component
- an input to change the hero location, in the form of a reusable editableField component. Instead
of manipulating the hero object itself, it sends a changeset upwards to the heroDetail, which sends
it upwards to the heroList component, which updates the original data.
<example name="heroComponentTree" module="heroApp">
<file name="index.js">
var mode = angular.module('heroApp', []);
</file>
<file name="heroList.js">
function HeroListController($scope, $element, $attrs) {
var ctrl = this;
// This would be loaded by $http etc.
ctrl.list = [
{
name: 'Superman',
location: ''
},
{
name: 'Batman',
location: 'Wayne Manor'
}
];
ctrl.updateHero = function(hero, prop, value) {
hero[prop] = value;
};
ctrl.deleteHero = function(hero) {
var idx = ctrl.list.indexOf(hero);
if (idx >= 0) {
ctrl.list.splice(idx, 1);
}
};
}
angular.module('heroApp').component('heroList', {
templateUrl: 'heroList.html',
controller: HeroListController
});
</file>
<file name="heroDetail.js">
function HeroDetailController($scope, $element, $attrs) {
var ctrl = this;
ctrl.update = function(prop, value) {
ctrl.onUpdate({hero: ctrl.hero, prop: prop, value: value});
};
}
angular.module('heroApp').component('heroDetail', {
templateUrl: 'heroDetail.html',
controller: HeroDetailController,
bindings: {
hero: '<',
onDelete: '&',
onUpdate: '&'
}
});
</file>
<file name="editableField.js">
function EditableFieldController($scope, $element, $attrs) {
var ctrl = this;
ctrl.editMode = false;
ctrl.handleModeChange = function() {
if (ctrl.editMode) {
ctrl.onUpdate({value: ctrl.fieldValue});
ctrl.fieldValueCopy = ctrl.fieldValue;
}
ctrl.editMode = !ctrl.editMode;
};
ctrl.reset = function() {
ctrl.fieldValue = ctrl.fieldValueCopy;
};
ctrl.$onInit = function() {
// Make a copy of the initial value to be able to reset it later
ctrl.fieldValueCopy = ctrl.fieldValue;
// Set a default fieldType
if (!ctrl.fieldType) {
ctrl.fieldType = 'text';
}
};
}
angular.module('heroApp').component('editableField', {
templateUrl: 'editableField.html',
controller: EditableFieldController,
bindings: {
fieldValue: '<',
fieldType: '@?',
onUpdate: '&'
}
});
</file>
<file name="index.html">
<hero-list></hero-list>
</file>
<file name="heroList.html">
<b>Heroes</b><br>
<hero-detail ng-repeat="hero in $ctrl.list" hero="hero" on-delete="$ctrl.deleteHero(hero)" on-update="$ctrl.updateHero(hero, prop, value)"></hero-detail>
</file>
<file name="heroDetail.html">
<hr>
<div>
Name: {{$ctrl.hero.name}}<br>
Location: <editable-field field-value="$ctrl.hero.location" field-type="text" on-update="$ctrl.update('location', value)"></editable-field><br>
<button ng-click="$ctrl.onDelete({hero: $ctrl.hero})">Delete</button>
</div>
</file>
<file name="editableField.html">
<span ng-switch="$ctrl.editMode">
<input ng-switch-when="true" type="{{$ctrl.fieldType}}" ng-model="$ctrl.fieldValue">
<span ng-switch-default>{{$ctrl.fieldValue}}</span>
</span>
<button ng-click="$ctrl.handleModeChange()">{{$ctrl.editMode ? 'Save' : 'Edit'}}</button>
<button ng-if="$ctrl.editMode" ng-click="$ctrl.reset()">Reset</button>
</file>
</example>
## Components as route templates
Components are also useful as route templates (e.g. when using {@link ngRoute ngRoute}). In a component-based
application, every view is a component:
```js
var myMod = angular.module('myMod', ['ngRoute']);
myMod.component('home', {
template: '<h1>Home</h1><p>Hello, {{ $ctrl.user.name }} !</p>',
controller: function() {
this.user = {name: 'world'};
}
});
myMod.config(function($routeProvider) {
$routeProvider.when('/', {
template: '<home></home>'
});
});
```
<br />
When using {@link ngRoute.$routeProvider $routeProvider}, you can often avoid some
boilerplate, by passing the resolved route dependencies directly to the component. Since 1.5,
ngRoute automatically assigns the resolves to the route scope property `$resolve` (you can also
configure the property name via `resolveAs`). When using components, you can take advantage of this and pass resolves
directly into your component without creating an extra route controller:
```js
var myMod = angular.module('myMod', ['ngRoute']);
myMod.component('home', {
template: '<h1>Home</h1><p>Hello, {{ $ctrl.user.name }} !</p>',
bindings: {
user: '<'
}
});
myMod.config(function($routeProvider) {
$routeProvider.when('/', {
template: '<home user="$resolve.user"></home>',
resolve: {
user: function($http) { return $http.get('...'); }
}
});
});
```
## Intercomponent Communication
Directives can require the controllers of other directives to enable communication
between each other. This can be achieved in a component by providing an
object mapping for the `require` property. The object keys specify the property names under which
the required controllers (object values) will be bound to the requiring component's controller.
<div class="alert alert-warning">
Note that the required controllers will not be available during the instantiation of the controller,
but they are guaranteed to be available just before the `$onInit` method is executed!
</div>
Here is a tab pane example built from components:
<example module="docsTabsExample">
<file name="script.js">
angular.module('docsTabsExample', [])
.component('myTabs', {
transclude: true,
controller: function() {
var panes = this.panes = [];
this.select = function(pane) {
angular.forEach(panes, function(pane) {
pane.selected = false;
});
pane.selected = true;
};
this.addPane = function(pane) {
if (panes.length === 0) {
this.select(pane);
}
panes.push(pane);
};
},
templateUrl: 'my-tabs.html'
})
.component('myPane', {
transclude: true,
require: {
tabsCtrl: '^myTabs'
},
bindings: {
title: '@'
},
controller: function() {
this.$onInit = function() {
this.tabsCtrl.addPane(this);
console.log(this);
};
},
templateUrl: 'my-pane.html'
});
</file>
<file name="index.html">
<my-tabs>
<my-pane title="Hello">
<h4>Hello</h4>
<p>Lorem ipsum dolor sit amet</p>
</my-pane>
<my-pane title="World">
<h4>World</h4>
<em>Mauris elementum elementum enim at suscipit.</em>
<p><a href ng-click="i = i + 1">counter: {{i || 0}}</a></p>
</my-pane>
</my-tabs>
</file>
<file name="my-tabs.html">
<div class="tabbable">
<ul class="nav nav-tabs">
<li ng-repeat="pane in $ctrl.panes" ng-class="{active:pane.selected}">
<a href="" ng-click="$ctrl.select(pane)">{{pane.title}}</a>
</li>
</ul>
<div class="tab-content" ng-transclude></div>
</div>
</file>
<file name="my-pane.html">
<div class="tab-pane" ng-show="$ctrl.selected" ng-transclude></div>
</file>
</example>
# Unit-testing Component Controllers
The easiest way to unit-test a component controller is by using the {@link ngMock.$componentController $componentController}
that is included in {@link ngMock}. The advantage of this method is that you do not have
to create any DOM elements. The following example shows how to do this for the `heroDetail` component
from above.
The examples use the [Jasmine](http://jasmine.github.io/) testing framework.
**Controller Test:**
```js
describe('component: heroDetail', function() {
var component, scope, hero, $componentController;
beforeEach(module('simpleComponent'));
beforeEach(inject(function($rootScope, _$componentController_) {
scope = $rootScope.$new();
$componentController = _$componentController_;
hero = {name: 'Wolverine'};
}));
it('should set the default values of the hero', function() {
// It's necessary to always pass the scope in the locals, so that the controller instance can be bound to it
component = $componentController('heroDetail', {$scope: scope});
expect(component.hero).toEqual({
name: undefined,
location: 'unknown'
});
});
it('should assign the name bindings to the hero object', function() {
// Here we are passing actual bindings to the component
component = $componentController('heroDetail',
{$scope: scope},
{hero: hero}
);
expect(component.hero.name).toBe('Wolverine');
});
it('should call the onDelete binding when a hero is deleted', function() {
component = $componentController('heroDetail',
{$scope: scope},
{hero: hero, onDelete: jasmine.createSpy('deleteSpy')}
);
component.onDelete({hero: component.hero});
expect(spy('deleteSpy')).toHaveBeenCalledWith(component.hero);
});
});
```
+34 -33
View File
@@ -8,22 +8,22 @@
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 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 |
|{@link concepts#filter Filter} | formats the value of an expression for display to the user |
|{@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 and functions |
|{@link concepts#injector Injector} | dependency injection container |
|{@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 |
| 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 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 |
|{@link concepts#filter Filter} | formats the value of an expression for display to the user |
|{@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 and functions |
|{@link concepts#injector Injector} | dependency injection container |
|{@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
@@ -55,11 +55,11 @@ Try out the Live Preview above, and then let's walk through the example and desc
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-databinding1.png">
This looks like normal HTML, with some new markup. In Angular, a file like this is called a
<a name="template">{@link templates template}</a>. When Angular starts your application, it parses and
processes this new markup from the template using the <a name="compiler">{@link compiler compiler}</a>.
The loaded, transformed and rendered DOM is then called the <a name="view"></a>*view*.
<a name="template">"{@link templates template}"</a>. When Angular starts your application, it parses and
processes this new markup from the template using the so-called <a name="compiler">"{@link compiler compiler}"</a>.
The loaded, transformed and rendered DOM is then called the <a name="view">"view"</a>.
The first kind of new markup are the <a name="directive">{@link directive directives}</a>.
The first kind of new markup are the so-called <a name="directive">"{@link directive directives}"</a>.
They apply special behavior to attributes or elements in the HTML. In the example above we use the
{@link ng.directive:ngApp `ng-app`} attribute, which is linked to a directive that automatically
initializes our application. Angular also defines a directive for the {@link ng.directive:input `input`}
@@ -75,16 +75,16 @@ stores/updates the value of the input field into/from a variable.
The second kind of new markup are the double curly braces `{{ expression | filter }}`:
When the compiler encounters this markup, it will replace it with the evaluated value of the markup.
An <a name="expression">{@link expression expression}</a> in a template is a JavaScript-like code snippet that allows
Angular to read and write variables. Note that those variables are not global variables.
An <a name="expression">"{@link expression expression}"</a> in a template is a JavaScript-like code snippet that allows
to read and write variables. Note that those variables are not global variables.
Just like variables in a JavaScript function live in a scope,
Angular provides a <a name="scope">{@link scope scope}</a> for the variables accessible to expressions.
The values that are stored in variables on the scope are referred to as the <a name="model"></a>*model*
Angular provides a <a name="scope">"{@link scope scope}"</a> for the variables accessible to expressions.
The values that are stored in variables on the scope are referred to as the <a name="model">"model"</a>
in the rest of the documentation.
Applied to the example above, the markup directs Angular to "take the data we got from the input widgets
and multiply them together".
The example above also contains a <a name="filter">{@link guide/filter filter}</a>.
The example above also contains a <a name="filter">"{@link guide/filter filter}"</a>.
A filter formats the value of an expression for display to the user.
In the example above, the filter {@link ng.filter:currency `currency`} formats a number
into an output that looks like money.
@@ -92,7 +92,7 @@ into an output that looks like money.
The important thing in the example is that Angular provides _live_ bindings:
Whenever the input values change, the value of the expressions are automatically
recalculated and the DOM is updated with their values.
The concept behind this is <a name="databinding">{@link databinding two-way data binding}</a>.
The concept behind this is <a name="databinding">"{@link databinding two-way data binding}"</a>.
## Adding UI logic: Controllers
@@ -150,7 +150,7 @@ different currencies and also pay the invoice.
What changed?
First, there is a new JavaScript file that contains a <a name="controller">{@link controller controller}</a>.
First, there is a new JavaScript file that contains a so-called <a name="controller">"{@link controller controller}"</a>.
More exactly, the file contains a constructor function that creates the actual controller instance.
The purpose of controllers is to expose variables and functionality to expressions and directives.
@@ -255,15 +255,15 @@ We moved the `convertCurrency` function and the definition of the existing curre
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.
This is where <a name="di">"{@link di Dependency Injection}"</a> comes into play.
Dependency Injection (DI) is a software design pattern that
deals with how objects and functions get created and how they get a hold of their dependencies.
Everything within Angular (directives, filters, controllers,
services, ...) is created and wired using dependency injection. Within Angular,
the DI container is called the <a name="injector">{@link di injector}</a>.
the DI container is called the <a name="injector">"{@link di injector}"</a>.
To use DI, there needs to be a place where all the things that should work together are registered.
In Angular, this is the purpose of the <a name="module">{@link module modules}</a>.
In Angular, this is the purpose of the so-called <a name="module">"{@link module modules}"</a>.
When Angular starts, it will use the configuration of the module with the name defined by the `ng-app` directive,
including the configuration of all modules that this module depends on.
@@ -334,9 +334,9 @@ The following example shows how this is done with Angular:
var refresh = function() {
var url = YAHOO_FINANCE_URL_PATTERN.
replace('PAIRS', 'USD' + currencies.join('","USD'));
return $http.jsonp(url).then(function(response) {
return $http.jsonp(url).success(function(data) {
var newUsdToForeignRates = {};
angular.forEach(response.data.query.results.rate, function(rate) {
angular.forEach(data.query.results.rate, function(rate) {
var currency = rate.id.substring(3,6);
newUsdToForeignRates[currency] = window.parseFloat(rate.Rate);
});
@@ -348,7 +348,8 @@ The following example shows how this is done with Angular:
return {
currencies: currencies,
convert: convert
convert: convert,
refresh: refresh
};
}]);
</file>
+7 -7
View File
@@ -33,12 +33,12 @@ Do not use controllers to:
services} instead.
- Manage the life-cycle of other components (for example, to create service instances).
## Setting up the initial state of a `$scope` object
# Setting up the initial state of a `$scope` object
Typically, when you create an application you need to set up the initial state for the Angular
`$scope`. You set up the initial state of a scope by attaching properties to the `$scope` object.
The properties contain the **view model** (the model that will be presented by the view). All the
`$scope` properties will be available to the {@link templates template} at the point in the DOM where the Controller
`$scope` properties will be available to the template at the point in the DOM where the Controller
is registered.
The following example demonstrates creating a `GreetingController`, which attaches a `greeting`
@@ -72,13 +72,13 @@ now be data-bound to the template:
```
## Adding Behavior to a Scope Object
# Adding Behavior to a Scope Object
In order to react to events or execute computation in the view we must provide behavior to the
scope. We add behavior to the scope by attaching methods to the `$scope` object. These methods are
then available to be called from the template/view.
The following example uses a Controller to add a method, which doubles a number, to the scope:
The following example uses a Controller to add a method to the scope, which doubles a number:
```js
var myApp = angular.module('myApp',[]);
@@ -102,7 +102,7 @@ objects (or primitives) assigned to the scope become model properties. Any metho
the scope are available in the template/view, and can be invoked via angular expressions
and `ng` event handler directives (e.g. {@link ng.directive:ngClick ngClick}).
## Using Controllers Correctly
# Using Controllers Correctly
In general, a Controller shouldn't try to do too much. It should contain only the business logic
needed for a single view.
@@ -128,7 +128,7 @@ following components:
- A model consisting of a string named `spice`
- A Controller with two functions that set the value of `spice`
The message in our template contains a binding to the `spice` model which, by default, is set to the
The message in our template contains a binding to the `spice` model, which by default is set to the
string "very". Depending on which button is clicked, the `spice` model is set to `chili` or
`jalapeño`, and the message is automatically updated by data-binding.
@@ -262,7 +262,7 @@ Inheritance works with methods in the same way as it does with properties. So in
examples, all of the properties could be replaced with methods that return string values.
# Testing Controllers
## Testing Controllers
Although there are many ways to test a Controller, one of the best conventions, shown below,
involves injecting the {@link ng.$rootScope $rootScope} and {@link ng.$controller $controller}:
-2
View File
@@ -3,8 +3,6 @@
@sortOrder 210
@description
# Data Binding
Data-binding in Angular apps is the automatic synchronization of data between the model and view
components. The way that Angular implements data-binding lets you treat the model as the
single-source-of-truth in your application. The view is a projection of the model at all times.
+92 -60
View File
@@ -19,9 +19,8 @@ how to implement them.
## What are Directives?
At a high level, directives are markers on a DOM element (such as an attribute, element
name, comment or CSS class) that tell AngularJS's **HTML compiler** ({@link ng.$compile `$compile`})
to attach a specified behavior to that DOM element (e.g. via event listeners), or even to transform
the DOM element and its children.
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 `ngClass`.
Much like you create controllers and services, you can create your own directives for Angular to use.
@@ -31,7 +30,7 @@ When Angular {@link guide/bootstrap bootstraps} your application, the
<div class="alert alert-info">
**What does it mean to "compile" an HTML template?**
For AngularJS, "compilation" means attaching directives to the HTML to make it interactive.
For AngularJS, "compilation" means attaching event listeners to the HTML to make it interactive.
The reason we use the term "compile" is that the recursive process of attaching directives
mirrors the process of compiling source code in
[compiled programming languages](http://en.wikipedia.org/wiki/Compiled_languages).
@@ -43,27 +42,18 @@ mirrors the process of compiling source code in
Before we can write a directive, we need to know how Angular's {@link guide/compiler HTML compiler}
determines when to use a given directive.
Similar to the terminology used when an [element **matches** a selector](https://developer.mozilla.org/en-US/docs/Web/API/Element.matches), we say an element **matches** a
directive when the directive is part of its declaration.
In the following example, we say that the `<input>` element **matches** the `ngModel` directive
In the following example, we say that the `<input>` element **matches** the `ngModel` directive.
```html
<input ng-model="foo">
```
The following `<input>` element also **matches** `ngModel`:
The following also **matches** `ngModel`:
```html
<input data-ng-model="foo">
```
And the following <person> element **matches** the `person` directive:
```html
<person>{{name}}</person>
```
### Normalization
Angular **normalizes** an element's tag and attribute name to determine which elements match which
@@ -99,13 +89,8 @@ For example, the following forms are all equivalent and match the {@link ngBind}
</file>
<file name="protractor.js" type="protractor">
it('should show off bindings', function() {
var containerElm = element(by.css('div[ng-controller="Controller"]'));
var nameBindings = containerElm.all(by.binding('name'));
expect(nameBindings.count()).toBe(5);
nameBindings.each(function(elem) {
expect(elem.getText()).toEqual('Max Karl Ernst Ludwig Planck (April 23, 1858 October 4, 1947)');
});
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>
</example>
@@ -146,6 +131,63 @@ directives when possible.
</div>
### Text and attribute bindings
During the compilation process the {@link ng.$compile compiler} matches text and attributes
using the {@link ng.$interpolate $interpolate} service to see if they contain embedded
expressions. These expressions are registered as {@link ng.$rootScope.Scope#$watch watches}
and will update as part of normal {@link ng.$rootScope.Scope#$digest digest} cycle. An
example of interpolation is shown below:
```html
<a ng-href="img/{{username}}.jpg">Hello {{username}}!</a>
```
### `ngAttr` attribute bindings
Web browsers are sometimes picky about what values they consider valid for attributes.
For example, considering this template:
```html
<svg>
<circle cx="{{cx}}"></circle>
</svg>
```
We would expect Angular to be able to bind to this, but when we check the console we see
something like `Error: Invalid value for attribute cx="{{cx}}"`. Because of the SVG DOM API's
restrictions, you cannot simply write `cx="{{cx}}"`.
With `ng-attr-cx` you can work around this problem.
If an attribute with a binding is prefixed with the `ngAttr` prefix (denormalized as `ng-attr-`)
then during the binding it will be applied to the corresponding unprefixed attribute. This allows
you to bind to attributes that would otherwise be eagerly processed by browsers
(e.g. an SVG element's `circle[cx]` attributes). When using `ngAttr`, the `allOrNothing` flag of
{@link ng.$interpolate $interpolate} is used, so if any expression in the interpolated string
results in `undefined`, the attribute is removed and not added to the element.
For example, we could fix the example above by instead writing:
```html
<svg>
<circle ng-attr-cx="{{cx}}"></circle>
</svg>
```
If one wants to modify a camelcased attribute (SVG elements have valid camelcased attributes), such as `viewBox` on the `svg` element, one can use underscores to denote that the attribute to bind to is naturally camelcased.
For example, to bind to `viewBox`, we can write:
```html
<svg ng-attr-view_box="{{viewBox}}">
</svg>
```
## Creating Directives
First let's talk about the {@link ng.$compileProvider#directive API for registering directives}. Much like
@@ -303,7 +345,6 @@ The `restrict` option is typically set to:
* `'A'` - only matches attribute name
* `'E'` - only matches element name
* `'C'` - only matches class name
* `'M'` - only matches comment
These restrictions can all be combined as needed:
@@ -407,7 +448,7 @@ This is clearly not a great solution.
What we want to be able to do is separate the scope inside a directive from the scope
outside, and then map the outer scope to a directive's inner scope. We can do this by creating what
we call an **isolate scope**. To do this, we can use a {@link $compile#-scope- directive's `scope`} option:
we call an **isolate scope**. To do this, we can use a directive's `scope` option:
<example module="docsIsolateScopeDirective">
<file name="script.js">
@@ -536,24 +577,14 @@ want to reuse throughout your app.
In this example we will build a directive that displays the current time.
Once a second, it updates the DOM to reflect the current time.
Directives that want to modify the DOM typically use the `link` option to register DOM listeners
as well as update the DOM. It is executed after the template has been cloned and is where
directive logic will be put.
`link` takes a function with the following signature,
`function link(scope, element, attrs, controller, transcludeFn) { ... }`, where:
Directives that want to modify the DOM typically use the `link` option.
`link` takes a function with the following signature, `function link(scope, element, attrs) { ... }`
where:
* `scope` is an Angular scope object.
* `element` is the jqLite-wrapped element that this directive matches.
* `attrs` is a hash object with key-value pairs of normalized attribute names and their
corresponding attribute values.
* `controller` is the directive's required controller instance(s) or its own controller (if any).
The exact value depends on the directive's require property.
* `transcludeFn` is a transclude linking function pre-bound to the correct transclusion scope.
<div class="alert alert-info">
For more details on the `link` option refer to the {@link ng.$compile#-link- `$compile` API} page.
</div>
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
@@ -647,7 +678,6 @@ To do this, we need to use the `transclude` option.
return {
restrict: 'E',
transclude: true,
scope: {},
templateUrl: 'my-dialog.html'
};
});
@@ -658,7 +688,8 @@ To do this, we need to use the `transclude` option.
</div>
</file>
<file name="my-dialog.html">
<div class="alert" ng-transclude></div>
<div class="alert" ng-transclude>
</div>
</file>
</example>
@@ -680,7 +711,7 @@ that redefines `name` as `Jeff`. What do you think the `{{name}}` binding will r
transclude: true,
scope: {},
templateUrl: 'my-dialog.html',
link: function (scope) {
link: function (scope, element) {
scope.name = 'Jeff';
}
};
@@ -692,7 +723,8 @@ that redefines `name` as `Jeff`. What do you think the `{{name}}` binding will r
</div>
</file>
<file name="my-dialog.html">
<div class="alert" ng-transclude></div>
<div class="alert" ng-transclude>
</div>
</file>
</example>
@@ -703,7 +735,7 @@ The `transclude` option changes the way scopes are nested. It makes it so that t
transcluded directive have whatever scope is outside the directive, rather than whatever scope is on
the inside. In doing so, it gives the contents access to the outside scope.
Note that if the directive did not create its own scope, then `scope` in `scope.name = 'Jeff'` would
Note that if the directive did not create its own scope, then `scope` in `scope.name = 'Jeff';` would
reference the outside scope and we would see `Jeff` in the output.
This behavior makes sense for a directive that wraps some content, because otherwise you'd have to
@@ -776,9 +808,9 @@ function.
Often it's desirable to pass data from the isolate scope via an expression to the
parent scope, this can be done by passing a map of local variable names and values into the expression
wrapper function. For example, the `hideDialog` function takes a message to display when the dialog
is hidden. This is specified in the directive by calling `close({message: 'closing for now'})`.
Then the local variable `message` will be available within the `on-close` expression.
wrapper fn. For example, the hideDialog function takes a message to display when the dialog is hidden.
This is specified in the directive by calling `close({message: 'closing for now'})`. Then the local
variable `message` will be available within the `on-close` expression.
<div class="alert alert-success">
**Best Practice:** use `&attr` in the `scope` option when you want your directive
@@ -837,7 +869,7 @@ element?
}]);
</file>
<file name="index.html">
<span my-draggable>Drag Me</span>
<span my-draggable>Drag ME</span>
</file>
</example>
@@ -860,7 +892,7 @@ to which tab is active.
restrict: 'E',
transclude: true,
scope: {},
controller: ['$scope', function($scope) {
controller: function($scope) {
var panes = $scope.panes = [];
$scope.select = function(pane) {
@@ -876,13 +908,13 @@ to which tab is active.
}
panes.push(pane);
};
}],
},
templateUrl: 'my-tabs.html'
};
})
.directive('myPane', function() {
return {
require: '^^myTabs',
require: '^myTabs',
restrict: 'E',
transclude: true,
scope: {
@@ -898,9 +930,11 @@ to which tab is active.
<file name="index.html">
<my-tabs>
<my-pane title="Hello">
<h4>Hello</h4>
<p>Lorem ipsum dolor sit amet</p>
</my-pane>
<my-pane title="World">
<h4>World</h4>
<em>Mauris elementum elementum enim at suscipit.</em>
<p><a href ng-click="i = i + 1">counter: {{i || 0}}</a></p>
</my-pane>
@@ -917,25 +951,22 @@ to which tab is active.
</div>
</file>
<file name="my-pane.html">
<div class="tab-pane" ng-show="selected">
<h4>{{title}}</h4>
<div ng-transclude></div>
<div class="tab-pane" ng-show="selected" ng-transclude>
</div>
</file>
</example>
The `myPane` directive has a `require` option with value `^^myTabs`. When a directive uses this
option, `$compile` will throw an error unless the specified controller is found. The `^^` prefix
means that this directive searches for the controller on its parents. (A `^` prefix would make the
directive look for the controller on its own element or its parents; without any prefix, the
directive would look on its own element only.)
The `myPane` directive has a `require` option with value `^myTabs`. When a directive uses this
option, `$compile` will throw an error unless the specified controller is found. The `^` prefix
means that this directive searches for the controller on its parents (without the `^` prefix, the
directive would look for the controller on just its own element).
So where does this `myTabs` controller come from? Directives can specify controllers using
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 from the
template, you can use the option `controllerAs` to specify the name of the controller as an alias.
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.
@@ -950,7 +981,7 @@ The corresponding parameter being sent to the `link` function will also be an ar
angular.module('docsTabsExample', [])
.directive('myPane', function() {
return {
require: ['^^myTabs', 'ngModel'],
require: ['^myTabs', '^ngModel'],
restrict: 'E',
transclude: true,
scope: {
@@ -986,3 +1017,4 @@ available in the {@link guide/compiler compiler guide}.
The {@link ng.$compile `$compile` API} page has a comprehensive list of directive options for
reference.
+11 -20
View File
@@ -5,9 +5,8 @@
# Angular Expressions
Angular expressions are JavaScript-like code snippets that are mainly placed in
interpolation bindings such as `<span title="{{ attrBinding }}">{{ textBinding }}</span>`,
but also used directly in directive attributes such as `ng-click="functionExpression()"`.
Angular expressions are JavaScript-like code snippets that are usually placed in bindings such as
`{{ expression }}`.
For example, these are valid expressions in Angular:
@@ -36,9 +35,7 @@ Angular expressions are like JavaScript expressions with the following differenc
* **No RegExp Creation With Literal Notation:** You cannot create regular expressions
in an Angular expression.
* **No Object Creation With New Operator:** You cannot use `new` operator in an Angular expression.
* **No Comma And Void Operators:** You cannot use `,` or `void` operators in an Angular expression.
* **No Comma And Void Operators:** You cannot use `,` or `void` in an Angular expression.
* **Filters:** You can use {@link guide/filter filters} within expressions to format data before
displaying it.
@@ -116,9 +113,6 @@ This restriction is intentional. It prevents accidental access to the global sta
Instead use services like `$window` and `$location` in functions called from expressions. Such services
provide mockable access to globals.
It is possible to access the context object using the identifier `this` and the locals object using the
identifier `$locals`.
<example module="expressionExample">
<file name="index.html">
<div class="example2" ng-controller="ExampleController">
@@ -147,9 +141,6 @@ identifier `$locals`.
}
element(by.css('[ng-click="greet()"]')).click();
// We need to give the browser time to display the alert
browser.wait(protractor.ExpectedConditions.alertIsPresent(), 1000);
var alertDialog = browser.switchTo().alert();
expect(alertDialog.getText()).toEqual('Hello World');
@@ -286,7 +277,7 @@ result is a non-undefined value (see value stabilization algorithm below).
</example>
### Reasons for using one-time binding
### 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.
@@ -306,9 +297,9 @@ then the expression is not fulfilled and will remain watched.
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 a value that is not `undefined`. If that's the case, deregister the watch. Otherwise,
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
@@ -325,10 +316,10 @@ Here are three example cases.
When interpolating text or attributes:
```html
<div name="attr: {{::color}}">text: {{::name | uppercase}}</div>
<div name="attr: {{::color}}">text: {{::name}}</div>
```
When using a directive with bidirectional binding and parameters that will not change:
When using a directive with bidirectional binding and the parameters will not change:
```js
someModule.directive('someDirective', function() {
@@ -351,6 +342,6 @@ When using a directive that takes an expression:
```html
<ul>
<li ng-repeat="item in ::items | orderBy:'name'">{{item.name}};</li>
<li ng-repeat="item in ::items">{{item.name}};</li>
</ul>
```
```
+5 -12
View File
@@ -3,8 +3,6 @@
@sortOrder 280
@description
# Filters
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.
@@ -34,13 +32,10 @@ E.g. the markup `{{ 1234 | number:2 }}` formats the number 1234 with 2 decimal p
## Using filters in controllers, services, and directives
You can also use filters in controllers, services, and directives.
<div class="alert alert-info">
For this, inject a dependency with the name `<filterName>Filter` into your controller/service/directive.
E.g. a filter called `number` is injected by using the dependency `numberFilter`. The injected argument
is a function that takes the value to format as first argument, and filter parameters starting with the second argument.
</div>
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
@@ -113,7 +108,6 @@ text upper-case.
No filter: {{greeting}}<br>
Reverse: {{greeting|reverse}}<br>
Reverse + uppercase: {{greeting|reverse:true}}<br>
Reverse, filtered in controller: {{filteredGreeting}}<br>
</div>
</file>
@@ -133,9 +127,8 @@ text upper-case.
return out;
};
})
.controller('MyController', ['$scope', 'reverseFilter', function($scope, reverseFilter) {
.controller('MyController', ['$scope', function($scope) {
$scope.greeting = 'hello';
$scope.filteredGreeting = reverseFilter($scope.greeting);
}]);
</file>
</example>
+17 -16
View File
@@ -3,8 +3,6 @@
@sortOrder 290
@description
# Forms
Controls (`input`, `select`, `textarea`) are ways for a user to enter data.
A Form is a collection of controls for the purpose of grouping related controls together.
@@ -16,7 +14,7 @@ be circumvented and thus can not be trusted. Server-side validation is still nec
secure application.
## Simple form
# Simple form
The key directive in understanding two-way data-binding is {@link ng.directive:ngModel ngModel}.
The `ngModel` directive provides the two-way data-binding by synchronizing the model to the view,
as well as view to the model. In addition it provides an {@link ngModel.NgModelController API}
@@ -33,7 +31,7 @@ for other directives to augment its behavior.
<input type="button" ng-click="reset()" value="Reset" />
<input type="submit" ng-click="update(user)" value="Save" />
</form>
<pre>user = {{user | json}}</pre>
<pre>form = {{user | json}}</pre>
<pre>master = {{master | json}}</pre>
</div>
@@ -64,7 +62,7 @@ For example: inputs of type `email` must have a value in the form of `user@domai
## Using CSS classes
# Using CSS classes
To allow styling of form as well as controls, `ngModel` adds these CSS classes:
@@ -95,7 +93,7 @@ and failing to satisfy its validity.
<input type="button" ng-click="reset()" value="Reset" />
<input type="submit" ng-click="update(user)" value="Save" />
</form>
<pre>user = {{user | json}}</pre>
<pre>form = {{user | json}}</pre>
<pre>master = {{master | json}}</pre>
</div>
@@ -130,7 +128,7 @@ and failing to satisfy its validity.
## Binding to form and control state
# Binding to form and control state
A form is an instance of {@link form.FormController FormController}.
The form instance can optionally be published into the scope using the `name` attribute.
@@ -185,7 +183,7 @@ didn't interact with a control
<input type="button" ng-click="reset(form)" value="Reset" />
<input type="submit" ng-click="update(user)" value="Save" />
</form>
<pre>user = {{user | json}}</pre>
<pre>form = {{user | json}}</pre>
<pre>master = {{master | json}}</pre>
</div>
</file>
@@ -214,7 +212,7 @@ didn't interact with a control
## Custom model update triggers
# Custom model update 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
@@ -255,7 +253,7 @@ will update the model only when the control loses focus (blur event).
## Non-immediate (debounced) model updates
# 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
@@ -296,7 +294,7 @@ after last change.
</file>
</example>
## Custom Validation
# Custom Validation
Angular provides basic implementation for most common HTML5 {@link ng.directive:input input}
types: ({@link input[text] text}, {@link input[number] number}, {@link input[url] url},
@@ -383,7 +381,7 @@ In the following example we create two directives:
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
var usernames = ['Jim', 'John', 'Jill', 'Jackie'];
var usernames = ['Jim', 'John', 'Jill', 'Jackie'];
ctrl.$asyncValidators.username = function(modelValue, viewValue) {
@@ -413,7 +411,7 @@ In the following example we create two directives:
</file>
</example>
## Modifying built-in validators
# Modifying built-in validators
Since Angular itself uses `$validators`, you can easily replace or remove built-in validators,
should you find it necessary. The following example shows you how to overwrite the email validator
@@ -440,7 +438,8 @@ Note that you can alternatively use `ng-pattern` to further restrict the validat
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@example\.com$/i;
return {
require: '?ngModel',
require: 'ngModel',
restrict: '',
link: function(scope, elm, attrs, ctrl) {
// only apply the validator if ngModel is present and Angular has added the email validator
if (ctrl && ctrl.$validators.email) {
@@ -457,7 +456,7 @@ Note that you can alternatively use `ng-pattern` to further restrict the validat
</example>
## Implementing custom form controls (using `ngModel`)
# Implementing custom form controls (using `ngModel`)
Angular implements all of the basic HTML form controls ({@link ng.directive:input input},
{@link ng.directive:select select}, {@link ng.directive:textarea textarea}),
which should be sufficient for most cases. However, if you need more flexibility,
@@ -494,7 +493,9 @@ The following example shows how to add two-way data-binding to contentEditable e
link: function(scope, elm, attrs, ctrl) {
// view -> model
elm.on('blur', function() {
ctrl.$setViewValue(elm.html());
scope.$apply(function() {
ctrl.$setViewValue(elm.html());
});
});
// model -> view
+2 -329
View File
@@ -18,10 +18,8 @@ application means providing translations and localized formats for the abstracte
Angular supports i18n/l10n for {@link ng.filter:date date}, {@link ng.filter:number number} and
{@link ng.filter:currency currency} filters.
Localizable pluralization is supported via the {@link ng.directive:ngPluralize `ngPluralize`
directive}. Additionally, you can use {@link guide/i18n#messageformat-extensions MessageFormat extensions} to
`$interpolate` for localizable pluralization and gender support in all interpolations via the
`ngMessageFormat` module.
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}.
@@ -139,328 +137,3 @@ 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
displaying the date with a timezone specified by the developer.
<a name="MessageFormat"></a>
## MessageFormat extensions
You can write localizable plural and gender based messages in Angular interpolation expressions and
`$interpolate` calls.
This syntax extension is provided by way of the `ngMessageFormat` module that your application can
depend upon (shipped separately as `angular-message-format.min.js` and `angular-message-format.js`.)
A current limitation of the `ngMessageFormat` module, is that it does not support redefining the
`$interpolate` start and end symbols. Only the default `{{` and `}}` are allowed.
The syntax extension is based on a subset of the ICU MessageFormat syntax that covers plurals and
gender selections. Please refer to the links in the “Further Reading” section at the bottom of this
section.
You may find it helpful to play with the following example as you read the explanations below:
<example name="message-format-example" module="messageFormatExample" deps="angular-message-format.js">
<file name="index.html">
<div ng-controller="ckCtrl">
<b>Set number of recipients</b>
<button ng-click="setNumRecipients(0)">None</button>
<button ng-click="setNumRecipients(1)">One</button>
<button ng-click="setNumRecipients(2)">Two</button>
<button ng-click="setNumRecipients(3)">Three</button>
<br><br>
<b>Sender's</b> name: <input ng-model="sender.name"> &nbsp;&nbsp;
<br><br><b>Recipients</b><br>
<div ng-repeat="recipient in recipients">
Name: <input ng-model="recipient.name"> &nbsp;&nbsp;
Gender: <button ng-click="setGender(recipient, 'male')">male</button>
<button ng-click="setGender(recipient, 'female')">female</button>
<button ng-click="setGender(recipient, 'other')">other</button>
</div>
<br><br><b>Message</b><br>
{{recipients.length, plural, offset:1
=0 {You ({{sender.name}}) gave no gifts}
=1 { {{ recipients[0].gender, select,
male {You ({{sender.name}}) gave him ({{recipients[0].name}}) a gift.}
female {You ({{sender.name}}) gave her ({{recipients[0].name}}) a gift.}
other {You ({{sender.name}}) gave them ({{recipients[0].name}}) a gift.}
}}
}
one { {{ recipients[0].gender, select,
male {You ({{sender.name}}) gave him ({{recipients[0].name}}) and one other person a gift.}
female {You ({{sender.name}}) gave her ({{recipients[0].name}}) and one other person a gift.}
other {You ({{sender.name}}) gave them ({{recipients[0].name}}) and one other person a gift.}
}}
}
other {You ({{sender.name}}) gave {{recipients.length}} people gifts. }
}}
<br><br><b>In an attribute</b><br>
<div attrib="{{recipients.length, plural, offset:1
=0 {You ({{sender.name}}) gave no gifts}
=1 { {{ recipients[0].gender, select,
male {You ({{sender.name}}) gave him ({{recipients[0].name}}) a gift.}
female {You ({{sender.name}}) gave her ({{recipients[0].name}}) a gift.}
other {You ({{sender.name}}) gave them ({{recipients[0].name}}) a gift.}
}}
}
one { {{ recipients[0].gender, select,
male {You ({{sender.name}}) gave him ({{recipients[0].name}}) and one other person a gift.}
female {You ({{sender.name}}) gave her ({{recipients[0].name}}) and one other person a gift.}
other {You ({{sender.name}}) gave them ({{recipients[0].name}}) and one other person a gift.}
}}
}
other {You ({{sender.name}}) gave {{recipients.length}} people gifts. }
}}">
This div has an attribute interpolated with messageformat. Use the DOM inspector to check it out.
</div>
</div>
</file>
<file name="app.js">
function Person(name, gender) {
this.name = name;
this.gender = gender;
}
angular.module('messageFormatExample', ['ngMessageFormat'])
.controller('ckCtrl', function ($scope, $injector, $parse) {
var people = [ new Person("Alice", "female"),
new Person("Bob", "male"),
new Person("Charlie", "male") ];
$scope.sender = new Person("Harry Potter", "male");
$scope.recipients = people.slice();
$scope.setNumRecipients = function(n) {
n = n > people.length ? people.length : n;
$scope.recipients = people.slice(0, n);
};
$scope.setGender = function(person, gender) {
person.gender = gender;
};
});
</file>
</example>
### Plural Syntax
The syntax for plural based message selection looks like the following:
```text
{{NUMERIC_EXPRESSION, plural,
=0 {MESSAGE_WHEN_VALUE_IS_0}
=1 {MESSAGE_WHEN_VALUE_IS_1}
=2 {MESSAGE_WHEN_VALUE_IS_2}
=3 {MESSAGE_WHEN_VALUE_IS_3}
...
zero {MESSAGE_WHEN_PLURAL_CATEGORY_IS_ZERO}
one {MESSAGE_WHEN_PLURAL_CATEGORY_IS_ONE}
two {MESSAGE_WHEN_PLURAL_CATEGORY_IS_TWO}
few {MESSAGE_WHEN_PLURAL_CATEGORY_IS_FEW}
many {MESSAGE_WHEN_PLURAL_CATEGORY_IS_MANY}
other {MESSAGE_WHEN_THERE_IS_NO_MATCH}
}}
```
Please note that whitespace (including newline) is generally insignificant except as part of the
actual message text that occurs in curly braces. Whitespace is generally used to aid readability.
Here, `NUMERIC_EXPRESSION` is an expression that evaluates to a numeric value based on which the
displayed message should change based on pluralization rules.
Following the Angular expression, you would denote the plural extension syntax by the `, plural,`
syntax element. The spaces there are optional.
This is followed by a list of selection keyword and corresponding message pairs. The "other"
keyword and corresponding message are **required** but you may have as few or as many of the other
categories as you need.
#### Selection Keywords
The selection keywords can be either exact matches or language dependent [plural
categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html).
Exact matches are written as the equal sign followed by the exact value. `=0`, `=1`, `=2` and
`=123` are all examples of exact matches. Note that there should be no space between the equal sign
and the numeric value.
Plural category matches are single words corresponding to the [plural
categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html) of
the CLDR plural category spec. These categories vary by locale. The "en" (English) locale, for
example, defines just "one" and "other" while the "ga" (Irish) locale defines "one", "two", "few",
"many" and "other". Typically, you would just write the categories for your language. During
translation, the translators will add or remove more categories depending on the target locale.
Exact matches always win over keyword matches. Therefore, if you define both `=0` and `zero`, when
the value of the expression is zero, the `=0` message is the one that will be selected. (The
duplicate keyword categories are helpful when used with the optional `offset` syntax described
later.)
#### Messages
Messages immediately follow a selection keyword and are optionally preceded by whitespace. They are
written in single curly braces (`{}`). They may contain Angular interpolation syntax inside them.
In addition, the `#` symbol is a placeholder for the actual numeric value of the expression.
### Simple plural example
```text
{{numMessages, plural,
=0 {You have no new messages}
=1 {You have one new message}
other {You have # new messages}
}}
```
Because these messages can themselves contain Angular expressions, you could also write this as
follows:
```text
{{numMessages, plural,
=0 {You have no new messages}
=1 {You have one new message}
other {You have {{numMessages}} new messages}
}}
```
### Plural syntax with optional `offset`
The plural syntax supports an optional `offset` syntax that is used in matching. It's simpler to
explain this with an example.
```text
{{recipients.length, plural, offset:1
=0 {You gave no gifts}
=1 {You gave {{recipients[0].name}} a gift}
one {You gave {{recipients[0].name}} and one other person a gift}
other {You gave {{recipients[0].name}} and # other people a gift}
}}
```
When an `offset` is specified, the matching works as follows. First, the exact value of the Angular
expression is matched against the exact matches (i.e. `=N` selectors) to find a match. If there is
one, that message is used. If there was no match, then the offset value is subtracted from the
value of the expression and locale specific pluralization rules are applied to this new value to
obtain its plural category (such as “one”, “few”, “many”, etc.) and a match is attempted against the
keyword selectors and the matching message is used. If there was no match, then the “other”
category (required) is used. The value of the `#` character inside a message is the value of
original expression reduced by the offset value that was specified.
### Escaping / Quoting
You will need to escape curly braces or the `#` character inside message texts if you want them to
be treated literally with no special meaning. You may quote/escape any character in your message
text by preceding it with a `\` (backslash) character. The backslash character removes any special
meaning to the character that immediately follows it. Therefore, you can escape or quote the
backslash itself by preceding it with another backslash character.
### Gender (aka select) Syntax
The gender support is provided by the more generic "select" syntax that is more akin to a switch
statement. It is general enough to support use for gender based messages.
The syntax for gender based message selection looks like the following:
```text
{{EXPRESSION, select,
male {MESSAGE_WHEN_EXPRESSION_IS_MALE}
female {MESSAGE_WHEN_EXPRESSION_IS_FEMALE}
...
other {MESSAGE_WHEN_THERE_IS_NO_GENDER_MATCH}
}}
```
Please note that whitespace (including newline) is generally insignificant except as part of the
actual message text that occurs in curly braces. Whitespace is generally used to aid readability.
Here, `EXPRESSION` is an Angular expression that evaluates to the gender of the person that
is used to select the message that should be displayed.
The Angular expression is followed by `, select,` where the spaces are optional.
This is followed by a list of selection keyword and corresponding message pairs. The "other"
keyword and corresponding message are **required** but you may have as few or as many of the other
gender values as you need (i.e. it isn't restricted to male/female.) Note however, that the
matching is **case-sensitive**.
#### Selection Keywords
Selection keywords are simple words like "male" and "female". The keyword, "other", and its
corresponding message are required while others are optional. It is used when the Angular
expression does not match (case-insensitively) any of the other keywords specified.
#### Messages
Messages immediately follow a selection keyword and are optionally preceded by whitespace. They are
written in single curly braces (`{}`). They may contain Angular interpolation syntax inside them.
### Simple gender example
```text
{{friendGender, select,
male {Invite him}
female {Invite her}
other {Invite them}
}}
```
### Nesting
As mentioned in the syntax for plural and select, the embedded messages can contain Angular
interpolation syntax. Since you can use MessageFormat extensions in Angular interpolation, this
allows you to nest plural and gender expressions in any order.
Please note that if these are intended to reach a translator and be translated, it is recommended
that the messages appear as a whole and not be split up.
### Demonstration of nesting
This is taken from the above example.
```text
{{recipients.length, plural, offset:1
=0 {You ({{sender.name}}) gave no gifts}
=1 { {{ recipients[0].gender, select,
male {You ({{sender.name}}) gave him ({{recipients[0].name}}) a gift.}
female {You ({{sender.name}}) gave her ({{recipients[0].name}}) a gift.}
other {You ({{sender.name}}) gave them ({{recipients[0].name}}) a gift.}
}}
}
one { {{ recipients[0].gender, select,
male {You ({{sender.name}}) gave him ({{recipients[0].name}}) and one other person a gift.}
female {You ({{sender.name}}) gave her ({{recipients[0].name}}) and one other person a gift.}
other {You ({{sender.name}}) gave them ({{recipients[0].name}}) and one other person a gift.}
}}
}
other {You ({{sender.name}}) gave {{recipients.length}} people gifts. }
}}
```
### Differences from the ICU MessageFormat syntax
This section is useful to you if you're already familiar with the ICU MessageFormat syntax.
This syntax extension, while based on MessageFormat, has been designed to be backwards compatible
with existing AngularJS interpolation expressions. The key rule is simply this: **All
interpolations are done inside double curlies.** The top level comma operator after an expression
inside the double curlies causes MessageFormat extensions to be recognized. Such a top level comma
is otherwise illegal in an Angular expression and is used by MessageFormat to specify the function
(such as plural/select) and it's related syntax.
To understand the extension, take a look at the ICU MessageFormat syntax as specified by the ICU
documentation. Anywhere in that MessageFormat that you have regular message text and you want to
substitute an expression, just put it in double curlies instead of single curlies that MessageFormat
dictates. This has a huge advantage. **You are no longer limited to simple identifiers for
substitutions**. Because you are using double curlies, you can stick in any arbitrary interpolation
syntax there, including nesting more MessageFormat expressions!
### Further Reading
For more details, please refer to our [design doc](https://docs.google.com/a/google.com/document/d/1pbtW2yvtmFBikfRrJd8VAsabiFkKezmYZ_PbgdjQOVU/edit).
You can read more about the ICU MessageFormat syntax at
[Formatting Messages | ICU User Guide](http://userguide.icu-project.org/formatparse/messages#TOC-MessageFormat).
+1 -4
View File
@@ -28,7 +28,4 @@ browsers, but it is up to you to test and decide whether it works for your parti
To ensure your Angular application works on IE please consider:
1. Use `ng-style` tags instead of `style="{{ someCss }}"`. The latter works in Chrome and Firefox
but does not work in Internet Explorer <= 11 (the most recent version at time of writing).
2. For the `type` attribute of buttons, use `ng-attr-type` tags instead of
`type="{{ someExpression }}"`. If using the latter, Internet Explorer overwrites the expression
with `type="submit"` before Angular has a chance to interpolate it.
but does not work in Internet Explorer <= 11 (the most recent version at time of writing).
+1 -4
View File
@@ -91,7 +91,7 @@ This is a short list of libraries with specific support and documentation for wo
### Server-Specific
* **Django:** [Tutorial](http://blog.mourafiq.com/post/55034504632/end-to-end-web-app-with-django-rest-framework), [Integrating AngularJS with Django](http://django-angular.readthedocs.org/en/latest/integration.html), [Getting Started with Django Rest Framework and AngularJS](http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html)
* **FireBase:** [AngularFire](http://angularfire.com/), [Firebase Foundations for AngularJS](http://blog.watchandcode.com/firebase-foundations/), [Realtime Apps with AngularJS and FireBase (video)](http://www.youtube.com/watch?v=C7ZI7z7qnHU)
* **FireBase:** [AngularFire](http://angularfire.com/), [Realtime Apps with AngularJS and FireBase (video)](http://www.youtube.com/watch?v=C7ZI7z7qnHU)
* **Google Cloud Platform: **[with Cloud Endpoints](https://cloud.google.com/developers/articles/angularjs-cloud-endpoints-recipe-for-building-modern-web-applications/), [with Go](https://github.com/GoogleCloudPlatform/appengine-angular-gotodos)
* **Hood.ie:** [60 Minutes to Awesome](http://www.roberthorvick.com/2013/06/30/todomvc-angularjs-hood-ie-60-minutes-to-awesome/)
* **MEAN Stack: **[Blog post](http://blog.mongodb.org/post/49262866911/the-mean-stack-mongodb-expressjs-angularjs-and), [Setup](http://thecodebarbarian.wordpress.com/2013/07/22/introduction-to-the-mean-stack-part-one-setting-up-your-tools/), [GDL Video](https://developers.google.com/live/shows/913996610)
@@ -111,12 +111,10 @@ This is a short list of libraries with specific support and documentation for wo
* [AngularJS : Novice to Ninja](http://www.amazon.in/AngularJS-Novice-Ninja-Sandeep-Panda/dp/0992279453) by Sandeep Panda
* [AngularJS UI Development](http://www.amazon.com/AngularJS-UI-Development-Amit-Ghart-ebook/dp/B00OXVAK7A) by Amit Gharat and Matthias Nehlsen
* [Responsive Web Design with AngularJS](http://www.amazon.com/Responsive-Design-AngularJS-Sandeep-Kumar/dp/178439842X) by Sandeep Kumar Patel
* [Professional AngularJS](http://www.amazon.com/Professional-AngularJS-Valeri-Karpov/dp/1118832078/)
###Videos:
* [egghead.io](http://egghead.io/)
* [Angular on YouTube](http://youtube.com/angularjs)
* [Firebase Foundations for AngularJS](http://blog.watchandcode.com/firebase-foundations/)
### Courses
* **Free online:**
@@ -124,7 +122,6 @@ This is a short list of libraries with specific support and documentation for wo
[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:**
[The Angular Course (115 videos that show you how to build a full app)](http://watchandcode.com/courses/angular-course/),
[Pluralsite (3 courses)](http://www.pluralsight.com/training/Courses/Find?highlight=true&searchTerm=angularjs),
[Tuts+](https://tutsplus.com/course/easier-js-apps-with-angular/),
[lynda.com](http://www.lynda.com/AngularJS-tutorials/Up-Running-AngularJS/133318-2.html),
-146
View File
@@ -1,146 +0,0 @@
@ngdoc overview
@name Interpolation
@sortOrder 275
@description
# Interpolation and data-binding
Interpolation markup with embedded {@link guide/expression expressions} is used by Angular to
provide data-binding to text nodes and attribute values.
An example of interpolation is shown below:
```html
<a ng-href="img/{{username}}.jpg">Hello {{username}}!</a>
```
### How text and attribute bindings work
During the compilation process the {@link ng.$compile compiler} uses the {@link ng.$interpolate $interpolate}
service to see if text nodes and element attributes contain interpolation markup with embedded expressions.
If that is the case, the compiler adds an interpolateDirective to the node and
registers {@link ng.$rootScope.Scope#$watch watches} on the computed interpolation function,
which will update the corresponding text nodes or attribute values as part of the
normal {@link ng.$rootScope.Scope#$digest digest} cycle.
Note that the interpolateDirective has a priority of 100 and sets up the watch in the preLink function.
### Binding to boolean attributes
Attributes such as `disabled` are called `boolean` attributes, because their presence means `true` and
their absence means `false`. We cannot use normal attribute bindings with them, because the HTML
specification does not require browsers to preserve the values of boolean attributes. This means that
if we put an Angular interpolation expression into such an attribute then the binding information
would be lost, because the browser ignores the attribute value.
In the following example, the interpolation information would be ignored and the browser would simply
interpret the attribute as present, meaning that the button would always be disabled.
```html
Disabled: <input type="checkbox" ng-model="isDisabled" />
<button disabled="{{isDisabled}}">Disabled</button>
```
For this reason, Angular provides special `ng`-prefixed directives for the following boolean attributes:
{@link ngDisabled `disabled`}, {@link ngRequired `required`}, {@link ngSelected `selected`},
{@link ngChecked `checked`}, {@link ngReadonly `readOnly`} , and {@link ngOpen `open`}.
These directives take an expression inside the attribute, and set the corresponding boolean attribute
to true when the expression evaluates to truthy.
```html
Disabled: <input type="checkbox" ng-model="isDisabled" />
<button ng-disabled="isDisabled">Disabled</button>
```
### `ngAttr` for binding to arbitrary attributes
Web browsers are sometimes picky about what values they consider valid for attributes.
For example, considering this template:
```html
<svg>
<circle cx="{{cx}}"></circle>
</svg>
```
We would expect Angular to be able to bind to this, but when we check the console we see
something like `Error: Invalid value for attribute cx="{{cx}}"`. Because of the SVG DOM API's
restrictions, you cannot simply write `cx="{{cx}}"`.
With `ng-attr-cx` you can work around this problem.
If an attribute with a binding is prefixed with the `ngAttr` prefix (denormalized as `ng-attr-`)
then during the binding it will be applied to the corresponding unprefixed attribute. This allows
you to bind to attributes that would otherwise be eagerly processed by browsers
(e.g. an SVG element's `circle[cx]` attributes). When using `ngAttr`, the `allOrNothing` flag of
{@link ng.$interpolate $interpolate} is used, so if any expression in the interpolated string
results in `undefined`, the attribute is removed and not added to the element.
For example, we could fix the example above by instead writing:
```html
<svg>
<circle ng-attr-cx="{{cx}}"></circle>
</svg>
```
If one wants to modify a camelcased attribute (SVG elements have valid camelcased attributes),
such as `viewBox` on the `svg` element, one can use underscores to denote that the attribute to bind
to is naturally camelcased.
For example, to bind to `viewBox`, we can write:
```html
<svg ng-attr-view_box="{{viewBox}}">
</svg>
```
Other attributes may also not work as expected when they contain interpolation markup, and
can be used with `ngAttr` instead. The following is a list of known problematic attributes:
- **size** in `<select>` elements (see [issue 1619](https://github.com/angular/angular.js/issues/1619))
- **placeholder** in `<textarea>` in Internet Explorer 10/11 (see [issue 5025](https://github.com/angular/angular.js/issues/5025))
- **type** in `<button>` in Internet Explorer 11 (see [issue 14117](https://github.com/angular/angular.js/issues/5025))
### Embedding interpolation markup inside expressions
<div class="alert alert-danger">
**Note:** Angular directive attributes take either expressions *or* interpolation markup with embedded expressions.
It is considered **bad practice** to embed interpolation markup inside an expression:
</div>
```html
<div ng-show="form{{$index}}.$invalid"></div>
```
You should instead delegate the computation of complex expressions to the scope, like this:
```html
<div ng-show="getForm($index).$invalid"></div>
```
```js
function getForm(index) {
return $scope['form' + index];
}
```
You can also access the `scope` with `this` in your templates:
```html
<div ng-show="this['form' + $index].$invalid"></div>
```
#### Why mixing interpolation and expressions is bad practice:
- It increases the complexity of the markup
- There is no guarantee that it works for every directive, because interpolation itself is a directive.
If another directive accesses attribute data before interpolation has run, it will get the raw
interpolation markup and not data.
- It impacts performance, as interpolation adds another watcher to the scope.
- Since this is not recommended usage, we do not test for this, and changes to
Angular core may break your code.
File diff suppressed because it is too large Load Diff
+6 -5
View File
@@ -75,8 +75,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.
You can find a community
[style guide](https://github.com/johnpapa/angular-styleguide) to help yourself when application grows.
We've also
[written a document](http://angularjs.blogspot.com/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.
@@ -139,7 +140,7 @@ The above is a suggestion. Tailor it to your needs.
# Module Loading & Dependencies
A module is a collection of configuration and run blocks which get applied to the application
during the bootstrap process. In its simplest form the module consists of a collection of two kinds
during the bootstrap process. In its simplest form the module consist of a collection of two kinds
of blocks:
1. **Configuration blocks** - get executed during the provider registrations and configuration
@@ -196,14 +197,14 @@ Then Angular applies configuration blocks in the same order they were registered
## Run Blocks
Run blocks are the closest thing in Angular to the main method. A run block is the code which
needs to run to kickstart the application. It is executed after all of the services have been
needs to run to kickstart the application. It is executed after all of the service have been
configured and the injector has been created. Run blocks typically contain code which is hard
to unit-test, and for this reason should be declared in isolated modules, so that they can be
ignored in the unit-tests.
## Dependencies
Modules can list other modules as their dependencies. Depending on a module implies that the required
Modules can list other modules as their dependencies. Depending on a module implies that required
module needs to be loaded before the requiring module is loaded. In other words the configuration
blocks of the required modules execute before the configuration blocks of the requiring module.
The same is true for the run blocks. Each module can only be loaded once, even if multiple other
+1 -1
View File
@@ -44,7 +44,7 @@ and {@link angular.reloadWithDebugInfo `angular.reloadWithDebugInfo`}.
## Strict DI Mode
Using strict di mode in your production application will throw errors when an injectable
Using strict di mode in your production application will throw errors when a injectable
function is not
{@link di#dependency-annotation annotated explicitly}. Strict di mode is intended to help
you make sure that your code will work when minified. However, it also will force you to

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