Compare commits

..

415 Commits

Author SHA1 Message Date
Brian Ford 819dd5df92 docs(changelog): release notes for 1.3.0-beta.9 release-naming 2014-05-16 15:14:12 -07:00
Shahar Talmi 5fbd618c2f fix(ngClass): handle index changes when an item is unshifted
Closes #7256
2014-05-16 14:02:41 -07:00
M Alix 63f284a55c chore($compile): make webstorm's regexp parser happy
Minor change puts \- at end of character pattern

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

Functionally absolutely equivalent. No test change needed.

Closes #7093

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

BREAKING CHANGE:

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

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

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

Before:

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

After:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

The same applies for `srcset` as well.

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

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

BREAKING CHANGE:

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

Example:

Previously, the following:

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

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

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

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

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

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

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

BREAKING CHANGE:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

> Nothing big.

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

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

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

JS example:

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

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

HTML:

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

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

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

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

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

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

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

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

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

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

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

or per instance:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Closes #4934
Closes #6275

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

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

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

to:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

The commit is causing regressions.

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

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

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

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

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

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

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

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

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

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

Oops!

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

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

Closes #6674
Closes #6739

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

In markup, this might look like so:

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

Or in JS:

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

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

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

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

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

BREAKING CHANGE:

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

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

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

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

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

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

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

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

    To migrate the code follow the example below:

    Before:

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

    After:

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

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

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

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

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

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

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

instead of:

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

do:

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

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

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

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

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

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

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

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

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

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

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

See the previous commit for more info.

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

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

Long term this should be fixed in npm: https://github.com/npm/npm/issues/3581
2014-03-14 23:01:53 -07:00
jenkins a15d9cb4b6 chore(release): update cdn version 2014-03-14 17:40:25 -07:00
Vojta Jina 9bfbb16e23 chore(scripts): fix the versions script again 2014-03-14 16:26:40 -07:00
Vojta Jina 2b741dc8b8 chore(scripts): fix the versions script 2014-03-14 15:59:23 -07:00
Vojta Jina e888dde3c5 chore(scripts): disable testing seed and phonecat during a release
This reverts commit d5294ebfa0.

It turned out to be more work and I don't wanna deal with it right now.
2014-03-14 14:48:56 -07:00
Vojta Jina d5294ebfa0 chore(scripts): test seed and phonecat during a release 2014-03-14 14:24:43 -07:00
Vojta Jina 6d6ebf7c61 chore(scripts): make the release script more flexible
Now the SHA can be short/long, whateva.
2014-03-14 14:24:21 -07:00
Jeff Cross 44b940e88d chore: update changelog for 1.3.0-beta.2 2014-03-14 14:02:35 -07:00
Pawel Kozlowski 56e73ea355 fix($http): don't covert 0 status codes to 404 for non-file protocols
PR #5547 introduced conversion of all 0 status codes to 404 for cases
where no response was recieved (previously this was done for the
file:// protocol only). But this mechanism is too eager and
masks legitimate cases where status 0 should be returned. This commits
reverts to the previous mechanism of handling 0 status code for the
file:// protocol (converting 0 to 404) while retaining the returned
status code 0 for all the protocols other than file://

Fixes #6074
Fixes #6155
2014-03-14 13:44:56 -07:00
Vojta Jina bfb6af7053 chore: make compare-master-to-stable script more flexible 2014-03-14 11:43:15 -07:00
Vojta Jina d7be9588a0 chore: update compare-master-to-stable to use v1.2.x 2014-03-14 11:26:30 -07:00
Vojta Jina 53e4da8eab chore: fix compare-master-to-stable script 2014-03-14 11:24:24 -07:00
Matias Niemelä 7b5e019981 fix($$rAF): always fallback to a $timeout incase native rAF isn't supported
Closes #6654
2014-03-14 12:42:07 -04:00
Tomer Chachamu 129e2e021a fix(ngAnimate): setting classNameFilter disables animation inside ng-if
Closes #6539
2014-03-14 12:40:40 -04:00
Peter Bacon Darwin 3cc02e7f03 docs(scripts/utils.inc): clarify documentation 2014-03-14 11:09:36 +00:00
Nick Heiner 79592ce9e2 docs(ngMock): grammar nitpick. 2014-03-13 16:52:59 -07:00
Sagie Maoz a9a38d84b9 docs(guide/compiler): add missing closing parenthesis 2014-03-13 16:39:23 -07:00
Nick Carter 24a67f9515 docs(guide/unit-testing): fix typo 2014-03-13 16:37:33 -07:00
Wesley Cho 91ef3a31a0 docs($resource): fix example using promise 2014-03-13 16:33:38 -07:00
Thomas Belin cea44b3e86 fix (ngAnimate): fix requestAnimationFrame for old version of Firefox
The recent $$RAFProvider which is a wrapper for the native
requestAnimationFrame method doesn't use the mozRequestAnimationFrame.
Old versions of FF (20 for example) crash if ngAnimate is included

No breaking changes and fix issue https://github.com/angular/angular.js/issues/6535

Closes #6535
Closes #6540
2014-03-13 16:31:11 -07:00
Tobias Bosch e8c6b9bf25 chore(CHANGELOG.md): add input type date PR as breaking change
Related to #6630
2014-03-13 16:03:20 -07:00
Peter Bacon Darwin 5412372e93 chore(shrinkwrap): update dgeni-packages 2014-03-13 18:33:10 +00:00
Peter Bacon Darwin 4f823f902d chore(shrinkwrap): re-run shrinkwrap locally
This will make the following commit clearer when the update is run.
2014-03-13 18:33:10 +00:00
Tobias Bosch fe0e434a87 chore(version-info): use remote tags and increment patch version 2014-03-12 15:19:48 -07:00
Peter Bacon Darwin edad4e63df chore(version-info): better error msg if not tags 2014-03-12 19:22:32 +00:00
Igor Minar f684cb09a5 chore(npm): add shrinkwrap to lock down dependencies
We need to be able to build angular at older shas, without the lock file / shrinkwrap file
the dependencies will resolve differently on different machines and at different times.

This will help us avoid broken builds and hard to track down issues.

I had to manually edit this file after it was generated because `npm shrinkwrap` will install
optional dependencies as if they were hard dependencies.

See: https://github.com/npm/npm/issues/2679#issuecomment-37361236

My manual edit:

```
diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json
index 756df44..dc157eb 100644
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -3110,19 +3110,7 @@
         "chokidar": {
           "version": "0.8.1",
           "from": "https://registry.npmjs.org/chokidar/-/chokidar-0.8.1.tgz",
-          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-0.8.1.tgz",
-          "dependencies": {
-            "fsevents": {
-              "version": "0.1.6",
-              "from": "fsevents@0.1.6",
-              "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-0.1.6.tgz"
-            },
-            "recursive-readdir": {
-              "version": "0.0.2",
-              "from": "recursive-readdir@0.0.2",
-              "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-0.0.2.tgz"
-            }
-          }
+          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-0.8.1.tgz"
         },
         "glob": {
           "version": "3.2.9",
```

Additionally chokidar doesn't list the dependencies above as optional, but that will hopefully
be soon fixed: https://github.com/paulmillr/chokidar/pull/106

In the meantime the patch from the PR above needs to be applied to
node_modules/karma/node_modules/chokidar/package.json before running `npm shrinkwrap`

----

After this change is applied, angular core developers don't need to do anything differently,
except when updating dependencies we need to call `npm update && npm shrinkwrap --dev`
followed by reappling my patch above until npm's bug.

Closes #6653
2014-03-11 22:44:37 -07:00
Peter Bacon Darwin d7717d93e4 chore(doc-gen): fix dependencyPath 2014-03-11 20:24:34 +00:00
Peter Bacon Darwin 247ec19c82 chore(package.json): update dgeni-packages dependency
The new version of dgeni-packages/ngdoc generates a manifest for each
example that can be used by plunker.
2014-03-11 19:10:34 +00:00
Stéphane Reynaud 78165c224d docs(tutorial): display button icons (Previous, Live Demo, ...)
In relation to https://github.com/angular/dgeni-packages/pull/8

Closes #6641
2014-03-11 10:19:42 +00:00
Peter Bacon Darwin d1214af132 chore(build): refactor build version information 2014-03-11 06:35:19 +00:00
Peter Bacon Darwin 11c5bb7f3d docs(versions): rework the version info extraction
The docs were relying on the grunt/util module for getting version info
but this was unreliable and full of custom regexes.  This is moved into
a new version-info module that makes much better use of the semver library.
2014-03-11 06:35:19 +00:00
Louis Haußknecht d6419d0aff docs(route.js): changed html entities lt gt to < and > 2014-03-10 15:37:44 -07:00
Basem Mostafa 55848a9139 docs(ngRepeat): Separate animation class in new lines
Moving to new lines & making it bold to avoid confusion
when they r all in same line without any separation

Closes #6633
2014-03-10 15:34:16 -07:00
Matias Niemelä 0f13f24ad2 chore(docs): ensure the "Improve this doc" button is clickable
Closes #6631
2014-03-10 15:09:25 -04:00
doodeec 0d8de2d3ea docs($route): change routes property to correct type
change $route.routes property type to Object, property is marked incorrectly as an Array

Closes #6552
2014-03-10 02:19:57 -07:00
Narretz 7833ce0a6e docs(guide/forms): remove unnecessary controller reference
the controller reference was breaking the custom validation example

Closes #6525
Closes #6533
2014-03-10 02:14:50 -07:00
Igor Minar 47ab8df455 feat(): whitelist blob urls for sanitization of data-bound image urls
Closes #4623
2014-03-10 01:39:15 -07:00
chadfennell b700282ffd docs(guide/providers): remove unneeded word "the"
no need to specify which space, there's only one :)

Closes #6622
2014-03-09 13:25:29 -04:00
Chung-Min Cheng 1b9395ea8f docs(tutorial/step-12): correct application name
Fixed wrong app name:
- phonecat -> phonecatApp, which meets the code in app.js

Closes #6611
2014-03-08 18:32:32 +01:00
Peter Bacon Darwin 44d160e3ce docs(Error404): better heading 2014-03-08 07:06:20 +00:00
Peter Bacon Darwin 4f90c9b531 docs(Error404): improve search results layout 2014-03-08 06:34:47 +00:00
jenkins 11aceac273 chore(release): update cdn version 2014-03-07 17:03:12 -08:00
Tobias Bosch f08bf6f1f7 chore(release): Update package.json to reflect 1.3.x version 2014-03-07 16:23:14 -08:00
Brian Ford ca4ddfadba docs(changelog): release notes for 1.3.0-beta.1 retractable-eyebrow 2014-03-07 15:27:39 -08:00
Sekib Omazic 4bab3d8227 docs($sce): correct typo
`consititute` -> `constitute`

Typo fixed

Closes #6607
2014-03-07 18:12:40 -05:00
Igor Minar b12c6b485d chore(build): upgrade grunt-jscs-checker to ~0.4.0
this is primarily to resolve peerdependency version mismatch issue
2014-03-07 15:06:53 -08:00
Sekib Omazic 9c353b4f17 docs(ngBind): fix typo
`preferrable` -> `preferable`

Typo fixed

Closes #6606
2014-03-07 17:55:10 -05:00
Igor Minar 21243d62a2 docs(guide/migration): fix broken link 2014-03-07 13:51:42 -08:00
Sekib Omazic ad309b1332 docs(guide/migration): fix typos
A few typos fixed.

Closes #6605
2014-03-07 16:30:40 -05:00
Sekib Omazic 7a75356388 docs(guide/directive): fix typo
`restictions` -> `restrictions`

Closes #6604
2014-03-07 16:15:54 -05:00
Peter Bacon Darwin dc57fe97e1 style(jsdoc tags): remove/ammend invalid tags
As highlighted by the new sterner dgeni.
2014-03-07 20:05:59 +00:00
Peter Bacon Darwin 853999de10 docs(404 errors): provide a better 404 experience
It is a bit rough and ready but does a better job than nothing.
2014-03-07 20:05:58 +00:00
Lucas Galfasó 53ec5e13e5 fix($compile): support templates with thead and tfoot root elements
If the first element in a template is a <thead> or a <tfoot>, then
use the existing logic to handle table elements compilation.

Closes #6289
2014-03-07 10:06:12 -08:00
Peter Bacon Darwin 235731d32b chore(package): update dgeni dependencies 2014-03-07 11:19:17 +00:00
Peter Bacon Darwin 5af8d2963b chore(doc-gen): add contentFolder config property 2014-03-07 11:19:17 +00:00
Peter Bacon Darwin 0b4a41af58 chore(doc-gen): add inline @type tag 2014-03-07 11:19:17 +00:00
Peter Bacon Darwin 0e066693f2 docs($route): fix formatting of example code 2014-03-07 11:19:17 +00:00
Peter Bacon Darwin 02cc2b2014 chore(doc-gen): fix error-doc processor
The meta-data should be parsed from the name not the id.
2014-03-07 10:41:48 +00:00
Peter Bacon Darwin 486f1b4e51 chore(doc-gen): improve error reporting 2014-03-07 10:41:48 +00:00
Eddie Hedges c5f2f583ab docs(tutorial): link update for Jasmine
Jasmine doesn't live at the replaced link anymore.
It has a link to click through, but I figured it would be better
to just go directly to the correct location.

Closes #6591
2014-03-07 08:40:32 +01:00
Misha Moroshko 186a68f8ff docs(guide/services): minor fixes 2014-03-06 15:38:59 -08:00
Ben Lesh 46bd6dc88d feat(input): support types date, time, datetime-local, month, week
On older browser that don't support the new HTML5 inputs
and display a text input instead, the user is required to enter
the data in the corresponding ISO format. The value in `ng-model`
will always be a date.

E2e tests contain a workaround to a bug in webdriver,
see https://github.com/angular/protractor/issues/562.

Also adds weeks as format to the `dateFilter`.

Related to #757.
Closes #5864.
2014-03-06 12:21:15 -08:00
Sekib Omazic 0609453e1f fix(style): expressions in style tags
Enable data-binding for style tags.

Note: this feature does not work on IE8.

Closes #2387
Closes #6492
2014-03-06 02:19:30 -08:00
Igor Minar 7682e5747a chore(build): don't instruct Jenkins test on IE
for an unknown reason the VMs can't connect to local karma, so all builds on Jenkins (ci.angularjs.org)
are failing right now.

Since we want to kill Jenkins anyway, and travis tests on IE, this should not have any
significant impact on us.
2014-03-06 02:15:24 -08:00
Igor Minar eaa1d00b24 chore(build): remove IE8 target from all test configs
BREAKING CHANGE: As communicated before, IE8 is no longer supported.

more info: http://blog.angularjs.org/2013/12/angularjs-13-new-release-approaches.html
2014-03-06 01:53:17 -08:00
Chirayu Krishnappa 3cf2da0e38 chore(publish.sh): publish to all serving backends 2014-03-05 21:49:28 -08:00
Timothée Jeannin 9335378602 style: enable jscs requireLeftStickedOperators rule
Closed #6544.
2014-03-05 16:30:51 -08:00
Tony Bergeron de2ecb8a96 docs(directive.ngdoc): typo fix 2014-03-05 16:15:52 -08:00
tpiere 66fdc03642 docs(tutorial): update step_09.ngdoc
Closes #5991
2014-03-05 22:49:06 +01:00
Zak Johnson 8e2e9adb46 docs(guide/services): clean up typos 2014-03-05 13:29:24 -08:00
mgerstenblatt 7d604975a7 docs(guide/forms): fix a typo
Closes #6556
2014-03-05 22:10:57 +01:00
Chung-Min Cheng 02075dcf13 docs(tutorial): update step_08.ngdoc
Closes #6537
2014-03-05 21:48:22 +01:00
Sharon DiOrio 7c73bc916e docs(tutorial/index): improve accessibility
- Adds accessibility attributes to links and images.
- Adds a note on using NVM for node.
2014-03-05 12:22:53 -08:00
Takashi Nakagawa 2036fb1e71 chore(grunt): remove unnecessary white spaces 2014-03-05 12:08:43 -08:00
Brian Ford d07101dec0 docs(guide/module): improve clarity 2014-03-03 12:52:03 -08:00
Brian Ford 2206b99359 docs(guide/di): fix formatting and improve clarity 2014-03-03 12:50:41 -08:00
Brian Ford 989f6f2056 docs(guide/databinding): reword for clarity 2014-03-03 12:43:41 -08:00
Brian Ford 4cb5113546 docs(guide/controller): reorganize, add Controller suffix, formatting 2014-03-03 12:38:26 -08:00
Brian Ford 66c14ce84a docs(guide/concepts): fix links 2014-03-03 12:36:30 -08:00
Brian Ford d224f03ef4 docs(guide/introduction): reword for clarity 2014-03-03 12:35:49 -08:00
Brian Ford fde61423cf docs(guide/unit-testing): rename and fix formatting 2014-03-03 12:35:00 -08:00
Brian Ford c29d21f4cd docs(guide/e2e-testing): rename and fix formatting 2014-03-03 12:33:56 -08:00
Brian Ford 7a19a80af2 docs(guide/css-styling): rename and fix formatting 2014-03-03 12:32:51 -08:00
Brian Ford c7e42223a2 docs(guide/bootstrap): fix formatting 2014-03-03 12:31:51 -08:00
Brian Ford 220e7bf2d4 docs(guide/services): rewrite services documentation 2014-03-03 12:30:33 -08:00
Brian Ford 8d6eed21d2 docs(guide/templates): clarity 2014-03-02 23:22:26 -08:00
Brian Ford 7c34e1f1a7 docs(guide/expression): clarify 2014-03-02 22:31:34 -08:00
Ole Michaelis bf82c8a708 docs(loader): remove duplicate "the"
Typo fixup

Closes #6521
2014-03-02 19:25:19 -05:00
Gronblom Sam d5f2084883 docs(ngView): rename controller suffix in ngView example
- According to
  https://github.com/angular/angular.js/blob/5bf81bc111a866ec65ef86c01336911e577df5df/docs/content/guide/controller.ngdoc#L166
  Ctrl should be the suffix for a controller

Closes #5817
2014-03-02 22:37:29 +01:00
MikeMcElroy 9bffccd935 docs($interval): cancel() takes a promise not a number
Closes #6485
2014-03-02 16:07:44 +00:00
amagee 6b18a564dd docs(error/ng/btstrpd): note that loading angular.js twice can cause the error
Closes #6502
2014-03-02 15:56:13 +00:00
Peter Bacon Darwin 27b7fa3914 docs(directives): ensure that the back-to-top directive resets location
Closes #6512
Closes #6499
2014-03-02 15:38:04 +00:00
Peter Bacon Darwin d7ecab775c chore(package.json): update dgeni dependencies 2014-03-02 15:02:29 +00:00
Peter Bacon Darwin e89139dc9d docs(*): ensure jsdoc type expressions are valid 2014-03-02 15:02:29 +00:00
Vlad GURDIGA 26169075c8 docs(guide/injecting-services): fix list formatting in Related* sections
Closes #5447
2014-03-02 15:03:11 +01:00
Paolo Moretti bf01ab2a13 docs(loader): change 'MyModule' -> 'myModule'
Closes #5393
2014-03-02 14:18:42 +01:00
thenickcox 3248233f5e docs(ngMock): fixes in flush() documentation
The docs for the `flush()` method contained a few grammatical
errors and were awkwardly worded. Change the explanation of
the method to remove errors and read more naturally.

Closes #4886
2014-03-02 13:55:51 +01:00
jenkins 3a8bbb721d chore(release): update cdn version 2014-03-01 10:43:16 -08:00
245 changed files with 14852 additions and 5857 deletions
+2 -1
View File
@@ -1,5 +1,6 @@
{
"disallowKeywords": ["with"],
"disallowTrailingWhitespace": true,
"requireRightStickedOperators": ["!"]
"requireRightStickedOperators": ["!"],
"requireLeftStickedOperators": [","]
}
-1
View File
@@ -8,7 +8,6 @@
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
"disallowLeftStickedOperators": ["?", "+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
"disallowRightStickedOperators": ["?", "+", "/", "*", ":", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
"requireLeftStickedOperators": [","],
"disallowImplicitTypeConversion": ["string"],
"disallowMultipleLineBreaks": true,
"disallowKeywordsOnNewLine": ["else"],
+4
View File
@@ -18,6 +18,10 @@ env:
- LOGS_DIR=/tmp/angular-build/logs
- BROWSER_PROVIDER_READY_FILE=/tmp/sauce-connect-ready
install:
- npm config set registry http://23.251.144.68
- npm install
before_script:
- mkdir -p $LOGS_DIR
- ./lib/sauce/sauce_connect_setup.sh
+693 -15
View File
@@ -1,3 +1,677 @@
<a name="1.3.0-beta.9"></a>
# 1.3.0-beta.9 release-naming (2014-05-16)
## Bug Fixes
- **$compile:** pass `transcludeFn` down to nested transclude directives
([4f03dc5a](https://github.com/angular/angular.js/commit/4f03dc5a9650f3f22f78b438474322b4b8871dec),
[#7240](https://github.com/angular/angular.js/issues/7240), [#7387](https://github.com/angular/angular.js/issues/7387))
- **jqLite:** use jQuery only if jQuery.fn.on present
([e9bc51cb](https://github.com/angular/angular.js/commit/e9bc51cb0964ea682c1654919174dacebd09fcf6))
- **ngClass:** handle index changes when an item is unshifted
([5fbd618c](https://github.com/angular/angular.js/commit/5fbd618c2ff0dbaa4e19d0fd0e55921ce7d89478),
[#7256](https://github.com/angular/angular.js/issues/7256))
- **ngMessages:** annotate ngMessages controller for minification
([0282ca97](https://github.com/angular/angular.js/commit/0282ca971df7923c8f3dba0eb0df544e244e5b93))
- **numberFilter:** fix rounding error edge case
([81d427b5](https://github.com/angular/angular.js/commit/81d427b5f0d3502f65e8db5beaa5ad837c9ede17),
[#7453](https://github.com/angular/angular.js/issues/7453), [#7478](https://github.com/angular/angular.js/issues/7478))
## Features
- **ngTouch:** add optional `ngSwipeDisableMouse` attribute to `ngSwipe` directives to ignore mouse events.
([5a568b4f](https://github.com/angular/angular.js/commit/5a568b4f960cc5381b3911e3a6423aff2ff7f7f9),
[#6627](https://github.com/angular/angular.js/issues/6627), [#6626](https://github.com/angular/angular.js/issues/6626))
## Breaking Changes
- **jqLite:** due to [d71dbb1a](https://github.com/angular/angular.js/commit/d71dbb1ae50f174680533492ce4c7db3ff74df00),
the jQuery `detach()` method does not trigger the `$destroy` event.
If you want to destroy Angular data attached to the element, use `remove()`.
<a name="1.3.0-beta.8"></a>
# 1.3.0-beta.8 accidental-haiku (2014-05-09)
## Bug Fixes
- **$compile:** set $isolateScope correctly for sync template directives
([562c4e42](https://github.com/angular/angular.js/commit/562c4e424b0ed5f8d4bffba0cd18e66db2059043),
[#6942](https://github.com/angular/angular.js/issues/6942))
- **$httpBackend:** Add missing expectHEAD() method
([e1d61784](https://github.com/angular/angular.js/commit/e1d6178457045e721872022f71227b277cb88726),
[#7320](https://github.com/angular/angular.js/issues/7320))
- **$interpolate:** don't ReferenceError when context is undefined
([924ee6db](https://github.com/angular/angular.js/commit/924ee6db06a2518224caada86769efedd21c0710),
[#7230](https://github.com/angular/angular.js/issues/7230), [#7237](https://github.com/angular/angular.js/issues/7237))
- **grunt-utils:** ensure special inline CSS works when `angular` is not a global
([af72f40a](https://github.com/angular/angular.js/commit/af72f40a5512daa97c1f175a59b547c33cff1dc0),
[#7176](https://github.com/angular/angular.js/issues/7176))
- **injector:** invoke config blocks for module after all providers
([c0b4e2db](https://github.com/angular/angular.js/commit/c0b4e2db9cbc8bc3164cedc4646145d3ab72536e),
[#7139](https://github.com/angular/angular.js/issues/7139), [#7147](https://github.com/angular/angular.js/issues/7147))
- **ngModelOptions:**
- enable overriding the default with a debounce of zero
([c56e32a7](https://github.com/angular/angular.js/commit/c56e32a7fa44e2edd2c70f663906720c7c9ad898),
[#7205](https://github.com/angular/angular.js/issues/7205))
- initialize ngModelOptions in prelink
([fbf5ab8f](https://github.com/angular/angular.js/commit/fbf5ab8f17d28efeadb492c5a252f0778643f072),
[#7281](https://github.com/angular/angular.js/issues/7281), [#7292](https://github.com/angular/angular.js/issues/7292))
- **ngSanitize:** encode surrogate pair properly
([627b0354](https://github.com/angular/angular.js/commit/627b0354ec35bef5c6dbfab6469168c2fadcbee5),
[#5088](https://github.com/angular/angular.js/issues/5088), [#6911](https://github.com/angular/angular.js/issues/6911))
- **ngSrc, ngSrcset:** only interpolate if all expressions are defined
([8d180383](https://github.com/angular/angular.js/commit/8d180383014cbe38d58ff3eab083f51cfcfb8dde),
[#6984](https://github.com/angular/angular.js/issues/6984))
- **ngSwitch:** properly support case labels with different numbers of transclude fns
([ac37915e](https://github.com/angular/angular.js/commit/ac37915ef64c60ec8f8d4e49e4d61d7baeb96ba0),
[#7372](https://github.com/angular/angular.js/issues/7372), [#7373](https://github.com/angular/angular.js/issues/7373))
## Features
- **$compile:** allow SVG and MathML templates via special `type` property
([f0e12ea7](https://github.com/angular/angular.js/commit/f0e12ea7fea853192e4eead00b40d6041c5f914a),
[#7265](https://github.com/angular/angular.js/issues/7265))
- **$interpolate:** add optional allOrNothing param
([c2362e3f](https://github.com/angular/angular.js/commit/c2362e3f45e732a9defdb0ea59ce4ec5236fcd3a))
- **FormController:** commit `$viewValue` of all child controls when form is submitted
([a0ae07bd](https://github.com/angular/angular.js/commit/a0ae07bd4ee8d98654df4eb261d16ca55884e374),
[#7017](https://github.com/angular/angular.js/issues/7017))
- **NgMessages:** introduce the NgMessages module and directives
([0f4016c8](https://github.com/angular/angular.js/commit/0f4016c84a47e01a0fb993867dfd0a64828c089c))
## Breaking Changes
- **$http:** due to [ad4336f9](https://github.com/angular/angular.js/commit/ad4336f9359a073e272930f8f9bcd36587a8648f),
Previously, it was possible to register a response interceptor like so:
```js
// register the interceptor as a service
$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
return function(promise) {
return promise.then(function(response) {
// do something on success
return response;
}, function(response) {
// do something on error
if (canRecover(response)) {
return responseOrNewPromise
}
return $q.reject(response);
});
}
});
$httpProvider.responseInterceptors.push('myHttpInterceptor');
```
Now, one must use the newer API introduced in v1.1.4 (4ae46814), like so:
```js
$provide.factory('myHttpInterceptor', function($q) {
return {
response: function(response) {
// do something on success
return response;
},
responseError: function(response) {
// do something on error
if (canRecover(response)) {
return responseOrNewPromise
}
return $q.reject(response);
}
};
});
$httpProvider.interceptors.push('myHttpInterceptor');
```
More details on the new interceptors API (which has been around as of v1.1.4) can be found at
https://docs.angularjs.org/api/ng/service/$http#interceptors
- **injector:** due to [c0b4e2db](https://github.com/angular/angular.js/commit/c0b4e2db9cbc8bc3164cedc4646145d3ab72536e),
Previously, config blocks would be able to control behaviour of provider registration, due to being
invoked prior to provider registration. Now, provider registration always occurs prior to configuration
for a given module, and therefore config blocks are not able to have any control over a providers
registration.
**Example**:
Previously, the following:
```js
angular.module('foo', [])
.provider('$rootProvider', function() {
this.$get = function() { ... }
})
.config(function($rootProvider) {
$rootProvider.dependentMode = "B";
})
.provider('$dependentProvider', function($rootProvider) {
if ($rootProvider.dependentMode === "A") {
this.$get = function() {
// Special mode!
}
} else {
this.$get = function() {
// something else
}
}
});
```
would have "worked", meaning behaviour of the config block between the registration of "$rootProvider"
and "$dependentProvider" would have actually accomplished something and changed the behaviour of the
app. This is no longer possible within a single module.
- **ngModelOptions:** due to [adfc322b](https://github.com/angular/angular.js/commit/adfc322b04a58158fb9697e5b99aab9ca63c80bb),
This commit changes the API on `NgModelController`, both semantically and
in terms of adding and renaming methods.
* `$setViewValue(value)` -
This method still changes the `$viewValue` but does not immediately commit this
change through to the `$modelValue` as it did previously.
Now the value is committed only when a trigger specified in an associated
`ngModelOptions` directive occurs. If `ngModelOptions` also has a `debounce` delay
specified for the trigger then the change will also be debounced before being
committed.
In most cases this should not have a significant impact on how `NgModelController`
is used: If `updateOn` includes `default` then `$setViewValue` will trigger
a (potentially debounced) commit immediately.
* `$cancelUpdate()` - is renamed to `$rollbackViewValue()` and has the same meaning,
which is to revert the current `$viewValue` back to the `$lastCommittedViewValue`,
to cancel any pending debounced updates and to re-render the input.
To migrate code that used `$cancelUpdate()` follow the example below:
Before:
```js
$scope.resetWithCancel = function (e) {
if (e.keyCode == 27) {
$scope.myForm.myInput1.$cancelUpdate();
$scope.myValue = '';
}
};
```
After:
```js
$scope.resetWithCancel = function (e) {
if (e.keyCode == 27) {
$scope.myForm.myInput1.$rollbackViewValue();
$scope.myValue = '';
}
}
```
<a name="v1.3.0-beta.7"></a>
# v1.3.0-beta.7 proper-attribution (2014-04-25)
## Bug Fixes
- **$location:** don't clobber path during parsing of path
([498835a1](https://github.com/angular/angular.js/commit/498835a1c4d0dc6397df4dd667796b09565fedf4),
[#7199](https://github.com/angular/angular.js/issues/7199))
## Performance Improvements
- **scope:** ~10x speedup from sharing the child scope class.
([8377e818](https://github.com/angular/angular.js/commit/8377e81827a840b9eb64f119de4bcbaba0ceb3be))
<a name="v1.3.0-beta.6"></a>
# v1.3.0-beta.6 expedient-caffeination (2014-04-21)
## Bug Fixes
- **$animate:** ensure class-based animations always perform a domOperation if skipped
([708f2ba9](https://github.com/angular/angular.js/commit/708f2ba9843b665e417b93c7df907194565db991),
[#6957](https://github.com/angular/angular.js/issues/6957))
- **$compile:**
- reference correct directive name in ctreq error
([1192531e](https://github.com/angular/angular.js/commit/1192531e9b48cd90cbb601b0c0fdeb12340c1885),
[#7062](https://github.com/angular/angular.js/issues/7062), [#7067](https://github.com/angular/angular.js/issues/7067))
- fix regression which affected old jQuery releases
([ef64169d](https://github.com/angular/angular.js/commit/ef64169db32ffdf5e0e3ae2154ac434c6a55378b))
- **$location:**
- fix and test html5Mode url-parsing algorithm for legacy browsers
([49e7c32b](https://github.com/angular/angular.js/commit/49e7c32bb45ce3984df6768ba7b2f6a723a4ebe7))
- make legacy browsers behave like modern ones in html5Mode
([3f047704](https://github.com/angular/angular.js/commit/3f047704c70a957596371fec554d3e1fb066a29d),
[#6162](https://github.com/angular/angular.js/issues/6162), [#6421](https://github.com/angular/angular.js/issues/6421), [#6899](https://github.com/angular/angular.js/issues/6899), [#6832](https://github.com/angular/angular.js/issues/6832), [#6834](https://github.com/angular/angular.js/issues/6834))
- **input:** don't dirty model when input event triggered due to placeholder change
([ff428e72](https://github.com/angular/angular.js/commit/ff428e72837c85b9540ee9e5a3daa2c9477c90bb),
[#2614](https://github.com/angular/angular.js/issues/2614), [#5960](https://github.com/angular/angular.js/issues/5960))
- **limitTo:** do not convert Infinity to NaN
([5dee9e4a](https://github.com/angular/angular.js/commit/5dee9e4a33ab2a0be6d8a8099297be3028771e0b),
[#6771](https://github.com/angular/angular.js/issues/6771), [#7118](https://github.com/angular/angular.js/issues/7118))
- **ngModelController:** introduce $cancelUpdate to cancel pending updates
([940fcb40](https://github.com/angular/angular.js/commit/940fcb4090e96824a4abc50252aa36aaf239e937),
[#6994](https://github.com/angular/angular.js/issues/6994), [#7014](https://github.com/angular/angular.js/issues/7014))
## Features
- **$resource:** Make stripping of trailing slashes configurable.
([3878be52](https://github.com/angular/angular.js/commit/3878be52f6d95fca4c386d4a5523f3c8fcb04270))
- **Scope:** add `$watchGroup` method for observing a set of expressions
([21f93163](https://github.com/angular/angular.js/commit/21f93163384f36fc4ae0934387339380e3dc3e9c))
- **injector:** "strict-DI" mode which disables "automatic" function annotation
([4b1695ec](https://github.com/angular/angular.js/commit/4b1695ec61aac8de7fcac1dfe8b4b420f9842c38),
[#6719](https://github.com/angular/angular.js/issues/6719), [#6717](https://github.com/angular/angular.js/issues/6717), [#4504](https://github.com/angular/angular.js/issues/4504), [#6069](https://github.com/angular/angular.js/issues/6069), [#3611](https://github.com/angular/angular.js/issues/3611))
- **ngModelOptions:** custom triggers and debounce of ngModel updates
([dbe381f2](https://github.com/angular/angular.js/commit/dbe381f29fc72490f8e3a5328d5c487b185fe652),
[#1285](https://github.com/angular/angular.js/issues/1285))
## Performance Improvements
- **$compile:** watch interpolated expressions individually
([0ebfa0d1](https://github.com/angular/angular.js/commit/0ebfa0d112c8ba42242cb8353db91e93eb42b463))
- **$interpolate:** speed up interpolation by recreating watchGroup approach
([546cb429](https://github.com/angular/angular.js/commit/546cb429d9cea25a9bdadbb87dfd401366b0b908))
## Breaking Changes
- **$interpolate:** due to [88c2193c](https://github.com/angular/angular.js/commit/88c2193c71954b9e7e7e4bdf636a2b168d36300d),
the function returned by `$interpolate`
no longer has a `.parts` array set on it.
Instead it has two arrays:
* `.expressions`, an array of the expressions in the
interpolated text. The expressions are parsed with
`$parse`, with an extra layer converting them to strings
when computed
* `.separators`, an array of strings representing the
separations between interpolations in the text.
This array is **always** 1 item longer than the
`.expressions` array for easy merging with it
<a name="1.3.0-beta.5"></a>
# 1.3.0-beta.5 chimeric-glitterfication (2014-04-03)
## Bug Fixes
- **$animate:**
- insert elements at the start of the parent container instead of at the end
([1cb8584e](https://github.com/angular/angular.js/commit/1cb8584e8490ecdb1b410a8846c4478c6c2c0e53),
[#4934](https://github.com/angular/angular.js/issues/4934), [#6275](https://github.com/angular/angular.js/issues/6275))
- ensure the CSS driver properly works with SVG elements
([c67bd69c](https://github.com/angular/angular.js/commit/c67bd69c58812da82b1a3a31d430df7aad8a50a8),
[#6030](https://github.com/angular/angular.js/issues/6030))
- **$parse:** mark constant unary minus expressions as constant
([7914d346](https://github.com/angular/angular.js/commit/7914d3463b5ec560c616a0c9fd008bc0e3f7c786),
[#6932](https://github.com/angular/angular.js/issues/6932))
- **Scope:**
- revert the `__proto__` cleanup as that could cause regressions
([71c11e96](https://github.com/angular/angular.js/commit/71c11e96c64d5d4eb71f48c1eb778c2ba5c63377))
- more scope clean up on $destroy to minimize leaks
([d64d41ed](https://github.com/angular/angular.js/commit/d64d41ed992430a4fc89cd415c03acf8d56022e6),
[#6794](https://github.com/angular/angular.js/issues/6794), [#6856](https://github.com/angular/angular.js/issues/6856), [#6968](https://github.com/angular/angular.js/issues/6968))
- **ngClass:** handle ngClassOdd/Even affecting the same classes
([c9677920](https://github.com/angular/angular.js/commit/c9677920d462046710fc72ca422ab7400f551d2e),
[#5271](https://github.com/angular/angular.js/issues/5271))
## Breaking Changes
- **$animate:** due to [1cb8584e](https://github.com/angular/angular.js/commit/1cb8584e8490ecdb1b410a8846c4478c6c2c0e53),
`$animate` will no longer default the after parameter to the last element of the parent
container. Instead, when after is not specified, the new element will be inserted as the
first child of the parent container.
To update existing code, change all instances of `$animate.enter()` or `$animate.move()` from:
`$animate.enter(element, parent);`
to:
`$animate.enter(element, parent, angular.element(parent[0].lastChild));`
<a name="1.2.16"></a>
# 1.2.16 badger-enumeration (2014-04-03)
## Bug Fixes
- **$animate:**
- ensure the CSS driver properly works with SVG elements
([38ea5426](https://github.com/angular/angular.js/commit/38ea542662b2b74703d583e3a637d65369fc26eb),
[#6030](https://github.com/angular/angular.js/issues/6030))
- prevent cancellation timestamp from being too far in the future
([35d635cb](https://github.com/angular/angular.js/commit/35d635cbcbdc20f304781655f3563111afa6567f),
[#6748](https://github.com/angular/angular.js/issues/6748))
- run CSS animations before JS animations to avoid style inheritance
([0e5106ec](https://github.com/angular/angular.js/commit/0e5106ec2ccc8596c589b89074d3b27d27bf395a),
[#6675](https://github.com/angular/angular.js/issues/6675))
- **$parse:** mark constant unary minus expressions as constant
([6e420ff2](https://github.com/angular/angular.js/commit/6e420ff28d9b3e76ac2c3598bf3797540ef8a1d3),
[#6932](https://github.com/angular/angular.js/issues/6932))
- **Scope:**
- revert the __proto__ cleanup as that could cause regressions
([2db66f5b](https://github.com/angular/angular.js/commit/2db66f5b695a06cff62a52e55e55d1a0a25eec2f))
- more scope clean up on $destroy to minimize leaks
([7e4e696e](https://github.com/angular/angular.js/commit/7e4e696ec3adf9d6fc77a7aa7e0909a9675fd43a),
[#6794](https://github.com/angular/angular.js/issues/6794), [#6856](https://github.com/angular/angular.js/issues/6856), [#6968](https://github.com/angular/angular.js/issues/6968))
- aggressively clean up scope on $destroy to minimize leaks
([8d4d437e](https://github.com/angular/angular.js/commit/8d4d437e8cd8d7cebab5d9ae5c8bcfeef2118ce9),
[#6794](https://github.com/angular/angular.js/issues/6794), [#6856](https://github.com/angular/angular.js/issues/6856))
- **filter.ngdoc:** Check if "input" variable is defined
([a275d539](https://github.com/angular/angular.js/commit/a275d539f9631d6ec64d03814b3b09420e6cf1ee),
[#6819](https://github.com/angular/angular.js/issues/6819))
- **input:** don't perform HTML5 validation on updated model-value
([b2363e31](https://github.com/angular/angular.js/commit/b2363e31023df8240113f68b4e01d942f8009b60),
[#6796](https://github.com/angular/angular.js/issues/6796), [#6806](https://github.com/angular/angular.js/issues/6806))
- **ngClass:** handle ngClassOdd/Even affecting the same classes
([55fe6d63](https://github.com/angular/angular.js/commit/55fe6d6331e501325c2658df8995dcc083fc4ffb),
[#5271](https://github.com/angular/angular.js/issues/5271))
## Features
- **$http:** add xhr statusText to completeRequest callback
([32c09c1d](https://github.com/angular/angular.js/commit/32c09c1d195fcb98f6e29fc7e554a867f4762301),
[#2335](https://github.com/angular/angular.js/issues/2335), [#2665](https://github.com/angular/angular.js/issues/2665), [#6713](https://github.com/angular/angular.js/issues/6713))
<a name="1.3.0-beta.4"></a>
# 1.3.0-beta.4 inconspicuous-deception (2014-03-28)
## Bug Fixes
- **$animate:**
- prevent cancellation timestamp from being too far in the future
([ff5cf736](https://github.com/angular/angular.js/commit/ff5cf736e5b8073c8121295743873ccd04cc7d6b),
[#6748](https://github.com/angular/angular.js/issues/6748))
- make CSS blocking optional for class-based animations
([1bebe36a](https://github.com/angular/angular.js/commit/1bebe36aa938890d61188762ed618b1b5e193634),
[#6674](https://github.com/angular/angular.js/issues/6674), [#6739](https://github.com/angular/angular.js/issues/6739))
- run CSS animations before JS animations to avoid style inheritance
([2317af68](https://github.com/angular/angular.js/commit/2317af68510fe3b67526282dad697ad4dc621a19),
[#6675](https://github.com/angular/angular.js/issues/6675))
- **Scope:** aggressively clean up scope on $destroy to minimize leaks
([f552f251](https://github.com/angular/angular.js/commit/f552f25171390e726ad7246ed18b994970bcf764),
[#6794](https://github.com/angular/angular.js/issues/6794), [#6856](https://github.com/angular/angular.js/issues/6856))
- **doc-gen:** Run Gulp on Windows too
([47ba6014](https://github.com/angular/angular.js/commit/47ba60146032c0bfadeaa9f3816644b31fc33315),
[#6346](https://github.com/angular/angular.js/issues/6346))
- **filter.ngdoc:** Check if "input" variable is defined
([4a6d4de5](https://github.com/angular/angular.js/commit/4a6d4de53ed1472c0cb2323292127495619d7ed9),
[#6819](https://github.com/angular/angular.js/issues/6819))
- **input:** don't perform HTML5 validation on updated model-value
([b472d027](https://github.com/angular/angular.js/commit/b472d0275f2900beba3b1f2fcee821369f8c15c1),
[#6796](https://github.com/angular/angular.js/issues/6796), [#6806](https://github.com/angular/angular.js/issues/6806))
## Features
- **$http:** add xhr statusText to completeRequest callback
([1d2414ca](https://github.com/angular/angular.js/commit/1d2414ca93a0340840ea1e80c48edb51ec55cd48),
[#2335](https://github.com/angular/angular.js/issues/2335), [#2665](https://github.com/angular/angular.js/issues/2665), [#6713](https://github.com/angular/angular.js/issues/6713))
## Breaking Changes
- **$animate:** due to [1bebe36a](https://github.com/angular/angular.js/commit/1bebe36aa938890d61188762ed618b1b5e193634),
Any class-based animation code that makes use of transitions
and uses the setup CSS classes (such as class-add and class-remove) must now
provide a empty transition value to ensure that its styling is applied right
away. In other words if your animation code is expecting any styling to be
applied that is defined in the setup class then it will not be applied
"instantly" unless a `transition:0s none` value is present in the styling
for that CSS class. This situation is only the case if a transition is already
present on the base CSS class once the animation kicks off.
Before:
.animated.my-class-add {
opacity:0;
transition:0.5s linear all;
}
.animated.my-class-add.my-class-add-active {
opacity:1;
}
After:
.animated.my-class-add {
transition:0s linear all;
opacity:0;
}
.animated.my-class-add.my-class-add-active {
transition:0.5s linear all;
opacity:1;
}
Please view the documentation for ngAnimate for more info.
<a name="1.3.0-beta.3"></a>
# 1.3.0-beta.3 emotional-waffles (2014-03-21)
## Bug Fixes
- **ngAnimate:** support `webkitCancelRequestAnimationFrame` in addition to `webkitCancelAnimationFrame`
([c839f78b](https://github.com/angular/angular.js/commit/c839f78b8f2d8d910bc2bfc9e41b3e3b67090ec1),
[#6526](https://github.com/angular/angular.js/issues/6526))
- **$http:** allow sending Blob data using `$http`
([b8cc71d4](https://github.com/angular/angular.js/commit/b8cc71d476f76ff51e719fb76fb2348027c858ce),
[#5012](https://github.com/angular/angular.js/issues/5012))
- **$httpBackend:** don't error when JSONP callback is called with no parameter
([6680b7b9](https://github.com/angular/angular.js/commit/6680b7b97c0326a80bdccaf0a35031e4af641e0e),
[#4987](https://github.com/angular/angular.js/issues/4987), [#6735](https://github.com/angular/angular.js/issues/6735))
- **$rootScope:** ng-repeat can't handle `NaN` values. #4605
([fb6062fb](https://github.com/angular/angular.js/commit/fb6062fb9d83545730b993e94ac7482ffd43a62c),
[#4605](https://github.com/angular/angular.js/issues/4605))
- **$rootScope:** `$watchCollection` should call listener with old value
([78057a94](https://github.com/angular/angular.js/commit/78057a945ef84cbb05f9417fe884cb8c28e67b44),
[#2621](https://github.com/angular/angular.js/issues/2621), [#5661](https://github.com/angular/angular.js/issues/5661), [#5688](https://github.com/angular/angular.js/issues/5688), [#6736](https://github.com/angular/angular.js/issues/6736))
- **angular.bootstrap:** allow angular to load only once
([748a6c8d](https://github.com/angular/angular.js/commit/748a6c8d9d8d61c3ee18eec462abe8ff245d6a98),
[#5863](https://github.com/angular/angular.js/issues/5863), [#5587](https://github.com/angular/angular.js/issues/5587))
- **jqLite:** `inheritedData()` now traverses Shadow DOM boundaries via the `host` property of `DocumentFragment`
([8a96f317](https://github.com/angular/angular.js/commit/8a96f317e594a5096d4fa56ceae4c685eec8ac8b),
[#6637](https://github.com/angular/angular.js/issues/6637))
- **ngCookie:** convert non-string values to string
([36528310](https://github.com/angular/angular.js/commit/3652831084c3788f786046b907a7361d2e89c520),
[#6151](https://github.com/angular/angular.js/issues/6151), [#6220](https://github.com/angular/angular.js/issues/6220))
- **ngTouch:** update workaround for Webkit quirk
([bc42950b](https://github.com/angular/angular.js/commit/bc42950b514b60f319812eeb87aae2915e394237),
[#6302](https://github.com/angular/angular.js/issues/6302))
- **orderBy:** support string predicates containing non-ident characters
([37bc5ef4](https://github.com/angular/angular.js/commit/37bc5ef4d87f19da47d3ab454c43d1e532c4f924),
[#6143](https://github.com/angular/angular.js/issues/6143), [#6144](https://github.com/angular/angular.js/issues/6144))
- **select:** avoid checking option element's `selected` property in render
([f40f54c6](https://github.com/angular/angular.js/commit/f40f54c6da4a5399fe18a89d068634bb491e9f1a),
[#2448](https://github.com/angular/angular.js/issues/2448), [#5994](https://github.com/angular/angular.js/issues/5994))
## Features
- **$compile:** add support for `$observer` deregistration
([299b220f](https://github.com/angular/angular.js/commit/299b220f5e05e1d4e26bfd58d0b2fd7329ca76b1),
[#5609](https://github.com/angular/angular.js/issues/5609))
- **ngMock.$httpBackend:** added support for function as URL matcher
([d6cfcace](https://github.com/angular/angular.js/commit/d6cfcacee101f2738e0a224a3377232ff85f78a4),
[#4580](https://github.com/angular/angular.js/issues/4580))
## Breaking Changes
- **$compile:** due to [299b220f](https://github.com/angular/angular.js/commit/299b220f5e05e1d4e26bfd58d0b2fd7329ca76b1),
calling `attr.$observe` no longer returns the observer function, but a
deregistration function instead. To migrate the code follow the example below:
Before:
directive('directiveName', function() {
return {
link: function(scope, elm, attr) {
var observer = attr.$observe('someAttr', function(value) {
console.log(value);
});
}
};
});
After:
directive('directiveName', function() {
return {
link: function(scope, elm, attr) {
var observer = function(value) {
console.log(value);
};
attr.$observe('someAttr', observer);
}
};
});
- **$httpBackend:** due to [6680b7b9](https://github.com/angular/angular.js/commit/6680b7b97c0326a80bdccaf0a35031e4af641e0e), the JSONP behavior for erroneous and empty responses changed:
Previously, a JSONP response was regarded as erroneous if it was empty. Now Angular is listening to the
correct events to detect errors, i.e. even empty responses can be successful.
<a name="v1.2.15"></a>
# v1.2.15 beer-underestimating (2014-03-21)
## Bug Fixes
- **$$RAFProvider:** check for webkitCancelRequestAnimationFrame
([e84da228](https://github.com/angular/angular.js/commit/e84da2283c4e195be557f7b06c8783fe502acbbb),
[#6526](https://github.com/angular/angular.js/issues/6526))
- **$$rAF:** always fallback to a $timeout incase native rAF isn't supported
([ee8e4a94](https://github.com/angular/angular.js/commit/ee8e4a946ed8f943e00846b88d8d51c0b2cd1fab),
[#6654](https://github.com/angular/angular.js/issues/6654))
- **$compile:** support templates with thead and tfoot root elements
([ca0ac649](https://github.com/angular/angular.js/commit/ca0ac649971ae4fb50419b38f92a98d2226eb696),
[#6289](https://github.com/angular/angular.js/issues/6289))
- **$http:**
- allow sending Blob data using $http
([fbb125a3](https://github.com/angular/angular.js/commit/fbb125a3af164e52af2f8119175b04cbbed2f331),
[#5012](https://github.com/angular/angular.js/issues/5012))
- don't covert 0 status codes to 404 for non-file protocols
([f108a2a9](https://github.com/angular/angular.js/commit/f108a2a994149ecc011e29f327bcb8e11adf72d9),
[#6074](https://github.com/angular/angular.js/issues/6074), [#6155](https://github.com/angular/angular.js/issues/6155))
- **$rootScope:**
- ng-repeat can't handle NaN values. #4605
([e48c28fe](https://github.com/angular/angular.js/commit/e48c28fe9292efe7af6205b2be116d2350990c73),
[#4605](https://github.com/angular/angular.js/issues/4605))
- $watchCollection should call listener with oldValue
([3dd95727](https://github.com/angular/angular.js/commit/3dd9572754c7bafec30dd625f5c611346959c969),
[#2621](https://github.com/angular/angular.js/issues/2621), [#5661](https://github.com/angular/angular.js/issues/5661), [#5688](https://github.com/angular/angular.js/issues/5688), [#6736](https://github.com/angular/angular.js/issues/6736))
- **angular.bootstrap:** only allow angular to load once
([0d60f8d3](https://github.com/angular/angular.js/commit/0d60f8d367e38224696749b0f7de04bd60649815),
[#5863](https://github.com/angular/angular.js/issues/5863), [#5587](https://github.com/angular/angular.js/issues/5587))
- **jqLite:** traverse `host` property for DocumentFragment in inheritedData()
([98d825e1](https://github.com/angular/angular.js/commit/98d825e10d3bf76f47e69abba857a8933c8cb7d9),
[#6637](https://github.com/angular/angular.js/issues/6637))
- **ngAnimate:** setting classNameFilter disables animation inside ng-if
([a41a2a1d](https://github.com/angular/angular.js/commit/a41a2a1d2ce20f86ac2709592e4ada527160e580),
[#6539](https://github.com/angular/angular.js/issues/6539))
- **ngCookie:** convert non-string values to string
([93d1c95c](https://github.com/angular/angular.js/commit/93d1c95c61dbfa565333bb64527a103242175af7),
[#6151](https://github.com/angular/angular.js/issues/6151), [#6220](https://github.com/angular/angular.js/issues/6220))
- **ngTouch:** update workaround for desktop Webkit quirk
([01a34f51](https://github.com/angular/angular.js/commit/01a34f513bb567ed6d4c81d00d7c2a777c0dae01),
[#6302](https://github.com/angular/angular.js/issues/6302))
- **orderBy:** support string predicates containing non-ident characters
([10d3e1e4](https://github.com/angular/angular.js/commit/10d3e1e4472ab9f5cf4418b6438ec2e0f2b0b288),
[#6143](https://github.com/angular/angular.js/issues/6143), [#6144](https://github.com/angular/angular.js/issues/6144))
- **select:** avoid checking option element selected properties in render
([dc149de9](https://github.com/angular/angular.js/commit/dc149de9364c66b988f169f67cad39577ba43434),
[#2448](https://github.com/angular/angular.js/issues/2448), [#5994](https://github.com/angular/angular.js/issues/5994), [#6769](https://github.com/angular/angular.js/issues/6769))
<a name="1.3.0-beta.2"></a>
# 1.3.0-beta.2 silent-ventriloquism (2014-03-14)
## Bug Fixes
- **$$rAF:** always fallback to a $timeout in case native rAF isn't supported
([7b5e0199](https://github.com/angular/angular.js/commit/7b5e019981f352add88be2984de68e553d1bfa93),
[#6654](https://github.com/angular/angular.js/issues/6654))
- **$http:** don't convert 0 status codes to 404 for non-file protocols
([56e73ea3](https://github.com/angular/angular.js/commit/56e73ea355c851fdfd574d6d2a9e2fcb75677945),
[#6074](https://github.com/angular/angular.js/issues/6074), [#6155](https://github.com/angular/angular.js/issues/6155))
- **ngAnimate:** setting classNameFilter disables animation inside ng-if
([129e2e02](https://github.com/angular/angular.js/commit/129e2e021ab1d773874428cd1fb329eae72797c4),
[#6539](https://github.com/angular/angular.js/issues/6539))
## Features
- whitelist blob urls for sanitization of data-bound image urls
([47ab8df4](https://github.com/angular/angular.js/commit/47ab8df455df1f1391b760e1fbcc5c21645512b8),
[#4623](https://github.com/angular/angular.js/issues/4623))
<a name="1.3.0-beta.1"></a>
# 1.3.0-beta.1 retractable-eyebrow (2014-03-07)
## Bug Fixes
- **$compile:** support templates with thead and tfoot root elements
([53ec5e13](https://github.com/angular/angular.js/commit/53ec5e13e5955830b6751019eef232bd2125c0b6),
[#6289](https://github.com/angular/angular.js/issues/6289))
- **style:** expressions in style tags
([0609453e](https://github.com/angular/angular.js/commit/0609453e1f9ae074f8d786df903096a6eadb6aa0),
[#2387](https://github.com/angular/angular.js/issues/2387), [#6492](https://github.com/angular/angular.js/issues/6492))
## Features
- **input:** support types date, time, datetime-local, month, week
([46bd6dc8](https://github.com/angular/angular.js/commit/46bd6dc88de252886d75426efc2ce8107a5134e9),
[#5864](https://github.com/angular/angular.js/issues/5864))
## Breaking Changes
- **build:** due to [eaa1d00b](https://github.com/angular/angular.js/commit/eaa1d00b24008f590b95ad099241b4003688cdda),
As communicated before, IE8 is no longer supported.
- **input:** types date, time, datetime-local, month, week now always
require a `Date` object as model ([46bd6dc8](https://github.com/angular/angular.js/commit/46bd6dc88de252886d75426efc2ce8107a5134e9),
[#5864](https://github.com/angular/angular.js/issues/5864))
For more info: http://blog.angularjs.org/2013/12/angularjs-13-new-release-approaches.html
<a name="1.2.14"></a>
# 1.2.14 feisty-cryokinesis (2014-03-01)
@@ -275,26 +949,30 @@ The animation mock module has been renamed from `mock.animate` to `ngAnimateMock
## Breaking Changes
- **$http:** due to [e1cfb195](https://github.com/angular/angular.js/commit/e1cfb1957feaf89408bccf48fae6f529e57a82fe),
it is now necessary to separately specify default HTTP headers for PUT, POST and PATCH requests, as these no longer share a single object.
it is now necessary to seperately specify default HTTP headers for PUT, POST and PATCH requests, as these no longer share a single object.
To migrate your code, follow the example below:
To migrate your code, follow the example below:
Before:
Before:
// Will apply to POST, PUT and PATCH methods
$httpProvider.defaults.headers.post = {
"X-MY-CSRF-HEADER": "..."
};
```
// Will apply to POST, PUT and PATCH methods
$httpProvider.defaults.headers.post = {
"X-MY-CSRF-HEADER": "..."
};
```
After:
After:
// POST, PUT and PATCH default headers must be specified separately,
// as they do not share data.
$httpProvider.defaults.headers.post =
$httpProvider.defaults.headers.put =
$httpProviders.defaults.headers.patch = {
"X-MY-CSRF-HEADER": "..."
};
```
// POST, PUT and PATCH default headers must be specified seperately,
// as they do not share data.
$httpProvider.defaults.headers.post =
$httpProvider.defaults.headers.put =
$httpProviders.defaults.headers.patch = {
"X-MY-CSRF-HEADER": "..."
};
```
<a name="1.2.8"></a>
# 1.2.8 interdimensional-cartography (2014-01-10)
+42 -34
View File
@@ -23,7 +23,7 @@ discussion list or [StackOverflow][stackoverflow]. We are also available on [IRC
## <a name="issue"></a> Found an Issue?
If you find a bug in the source code or a mistake in the documentation, you can help us by
submitting and issue to our [GitHub Repository][github]. Even better you can submit a Pull Request
submitting an issue to our [GitHub Repository][github]. Even better you can submit a Pull Request
with a fix.
***Localization Issue:*** *Angular.js uses the [Google Closure I18N library], to generate its own I18N files. This means that
@@ -40,17 +40,18 @@ would like to implement a new feature then consider what kind of change it is:
[dev mailing list][angular-dev] or [IRC][irc] so that we can better coordinate our efforts, prevent
duplication of work, and help you to craft the change so that it is successfully accepted into the
project.
* **Small Changes** can be crafted and submitted to [GitHub Repository][github] as a Pull Request.
* **Small Changes** can be crafted and submitted to the [GitHub Repository][github] as a Pull Request.
## <a name="docs"></a> Want a Doc Fix?
If you want to help improve the docs, it's a good idea to let others know what you're working on to
minimize duplication of effort. Before starting, check out the issue queue for [Milestone:Docs Only](https://github.com/angular/angular.js/issues?milestone=24&state=open).
If you want to help improve the docs, it's a good idea to let others know what you're working on to
minimize duplication of effort. Before starting, check out the issue queue for
[Milestone:Docs Only](https://github.com/angular/angular.js/issues?milestone=24&state=open).
Comment on an issue to let others know what you're working on, or create a new issue if your work
doesn't fit within the scope of any of the existing doc fix projects.
For large fixes, please build and test the documentation before submitting the PR to be sure you haven't
accidentally introduced any layout or formatting issues.You should also make sure that your commit message
accidentally introduced any layout or formatting issues. You should also make sure that your commit message
is labeled "docs:" and follows the **Git Commit Guidelines** outlined below.
If you're just making a small change, don't worry about filing an issue first. Use the friendly blue "Improve this doc" button at the top right of the doc page to fork the repository in-place and make a quick change on the fly.
@@ -84,7 +85,7 @@ Before you submit your pull request consider the following guidelines:
* Search [GitHub](https://github.com/angular/angular.js/pulls) for an open or closed Pull Request
that relates to your submission. You don't want to duplicate effort.
* Please sign our [Contributor License Agreement (CLA)](#signing-the-cla) before sending pull
* Please sign our [Contributor License Agreement (CLA)](#cla) before sending pull
requests. We cannot accept code without this.
* Make your changes in a new git branch
@@ -92,16 +93,19 @@ Before you submit your pull request consider the following guidelines:
git checkout -b my-fix-branch master
```
* Create your patch, including appropriate test cases.
* Follow our [Coding Rules](#coding-rules)
* Commit your changes and create a descriptive commit message (the
commit message is used to generate release notes, please check out our
[commit message conventions](#commit-message-format) and our commit message presubmit hook
`validate-commit-msg.js`):
* Create your patch, **including appropriate test cases**.
* Follow our [Coding Rules](#rules).
* Run the full Angular test suite, as described in the [developer documentation][dev-doc],
and ensure that all tests pass.
* Commit your changes using a descriptive commit message that follows our
[commit message conventions](#commit-message-format) and passes our commit message presubmit hook
`validate-commit-msg.js`. Adherence to the [commit message conventions](#commit-message-format)
is required because release notes are automatically generated from these messages.
```shell
git commit -a
```
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
* Build your changes locally to ensure all the tests pass
@@ -109,15 +113,17 @@ Before you submit your pull request consider the following guidelines:
grunt test
```
* Push your branch to Github:
* Push your branch to GitHub:
```shell
git push origin my-fix-branch
```
* In Github, send a pull request to `angular:master`.
* If we suggest changes then you can modify your branch, rebase and force a new push to your GitHub
repository to update the Pull Request:
* In GitHub, send a pull request to `angular:master`.
* If we suggest changes then
* Make the required updates.
* Re-run the Angular test suite to ensure tests are still passing.
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
```shell
git rebase master -i
@@ -126,10 +132,12 @@ Before you submit your pull request consider the following guidelines:
That's it! Thank you for your contribution!
When the patch is reviewed and merged, you can safely delete your branch and pull the changes
#### After your pull request is merged
After your pull request is merged, you can safely delete your branch and pull the changes
from the main (upstream) repository:
* Delete the remote branch on Github:
* Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:
```shell
git push origin --delete my-fix-branch
@@ -245,24 +253,24 @@ You can find out more detailed information about contributing in the
[github]: https://github.com/angular/angular.js
[Google Closure I18N library]: https://code.google.com/p/closure-library/source/browse/closure/goog/i18n/
[list]: https://groups.google.com/forum/?fromgroups#!forum/angular
[contribute]: http://docs.angularjs.org/misc/contribute
[stackoverflow]: http://stackoverflow.com/questions/tagged/angularjs
[groups]: https://groups.google.com/forum/?fromgroups#!forum/angular
[angular-dev]: https://groups.google.com/forum/?fromgroups#!forum/angular-dev
[irc]: http://webchat.freenode.net/?channels=angularjs&uio=d4
[plunker]: http://plnkr.co/edit
[jsfiddle]: http://jsfiddle.net/
[ngDocs]: https://github.com/angular/angular.js/wiki/Writing-AngularJS-Documentation
[unit-testing]: http://docs.angularjs.org/guide/dev_guide.unit-testing
[js-style-guide]: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
[contributing]: http://docs.angularjs.org/misc/contribute
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
[github-pr-helper]: https://chrome.google.com/webstore/detail/github-pr-helper/mokbklfnaddkkbolfldepnkfmanfhpen
[coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
[contribute]: http://docs.angularjs.org/misc/contribute
[contributing]: http://docs.angularjs.org/misc/contribute
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
[dev-doc]: https://docs.angularjs.org/guide
[github]: https://github.com/angular/angular.js
[groups]: https://groups.google.com/forum/?fromgroups#!forum/angular
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
[irc]: http://webchat.freenode.net/?channels=angularjs&uio=d4
[js-style-guide]: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
[jsfiddle]: http://jsfiddle.net/
[list]: https://groups.google.com/forum/?fromgroups#!forum/angular
[ngDocs]: https://github.com/angular/angular.js/wiki/Writing-AngularJS-Documentation
[plunker]: http://plnkr.co/edit
[stackoverflow]: http://stackoverflow.com/questions/tagged/angularjs
[unit-testing]: http://docs.angularjs.org/guide/dev_guide.unit-testing
[![Analytics](https://ga-beacon.appspot.com/UA-8594346-11/angular.js/CONTRIBUTING.md?pixel)](https://github.com/igrigorik/ga-beacon)
+15 -3
View File
@@ -1,5 +1,6 @@
var files = require('./angularFiles').files;
var util = require('./lib/grunt/utils.js');
var versionInfo = require('./lib/versions/version-info');
var path = require('path');
module.exports = function(grunt) {
@@ -8,10 +9,10 @@ module.exports = function(grunt) {
grunt.loadTasks('lib/grunt');
var NG_VERSION = util.getVersion();
var NG_VERSION = versionInfo.currentVersion;
NG_VERSION.cdn = versionInfo.cdnVersion;
var dist = 'angular-'+ NG_VERSION.full;
//global beforeEach
util.init();
@@ -106,6 +107,9 @@ module.exports = function(grunt) {
options: {
jshintrc: true,
},
tests: {
files: { src: 'test/**/*.js' },
},
ng: {
files: { src: files['angularSrc'] },
},
@@ -118,6 +122,9 @@ module.exports = function(grunt) {
ngLocale: {
files: { src: 'src/ngLocale/**/*.js' },
},
ngMessages: {
files: { src: 'src/ngMessages/**/*.js' },
},
ngMock: {
files: { src: 'src/ngMock/**/*.js' },
},
@@ -186,6 +193,10 @@ module.exports = function(grunt) {
dest: 'build/angular-resource.js',
src: util.wrap(files['angularModules']['ngResource'], 'module')
},
messages: {
dest: 'build/angular-messages.js',
src: util.wrap(files['angularModules']['ngMessages'], 'module')
},
animate: {
dest: 'build/angular-animate.js',
src: util.wrap(files['angularModules']['ngAnimate'], 'module')
@@ -210,6 +221,7 @@ module.exports = function(grunt) {
animate: 'build/angular-animate.js',
cookies: 'build/angular-cookies.js',
loader: 'build/angular-loader.js',
messages: 'build/angular-messages.js',
touch: 'build/angular-touch.js',
resource: 'build/angular-resource.js',
route: 'build/angular-route.js',
@@ -278,7 +290,7 @@ module.exports = function(grunt) {
//alias tasks
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'package','test:unit','test:promises-aplus', 'tests:docs', 'test:protractor']);
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'jscs', 'package','test:unit','test:promises-aplus', 'tests:docs', 'test:protractor']);
grunt.registerTask('test:jqlite', 'Run the unit tests with Karma' , ['tests:jqlite']);
grunt.registerTask('test:jquery', 'Run the jQuery unit tests with Karma', ['tests:jquery']);
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', ['tests:modules']);
+1 -1
View File
@@ -1,4 +1,4 @@
AngularJS [![Build Status](https://travis-ci.org/angular/angular.js.png?branch=master)](https://travis-ci.org/angular/angular.js)
AngularJS [![Build Status](https://travis-ci.org/angular/angular.js.svg?branch=master)](https://travis-ci.org/angular/angular.js)
=========
AngularJS lets you write client-side web applications as if you had a smarter browser. It lets you
+3 -3
View File
@@ -61,9 +61,9 @@ This process based on the idea of minimizing user pain
1. Label `origin: google` for issues from Google
1. Assign a milestone:
* Current 1.x.y milestone - regressions and urgent bugs only
* Backlog - fixes; changes that should go into a patch release
* Ice Box - new features; changes that belong inß a major/minor release
* Backlog - triaged fixes and features, should be the default choice
* Current 1.x.y milestone (e.g. 1.3.0-beta-2) - regressions and urgent bugs only
1. Unassign yourself from the issue
+5
View File
@@ -80,6 +80,9 @@ angularFiles = {
'ngCookies': [
'src/ngCookies/cookies.js'
],
'ngMessages': [
'src/ngMessages/messages.js'
],
'ngResource': [
'src/ngResource/resource.js'
],
@@ -128,6 +131,7 @@ angularFiles = {
'test/auto/*.js',
'test/ng/**/*.js',
'test/ngAnimate/*.js',
'test/ngMessages/*.js',
'test/ngCookies/*.js',
'test/ngResource/*.js',
'test/ngRoute/**/*.js',
@@ -189,6 +193,7 @@ angularFiles = {
angularFiles['angularSrcModules'] = [].concat(
angularFiles['angularModules']['ngAnimate'],
angularFiles['angularModules']['ngMessages'],
angularFiles['angularModules']['ngCookies'],
angularFiles['angularModules']['ngResource'],
angularFiles['angularModules']['ngRoute'],
+6 -3
View File
@@ -1,4 +1,4 @@
#!/usr/local/bin/node
#!/usr/bin/env node
var util = require('util');
var cp = require('child_process');
@@ -121,9 +121,12 @@ then(function (tags) {
value();
}).
then(function (tags) {
var master = tags.pop();
var stable = tags.pop();
return [
{ name: 'v1.0.x', tag: tags[0] },
{ name: 'master', tag: tags[1] }
{ name: stable.replace(/\d+$/, 'x'), tag: stable },
{ name: 'master', tag: master}
];
}).
then(allInSeries(function (branch) {
-5
View File
@@ -9,8 +9,3 @@
ng\:form {
display: block;
}
.ng-animate-block-transitions {
transition:0s all!important;
-webkit-transition:0s all!important;
}
+11
View File
@@ -0,0 +1,11 @@
<h1>Oops!</h1>
<p>The page you requested does not exist. Perhaps you were looking for something else...</p>
<div ng-controller="Error404SearchCtrl">
<dl ng-repeat="(key, value) in results" ng-show="value.length" style="float: left; margin-right:20px">
<dt>{{ key }}</dt>
<dd ng-repeat="item in value"><a ng-href="{{ item.path }}">{{ item.name }}</a></dd>
</dl>
</div>
+30 -11
View File
@@ -184,10 +184,8 @@ h1,h2,h3,h4,h5,h6 {
}
pre {
padding:15px;
border:1px solid #ddd;
display:block;
border-radius:5px;
white-space: pre-wrap;
word-break: normal;
}
.aside-nav a,
@@ -420,6 +418,7 @@ iframe.example {
.type-hint {
display:inline-block;
background: gray;
}
.variables-matrix .type-hint {
@@ -464,6 +463,14 @@ iframe.example {
background:rgb(189, 63, 66);
}
.type-hint-regexp {
background: rgb(90, 84, 189);
}
.type-hint-domelement {
background: rgb(95, 158, 160);
}
.runnable-example-frame {
width:100%;
height:300px;
@@ -501,10 +508,6 @@ h4 {
padding-top:20px;
}
.improve-docs {
float:right;
}
.btn {
color:#428bca;
position: relative;
@@ -538,10 +541,17 @@ h4 {
background:white!important;
}
.view-source, .improve-docs {
position:relative;
z-index:100;
}
.view-source {
margin-right:10px;
padding-right:10px;
border-right:1px solid #999;
}
.improve-docs {
float:right;
}
.return-arguments,
@@ -554,7 +564,7 @@ h4 {
}
.return-arguments td:first-child {
width:100px;
width:100px;
}
ul.methods > li,
@@ -562,6 +572,15 @@ ul.events > li {
margin-bottom:40px;
}
@media only screen and (min-width: 769px) and (max-width: 991px) {
.main-body-grid {
margin-top: 160px;
}
.main-body-grid > .grid-left {
top: 160px;
}
}
@media only screen and (max-width : 768px) {
.picker, .picker select {
width:auto;
+1 -1
View File
@@ -215,7 +215,7 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
}
});
element.bind('$destroy', function() {
element.on('$destroy', function() {
deregisterEmbedRootScope();
embedRootScope.$destroy();
});
+4 -4
View File
@@ -274,13 +274,13 @@ var popoverElement = function() {
this.contentElement = angular.element(inner.childNodes[1]);
//stop the click on the tooltip
this.element.bind('click', function(event) {
this.element.on('click', function(event) {
event.preventDefault();
event.stopPropagation();
});
var self = this;
angular.element(document.body).bind('click',function(event) {
angular.element(document.body).on('click',function(event) {
if(self.visible()) self.hide();
});
},
@@ -359,7 +359,7 @@ directive.popover = ['popoverElement', function(popover) {
restrict: 'A',
priority : 500,
link: function(scope, element, attrs) {
element.bind('click',function(event) {
element.on('click',function(event) {
event.preventDefault();
event.stopPropagation();
if(popover.isSituatedAt(element) && popover.visible()) {
@@ -396,7 +396,7 @@ directive.foldout = ['$http', '$animate','$window', function($http, $animate, $w
if(/\/build\//.test($window.location.href)) {
url = '/build/docs' + url;
}
element.bind('click',function() {
element.on('click',function() {
scope.$apply(function() {
if(!container) {
if(loading) return;
+5 -5
View File
@@ -35,8 +35,8 @@ angular.module('ui.bootstrap.dropdown', [])
this.open = function( dropdownScope ) {
if ( !openScope ) {
$document.bind('click', closeDropdown);
$document.bind('keydown', escapeKeyBind);
$document.on('click', closeDropdown);
$document.on('keydown', escapeKeyBind);
}
if ( openScope && openScope !== dropdownScope ) {
@@ -49,8 +49,8 @@ angular.module('ui.bootstrap.dropdown', [])
this.close = function( dropdownScope ) {
if ( openScope === dropdownScope ) {
openScope = null;
$document.unbind('click', closeDropdown);
$document.unbind('keydown', escapeKeyBind);
$document.off('click', closeDropdown);
$document.off('keydown', escapeKeyBind);
}
};
@@ -124,7 +124,7 @@ angular.module('ui.bootstrap.dropdown', [])
return;
}
element.bind('click', function(event) {
element.on('click', function(event) {
event.preventDefault();
event.stopPropagation();
+4 -7
View File
@@ -1,18 +1,15 @@
angular.module('directives', [])
.directive('code', function() {
return { restrict:'E', terminal: true };
})
/**
* backToTop Directive
* @param {Function} $anchorScroll
*
* @description Ensure that the browser scrolls when the anchor is clicked
*/
.directive('backToTop', ['$anchorScroll', function($anchorScroll) {
.directive('backToTop', ['$anchorScroll', '$location', function($anchorScroll, $location) {
return function link(scope, element) {
element.on('click', function(event) {
$location.hash('');
scope.$apply($anchorScroll);
});
};
@@ -24,8 +21,8 @@ angular.module('directives', [])
restrict: 'E',
terminal: true,
compile: function(element) {
var linenums = element.hasClass('linenum') || element.parent()[0].nodeName === 'PRE';
var match = /lang-(\S)+/.exec(element.className);
var linenums = element.hasClass('linenum');// || element.parent()[0].nodeName === 'PRE';
var match = /lang-(\S+)/.exec(element[0].className);
var lang = match && match[1];
var html = element.html();
element.html(window.prettyPrintOne(html, lang, linenums));
+12 -3
View File
@@ -1,6 +1,15 @@
angular.module('DocsController', [])
.controller('DocsController', function($scope, $rootScope, $location, $window, $cookies, NG_PAGES, NG_NAVIGATION, NG_VERSION) {
.controller('DocsController', [
'$scope', '$rootScope', '$location', '$window', '$cookies', 'openPlunkr',
'NG_PAGES', 'NG_NAVIGATION', 'NG_VERSION',
function($scope, $rootScope, $location, $window, $cookies, openPlunkr,
NG_PAGES, NG_NAVIGATION, NG_VERSION) {
$scope.openPlunkr = openPlunkr;
$scope.docsVersion = NG_VERSION.isSnapshot ? 'snapshot' : NG_VERSION.version;
$scope.fold = function(url) {
if(url) {
@@ -87,7 +96,7 @@ angular.module('DocsController', [])
breadcrumbPath += '/';
});
} else {
$scope.currentArea = null;
$scope.currentArea = NG_NAVIGATION['api'];
$scope.breadcrumb = [];
}
});
@@ -118,4 +127,4 @@ angular.module('DocsController', [])
});
}
});
});
}]);
+47 -241
View File
@@ -1,65 +1,6 @@
angular.module('examples', [])
.directive('sourceEdit', function(getEmbeddedTemplate) {
return {
template: '<div class="btn-group pull-right">' +
'<a class="btn dropdown-toggle btn-primary" data-toggle="dropdown" href>' +
' <i class="icon-pencil icon-white"></i> Edit<span class="caret"></span>' +
'</a>' +
'<ul class="dropdown-menu">' +
' <li><a ng-click="plunkr($event)" href="">In Plunkr</a></li>' +
' <li><a ng-click="fiddle($event)" href="">In JsFiddle</a></li>' +
'</ul>' +
'</div>',
scope: true,
controller: function($scope, $attrs, openJsFiddle, openPlunkr) {
var sources = {
module: $attrs.sourceEdit,
deps: read($attrs.sourceEditDeps),
html: read($attrs.sourceEditHtml),
css: read($attrs.sourceEditCss),
js: read($attrs.sourceEditJs),
json: read($attrs.sourceEditJson),
unit: read($attrs.sourceEditUnit),
scenario: read($attrs.sourceEditScenario)
};
$scope.fiddle = function(e) {
e.stopPropagation();
openJsFiddle(sources);
};
$scope.plunkr = function(e) {
e.stopPropagation();
openPlunkr(sources);
};
}
};
function read(text) {
var files = [];
angular.forEach(text ? text.split(' ') : [], function(refId) {
// refId is index.html-343, so we need to strip the unique ID when exporting the name
files.push({name: refId.replace(/-\d+$/, ''), content: getEmbeddedTemplate(refId)});
});
return files;
}
})
.factory('angularUrls', function($document) {
var urls = {};
angular.forEach($document.find('script'), function(script) {
var match = script.src.match(/^.*\/(angular[^\/]*\.js)$/);
if (match) {
urls[match[1].replace(/(\-\d.*)?(\.min)?\.js$/, '.js')] = match[0];
}
});
return urls;
})
.factory('formPostData', function($document) {
.factory('formPostData', ['$document', function($document) {
return function(url, fields) {
var form = angular.element('<form style="display: none;" method="post" action="' + url + '" target="_blank"></form>');
angular.forEach(fields, function(value, name) {
@@ -71,196 +12,61 @@ angular.module('examples', [])
form[0].submit();
form.remove();
};
})
}])
.factory('prepareDefaultAppModule', function() {
return function(content) {
var deps = [];
angular.forEach(content.deps, function(file) {
if(file.name == 'angular-animate.js') {
deps.push('ngAnimate');
}
});
.factory('openPlunkr', ['formPostData', '$http', '$q', function(formPostData, $http, $q) {
return function(exampleFolder) {
var moduleName = 'App';
return {
module : moduleName,
script : "angular.module('" + moduleName + "', [" +
(deps.length ? "'" + deps.join("','") + "'" : "") + "]);\n\n"
};
};
})
var exampleName = 'AngularJS Example';
.factory('prepareEditorAssetTags', function(angularUrls) {
return function(content, options) {
options = options || {};
var includeLocalFiles = options.includeLocalFiles;
var html = makeScriptTag(angularUrls['angular.js']);
// Load the manifest for the example
$http.get(exampleFolder + '/manifest.json')
.then(function(response) {
return response.data;
})
.then(function(manifest) {
var filePromises = [];
var allFiles = [].concat(content.js, content.css, content.html, content.json);
angular.forEach(content.deps, function(file) {
if (file.name !== 'angular.js') {
var isLocal = false;
for(var i=0;i<allFiles.length;i++) {
if(allFiles[i].name == file.name) {
isLocal = true;
break;
}
}
if(!(isLocal && !includeLocalFiles)) {
var assetUrl = angularUrls[file.name] || file.name;
html += makeScriptTag(assetUrl);
}
}
});
if(includeLocalFiles) {
angular.forEach(content.css, function(file, index) {
html += makeCssLinkTag(file.name);
});
}
return html;
function makeScriptTag(src) {
return '<script type="text/javascript" src="' + src + '"></script>\n';
}
function makeCssLinkTag(src) {
return '<link rel="stylesheet" type="text/css" href="' + src + '" />\n';
}
};
})
.factory('openPlunkr', function(templateMerge, formPostData, prepareEditorAssetTags, prepareDefaultAppModule) {
return function(content) {
var hasRouting = false;
angular.forEach(content.deps, function(file) {
hasRouting = hasRouting || file.name == 'angular-route.js';
});
var indexHtmlContent = '<!doctype html>\n' +
'<html ng-app="{{module}}">\n' +
' <head>\n' +
'{{scriptDeps}}';
if(hasRouting) {
indexHtmlContent += '<script type="text/javascript">\n' +
'//this is here to make plunkr work with AngularJS routing\n' +
'angular.element(document.getElementsByTagName(\'head\')).append(' +
'angular.element(\'<base href="\' + window.location.pathname + \'" />\')' +
');\n' +
'</script>\n';
}
indexHtmlContent += '</head>\n' +
' <body>\n\n' +
'{{indexContents}}\n\n' +
' </body>\n' +
'</html>\n';
indexProp = {
module: content.module,
scriptDeps: prepareEditorAssetTags(content, { includeLocalFiles : true }),
indexContents: content.html[0].content
};
var allFiles = [].concat(content.js, content.css, content.html, content.json);
if(!content.module) {
var moduleData = prepareDefaultAppModule(content);
indexProp.module = moduleData.module;
var found = false;
angular.forEach(content.js, function(file) {
if(file.name == 'script.js') {
file.content = moduleData.script + file.content;
found = true;
}
});
if(!found) {
indexProp.scriptDeps += '<script type="text/javascript" src="script.js"></script>\n';
allFiles.push({
name : 'script.js',
content : moduleData.script
// Build a pretty title for the Plunkr
var exampleNameParts = manifest.name.split('-');
exampleNameParts.unshift('AngularJS');
angular.forEach(exampleNameParts, function(part, index) {
exampleNameParts[index] = part.charAt(0).toUpperCase() + part.substr(1);
});
}
}
exampleName = exampleNameParts.join(' - ');
var postData = {};
angular.forEach(manifest.files, function(filename) {
filePromises.push($http.get(exampleFolder + '/' + filename, { transformResponse: [] })
.then(function(response) {
angular.forEach(allFiles, function(file, index) {
if (file.content && file.name != 'index.html') {
postData['files[' + file.name + ']'] = file.content;
}
});
// The manifests provide the production index file but Plunkr wants
// a straight index.html
if (filename === "index-production.html") {
filename = "index.html"
}
postData['files[index.html]'] = templateMerge(indexHtmlContent, indexProp);
postData['tags[]'] = "angularjs";
return {
name: filename,
content: response.data
};
}));
});
return $q.all(filePromises);
})
.then(function(files) {
var postData = {};
postData.private = true;
postData.description = 'AngularJS Example Plunkr';
angular.forEach(files, function(file) {
postData['files[' + file.name + ']'] = file.content;
});
formPostData('http://plnkr.co/edit/?p=preview', postData);
postData['tags[0]'] = "angularjs";
postData['tags[1]'] = "example";
postData.private = true;
postData.description = exampleName;
formPostData('http://plnkr.co/edit/?p=preview', postData);
});
};
})
.factory('openJsFiddle', function(templateMerge, formPostData, prepareEditorAssetTags, prepareDefaultAppModule) {
var HTML = '<div ng-app=\"{{module}}\">\n{{html:2}}</div>',
CSS = '</style> <!-- Ugly Hack to make remote files preload in jsFiddle --> \n' +
'{{head:0}}<style>{{css}}',
SCRIPT = '{{script}}',
SCRIPT_CACHE = '\n\n<!-- {{name}} -->\n<script type="text/ng-template" id="{{name}}">\n{{content:2}}</script>',
BASE_HREF_TAG = '<!-- Ugly Hack to make AngularJS routing work inside of jsFiddle -->\n' +
'<base href="/" />\n\n';
return function(content) {
var prop = {
module: content.module,
html: '',
css: '',
script: ''
};
if(!prop.module) {
var moduleData = prepareDefaultAppModule(content);
prop.script = moduleData.script;
prop.module = moduleData.module;
}
angular.forEach(content.html, function(file, index) {
if (index) {
prop.html += templateMerge(SCRIPT_CACHE, file);
} else {
prop.html += file.content;
}
});
prop.head = prepareEditorAssetTags(content, { includeLocalFiles : false });
angular.forEach(content.js, function(file, index) {
prop.script += file.content;
});
angular.forEach(content.css, function(file, index) {
prop.css += file.content;
});
var hasRouting = false;
angular.forEach(content.deps, function(file) {
hasRouting = hasRouting || file.name == 'angular-route.js';
});
var compiledHTML = templateMerge(HTML, prop);
if(hasRouting) {
compiledHTML = BASE_HREF_TAG + compiledHTML;
}
formPostData("http://jsfiddle.net/api/post/library/pure/", {
title: 'AngularJS Example',
html: compiledHTML,
js: templateMerge(SCRIPT, prop),
css: templateMerge(CSS, prop)
});
};
});
}]);
+8 -4
View File
@@ -8,7 +8,7 @@ angular.module('search', [])
}
$scope.search = function(q) {
var MIN_SEARCH_LENGTH = 3;
var MIN_SEARCH_LENGTH = 2;
if(q.length >= MIN_SEARCH_LENGTH) {
var results = docsSearch(q);
var totalAreas = 0;
@@ -35,7 +35,7 @@ angular.module('search', [])
}
}
if(result) {
$location.path(result.url);
$location.path(result.path);
$scope.hideResults();
}
};
@@ -45,6 +45,10 @@ angular.module('search', [])
};
}])
.controller('Error404SearchCtrl', ['$scope', '$location', 'docsSearch', function($scope, $location, docsSearch) {
$scope.results = docsSearch($location.path().split(/[\/\.:]/).pop());
}])
.factory('lunrSearch', function() {
return function(properties) {
if (window.RUNNING_IN_NG_TEST_RUNNER) return null;
@@ -127,7 +131,7 @@ angular.module('search', [])
return function(scope, element, attrs) {
var ESCAPE_KEY_KEYCODE = 27,
FORWARD_SLASH_KEYCODE = 191;
angular.element($document[0].body).bind('keydown', function(event) {
angular.element($document[0].body).on('keydown', function(event) {
var input = element[0];
if(event.keyCode == FORWARD_SLASH_KEYCODE && document.activeElement != input) {
event.stopPropagation();
@@ -136,7 +140,7 @@ angular.module('search', [])
}
});
element.bind('keydown', function(event) {
element.on('keydown', function(event) {
if(event.keyCode == ESCAPE_KEY_KEYCODE) {
event.stopPropagation();
event.preventDefault();
+18 -26
View File
@@ -21,38 +21,30 @@ angular.module('tutorials', [])
element.addClass('btn-group');
element.addClass('tutorial-nav');
element.append(templateMerge(
'<a href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="icon-step-backward"></i> Previous</li></a>\n' +
'<a href="http://angular.github.com/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="icon-play"></i> Live Demo</li></a>\n' +
'<a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><li class="btn btn-primary"><i class="icon-search"></i> Code Diff</li></a>\n' +
'<a href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="icon-step-forward"></i></li></a>', props));
'<a href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-step-backward"></i> Previous</li></a>\n' +
'<a href="http://angular.github.io/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="glyphicon glyphicon-play"></i> Live Demo</li></a>\n' +
'<a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-search"></i> Code Diff</li></a>\n' +
'<a href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="glyphicon glyphicon-step-forward"></i></li></a>', props));
}
};
})
.directive('docTutorialReset', function() {
function tab(name, command, id, step) {
return '' +
' <div class=\'tab-pane well\' title="' + name + '" value="' + id + '">\n' +
' <ol>\n' +
' <li><p>Reset the workspace to step ' + step + '.</p>' +
' <pre>' + command + '</pre></li>\n' +
' <li><p>Refresh your browser or check the app out on <a href="http://angular.github.com/angular-phonecat/step-' + step + '/app">Angular\'s server</a>.</p></li>\n' +
' </ol>\n' +
' </div>\n';
}
return {
compile: function(element, attrs) {
var step = attrs.docTutorialReset;
element.html(
'<div ng-hide="show">' +
'<p><a href="" ng-click="show=true;$event.stopPropagation()">Workspace Reset Instructions ➤</a></p>' +
'</div>\n' +
'<div class="tabbable" ng-show="show" ng-model="$cookies.platformPreference">\n' +
tab('Git on Mac/Linux', 'git checkout -f step-' + step, 'gitUnix', step) +
tab('Git on Windows', 'git checkout -f step-' + step, 'gitWin', step) +
'</div>\n');
}
scope: {
'step': '@docTutorialReset'
},
template:
'<p><a href="" ng-click="show=!show;$event.stopPropagation()">Workspace Reset Instructions ➤</a></p>\n' +
'<div class="alert alert-info" ng-show="show">\n' +
' <p>Reset the workspace to step {{step}}.</p>' +
' <p><pre>git checkout -f step-{{step}}</pre></p>\n' +
' <p>Refresh your browser or check out this step online: '+
'<a href="http://angular.github.io/angular-phonecat/step-{{step}}/app">Step {{step}} Live Demo</a>.</p>\n' +
'</div>\n' +
'<p>The most important changes are listed below. You can see the full diff on ' +
'<a ng-href="https://github.com/angular/angular-phonecat/compare/step-{{step ? (step - 1): \'0~1\'}}...step-{{step}}">GitHub</a>\n' +
'</p>'
};
});
+38
View File
@@ -0,0 +1,38 @@
describe("code", function() {
var prettyPrintOne, oldPP;
var compile, scope;
var any = jasmine.any;
beforeEach(module('directives'));
beforeEach(inject(function($rootScope, $compile) {
// Provide stub for pretty print function
oldPP = window.prettyPrintOne;
prettyPrintOne = window.prettyPrintOne = jasmine.createSpy();
scope = $rootScope.$new();
compile = $compile;
}));
afterEach(function() {
window.prettyPrintOne = oldPP;
});
it('should pretty print innerHTML', function() {
compile('<code>var x;</code>')(scope);
expect(prettyPrintOne).toHaveBeenCalledWith('var x;', null, false);
});
it('should allow language declaration', function() {
compile('<code class="lang-javascript"></code>')(scope);
expect(prettyPrintOne).toHaveBeenCalledWith(any(String), 'javascript', false);
});
it('supports allow line numbers', function() {
compile('<code class="linenum"></code>')(scope);
expect(prettyPrintOne).toHaveBeenCalledWith(any(String), null, true);
});
});
+1
View File
@@ -3,6 +3,7 @@ describe("DocsController", function() {
angular.module('fake', [])
.value('$cookies', {})
.value('openPlunkr', function() {})
.value('NG_PAGES', {})
.value('NG_NAVIGATION', {})
.value('NG_VERSION', {});
+8
View File
@@ -25,6 +25,14 @@ module.exports = function(config) {
require('./tag-defs/tutorial-step')
]);
config.append('processing.defaultTagTransforms', [
require('dgeni-packages/jsdoc/tag-defs/transforms/trim-whitespace')
]);
config.append('processing.inlineTagDefinitions', [
require('./inline-tag-defs/type')
]);
config.set('processing.search.ignoreWordsFile', path.resolve(packagePath, 'ignore.words'));
config.prepend('rendering.templateFolders', [
+12
View File
@@ -0,0 +1,12 @@
var typeClassFilter = require('dgeni-packages/ngdoc/rendering/filters/type-class');
var encoder = new require('node-html-encoder').Encoder();
module.exports = {
name: 'type',
description: 'Replace with markup that displays a nice type',
handlerFactory: function() {
return function(doc, tagName, tagDescription) {
return '<a href="" class="' + typeClassFilter.process(tagDescription) + '">'+encoder.htmlEncode(tagDescription) + '</a>';
};
}
};
+15 -8
View File
@@ -1,26 +1,33 @@
var writer = require('dgeni/lib/utils/doc-writer');
var fs = require('q-io/fs');
var log = require('winston');
var util = require("util");
var filter, outputPath, depth;
module.exports = {
name: 'debug-dump',
runBefore: ['write-files'],
description: 'This processor dumps docs that match a filter to a file',
init: function(config, injectables) {
process: function(docs, config) {
var filter, outputPath, depth;
filter = config.get('processing.debug-dump.filter');
outputPath = config.get('processing.debug-dump.outputPath');
depth = config.get('processing.debug-dump.depth', 2);
},
process: function(docs) {
if ( filter && outputPath ) {
log.info('Dumping docs:', filter, outputPath);
var filteredDocs = filter(docs);
var dumpedDocs = util.inspect(filteredDocs, depth);
return writer.writeFile(outputPath, dumpedDocs).then(function() {
return writeFile(outputPath, dumpedDocs).then(function() {
return docs;
});
}
}
};
};
function writeFile(file, content) {
return fs.makeTree(fs.directory(file)).then(function() {
return fs.write(file, content, 'wb');
});
}
+18 -10
View File
@@ -5,16 +5,18 @@ var path = require('canonical-path');
module.exports = {
name: 'error-docs',
description: 'Compute the various fields for docs in the Error area',
runAfter: ['tags-extracted'],
init: function(config, injectables) {
injectables.value('errorNamespaces', {});
var minerrInfoPath = config.get('processing.errors.minerrInfoPath');
if ( !minerrInfoPath ) {
throw new Error('Error in configuration: Please provide a path to the minerr info file (errors.json) ' +
'in the `config.processing.errors.minerrInfoPath` property');
}
injectables.value('minerrInfo', require(minerrInfoPath));
runAfter: ['tags-extracted', 'compute-path'],
runBefore: ['extra-docs-added'],
exports: {
errorNamespaces: ['factory', function() { return {}; }],
minerrInfo: ['factory', function(config) {
var minerrInfoPath = config.get('processing.errors.minerrInfoPath');
if ( !minerrInfoPath ) {
throw new Error('Error in configuration: Please provide a path to the minerr info file (errors.json) ' +
'in the `config.processing.errors.minerrInfoPath` property');
}
return require(minerrInfoPath);
}]
},
process: function(docs, partialNames, errorNamespaces, minerrInfo) {
@@ -22,6 +24,12 @@ module.exports = {
_.forEach(docs, function(doc) {
if ( doc.docType === 'error' ) {
// Parse out the error info from the id
parts = doc.name.split(':');
doc.namespace = parts[0];
doc.name = parts[1];
var namespaceDoc = errorNamespaces[doc.namespace];
if ( !namespaceDoc ) {
// First time we came across this namespace, so create a new one
+10 -7
View File
@@ -1,15 +1,18 @@
var gruntUtils = require('../../../lib/grunt/utils');
var versionInfo = require('../../../lib/versions/version-info');
module.exports = {
name: 'git-data',
runBefore: ['loading-files'],
runBefore: ['reading-files'],
description: 'This processor adds information from the local git repository to the extraData injectable',
init: function(config, injectables) {
injectables.value('gitData', {
version: gruntUtils.getVersion(),
versions: gruntUtils.getPreviousVersions(),
info: gruntUtils.getGitRepoInfo()
});
exports: {
gitData: ['factory', function() {
return {
version: versionInfo.currentVersion,
versions: versionInfo.previousVersions,
info: versionInfo.gitRepoInfo
};
}]
},
process: function(extraData, gitData) {
extraData.git = gitData;
+3 -5
View File
@@ -1,20 +1,18 @@
var _ = require('lodash');
var log = require('winston');
var path = require('canonical-path');
var deployment;
module.exports = {
name: 'index-page',
runAfter: ['adding-extra-docs'],
runBefore: ['extra-docs-added'],
description: 'This processor creates docs that will be rendered as the index page for the app',
init: function(config) {
deployment = config.deployment;
process: function(docs, config) {
var deployment = config.deployment;
if ( !deployment || !deployment.environments ) {
throw new Error('No deployment environments found in the config.');
}
},
process: function(docs) {
// Collect up all the areas in the docs
var areas = {};
+9 -12
View File
@@ -3,20 +3,20 @@ var log = require('winston');
var fs = require('fs');
var path = require('canonical-path');
// Keywords to ignore
var wordsToIgnore = [];
var propertiesToIgnore;
var areasToSearch;
// Keywords start with "ng:" or one of $, _ or a letter
var KEYWORD_REGEX = /^((ng:|[\$_a-z])[\w\-_]+)/;
module.exports = {
name: 'keywords',
runAfter: ['docs-processed'],
runBefore: ['adding-extra-docs'],
description: 'This processor extracts all the keywords from the document',
init: function(config) {
process: function(docs, config) {
// Keywords to ignore
var wordsToIgnore = [];
var propertiesToIgnore;
var areasToSearch;
// Keywords start with "ng:" or one of $, _ or a letter
var KEYWORD_REGEX = /^((ng:|[\$_a-z])[\w\-_]+)/;
// Load up the keywords to ignore, if specified in the config
if ( config.processing.search && config.processing.search.ignoreWordsFile ) {
@@ -34,9 +34,6 @@ module.exports = {
propertiesToIgnore = _.indexBy(config.get('processing.search.propertiesToIgnore', []));
log.debug('Properties to ignore', propertiesToIgnore);
},
process: function(docs) {
var ignoreWordsMap = _.indexBy(wordsToIgnore);
// If the title contains a name starting with ng, e.g. "ngController", then add the module name
+13 -19
View File
@@ -11,17 +11,17 @@ var AREA_NAMES = {
};
function getNavGroup(pages, area, pageSorter, pageMapper) {
var navItems = _(pages)
// We don't want the child to include the index page as this is already catered for
.omit(function(page) { return page.id === 'index'; })
// Apply the supplied sorting function
.sortBy(pageSorter)
// Apply the supplied mapping function
.map(pageMapper)
.value();
return {
@@ -129,22 +129,22 @@ var navGroupMappers = {
}
};
var outputFolder;
module.exports = {
name: 'pages-data',
description: 'This plugin will create a new doc that will be rendered as an angularjs module ' +
'which will contain meta information about the pages and navigation',
runAfter: ['adding-extra-docs', 'component-groups-generate'],
runAfter: ['adding-extra-docs', 'component-groups-generate', 'compute-path'],
runBefore: ['extra-docs-added'],
init: function(config) {
outputFolder = config.rendering.outputFolder;
},
process: function(docs) {
process: function(docs, config) {
var outputFolder = config.rendering.outputFolder;
_(docs)
.filter(function(doc) { return doc.area === 'api'; })
.filter(function(doc) { return doc.docType === 'module'; })
.forEach(function(doc) { if ( !doc.path ) {
log.warn('Missing path property for ', doc.id);
}})
.map(function(doc) { return _.pick(doc, ['id', 'module', 'docType', 'area']); })
.tap(function(docs) {
log.debug(docs);
@@ -173,7 +173,7 @@ module.exports = {
// - ngView
// - section "service"
// - $route
//
//
var areas = {};
_(navPages)
.groupBy('area')
@@ -188,13 +188,7 @@ module.exports = {
area.navGroups = navGroupMapper(pages, area);
});
_.forEach(docs, function(doc) {
if ( !doc.path ) {
log.warn('Missing path property for ', doc.id);
}
});
// Extract a list of basic page information for mapping paths to paritals and for client side searching
// Extract a list of basic page information for mapping paths to partials and for client side searching
var pages = _(docs)
.map(function(doc) {
var page = _.pick(doc, [
+26 -29
View File
@@ -1,45 +1,20 @@
var _ = require('lodash');
var log = require('winston');
var path = require('canonical-path');
var trimIndentation = require('dgeni/lib/utils/trim-indentation');
var code = require('dgeni/lib/utils/code');
var protractorFolder;
function createProtractorDoc(example, file, env) {
var protractorDoc = {
docType: 'e2e-test',
id: 'protractorTest' + '-' + example.id,
template: 'protractorTests.template.js',
outputPath: path.join(protractorFolder, example.id, env + '_test.js'),
innerTest: file.fileContents,
pathPrefix: '.', // Hold for if we test with full jQuery
exampleId: example.id,
description: example.doc.id
};
if (env === 'jquery') {
protractorDoc.examplePath = example.outputFolder + '/index-jquery.html'
} else {
protractorDoc.examplePath = example.outputFolder + '/index.html'
}
return protractorDoc;
}
module.exports = {
name: 'protractor-generate',
description: 'Generate a protractor test file from the e2e tests in the examples',
runAfter: ['adding-extra-docs'],
runBefore: ['extra-docs-added'],
init: function(config, injectables) {
protractorFolder = config.get('rendering.protractor.outputFolder', 'ptore2e');
},
process: function(docs, examples) {
process: function(docs, examples, config) {
var protractorFolder = config.get('rendering.protractor.outputFolder', 'ptore2e');
_.forEach(examples, function(example) {
_.forEach(example.files, function(file) {
// Check if it's a Protractor test.
if (!(file.type == 'protractor')) {
if (file.type !== 'protractor') {
return;
}
@@ -48,5 +23,27 @@ module.exports = {
docs.push(createProtractorDoc(example, file, 'jqlite'));
});
});
function createProtractorDoc(example, file, env) {
var protractorDoc = {
docType: 'e2e-test',
id: 'protractorTest' + '-' + example.id,
template: 'protractorTests.template.js',
outputPath: path.join(protractorFolder, example.id, env + '_test.js'),
innerTest: file.fileContents,
pathPrefix: '.', // Hold for if we test with full jQuery
exampleId: example.id,
description: example.doc.id
};
if (env === 'jquery') {
protractorDoc.examplePath = example.outputFolder + '/index-jquery.html';
} else {
protractorDoc.examplePath = example.outputFolder + '/index.html';
}
return protractorDoc;
}
}
};
+2 -2
View File
@@ -1,9 +1,9 @@
module.exports = {
name: 'step',
transformFn: function(doc, tag) {
transforms: function(doc, tag, value) {
if ( doc.docType !== 'tutorial' ) {
throw new Error('Invalid tag, step. You should only use this tag on tutorial docs');
}
return parseInt(tag.description,10);
return parseInt(value,10);
}
};
@@ -175,7 +175,7 @@
<div class="container main-grid main-header-grid">
<div class="grid-left">
<div ng-controller="DocsVersionsCtrl" class="picker version-picker">
<select ng-options="v as ('v' + v.full) group by (v.isStable?'Stable':'Unstable') for v in docs_versions"
<select ng-options="v as ('v' + v.version + (v.isSnapshot ? ' (snapshot)' : '')) group by (v.isStable?'Stable':'Unstable') for v in docs_versions"
ng-model="docs_version"
ng-change="jumpToDocsVersion(docs_version)"
class="docs-version-jump">
@@ -219,14 +219,14 @@
</div>
<div class="grid-right">
<div id="loading" ng-show="loading">Loading...</div>
<div ng-hide="loading" ng-include="currentPage.outputPath" onload="afterPartialLoaded()" autoscroll></div>
<div ng-hide="loading" ng-include="currentPage.outputPath || 'Error404.html'" onload="afterPartialLoaded()" autoscroll></div>
</div>
</div>
</section>
<footer class="footer">
<div class="container">
<p class="pull-right"><a back-to-top href="#">Back to top</a></p>
<p class="pull-right"><a back-to-top>Back to top</a></p>
<p>
Super-powered by Google ©2010-2014
@@ -0,0 +1,27 @@
{# Be aware that we need these extra new lines here or marked will not realise that the <div>
is HTML and wrap each line in a <p> - thus breaking the HTML #}
<div>
<a ng-click="openPlunkr('{$ doc.example.outputFolder $}')" class="btn pull-right">
<i class="glyphicon glyphicon-edit">&nbsp;</i>
Edit in Plunker</a>
<div class="runnable-example"
path="{$ doc.example.outputFolder $}"
{%- for attrName, attrValue in doc.example.attributes %}
{$ attrName $}="{$ attrValue $}"{% endfor %}>
{% for fileName, file in doc.example.files %}
<div class="runnable-example-file" {% for attrName, attrValue in file.attributes %}
{$ attrName $}="{$ attrValue $}"{% endfor %}>
{% code -%}
{$ file.fileContents $}
{%- endcode %}
</div>
{% endfor %}
<iframe class="runnable-example-frame" src="{$ doc.example.outputFolder $}/index.html" name="{$ doc.example.id $}"></iframe>
</div>
</div>
{# Be aware that we need these extra new lines here or marked will not realise that the <div>
above is HTML and wrap each line in a <p> - thus breaking the HTML #}
+1 -1
View File
@@ -6,7 +6,7 @@
Welcome to the AngularJS API docs page. These pages contain the AngularJS reference materials for version <strong ng-bind="version"></strong>.
The documentation is organized into **{@link guide/module modules}** which contain various components of an AngularJS application.
These components are {@link guide/directive directives}, {@link guide/dev_guide.services services}, {@link guide/filter filters}, {@link guide/providers providers}, {@link guide/templates types}, global APIs and testing mocks.
These components are {@link guide/directive directives}, {@link guide/services services}, {@link guide/filter filters}, {@link guide/providers providers}, {@link guide/templates types}, global APIs and testing mocks.
<div class="alert alert-info">
**Angular Namespaces `$` and `$$`**
+1 -1
View File
@@ -8,7 +8,7 @@ but the required directive controller is not present on the current DOM element
To resolve this error ensure that there is no typo in the required controller name and that the required directive controller is present on the current element.
If the required controller is expected to be on a ancestor element, make ensure that you prefix the controller name in the `require` definition with `^`.
If the required controller is expected to be on a ancestor element, make sure that you prefix the controller name in the `require` definition with `^`.
If the required controller is optionally requested, use `?` or `^?` to specify that.
@@ -30,6 +30,9 @@ Following are invalid uses of this directive:
<!-- ERROR because `myFn()=localValue` is an invalid statement -->
<my-directive bind="myFn()">
<!-- ERROR because attribute bind wasn't provided -->
<my-directive>
```
+15 -8
View File
@@ -3,24 +3,31 @@
@fullName Module Unavailable
@description
This error occurs when trying to "re-open" a module that has not yet been defined.
This error occurs when you declare a dependency on a module that isn't defined anywhere or hasn't
been loaded in the current browser context.
When you receive this error, check that the name of the module in question is correct and that the
file in which this module is defined has been loaded (either via `<script>` tag, loader like
require.js, or testing harness like karma).
A less common reason for this error is trying to "re-open" a module that has not yet been defined.
To define a new module, call {@link angular.module angular.module} with a name
and an array of dependent modules, like so:
```
```js
// When defining a module with no module dependencies,
// the requires array should be defined and empty.
// the array of dependencies should be defined and empty.
var myApp = angular.module('myApp', []);
```
To retrieve a reference to the same module for further configuration, call
`angular.module` without the `requires` array.
`angular.module` without the array argument.
```
```js
var myApp = angular.module('myApp');
```
Calling `angular.module` without the `requires` array when the module has not yet
been defined causes this error to be thrown. To fix it, define your module with
a name and an empty array, as in the first example above.
Calling `angular.module` without the array of dependencies when the module has not yet been defined
causes this error to be thrown. To fix it, define your module with a name and an empty array, as in
the first example above.
@@ -0,0 +1,54 @@
@ngdoc error
@name $injector:strictdi
@fullName Explicit annotation required
@description
This error occurs when attempting to invoke a function or provider which
has not been explicitly annotated, while the application is running with
strict-di mode enabled.
For example:
```
angular.module("myApp", [])
// BadController cannot be invoked, because
// the dependencies to be injected are not
// explicitly listed.
.controller("BadController", function($scope, $http, $filter) {
// ...
});
```
To fix the error, explicitly annotate the function using either the inline
bracket notation, or with the $inject property:
```
function GoodController1($scope, $http, $filter) {
// ...
}
GoodController1.$inject = ["$scope", "$http", "$filter"];
angular.module("myApp", [])
// GoodController1 can be invoked because it
// had an $inject property, which is an array
// containing the dependency names to be
// injected.
.controller("GoodController1", GoodController1)
// GoodController2 can also be invoked, because
// the dependencies to inject are listed, in
// order, in the array, with the function to be
// invoked trailing on the end.
.controller("GoodController2", [
"$scope",
"$http",
"$filter",
function($scope, $http, $filter) {
// ...
}
]);
```
For more information about strict-di mode, see {@link ng.directive:ngApp ngApp}
and {@link api/angular.bootstrap angular.bootstrap}.
+2 -2
View File
@@ -9,7 +9,7 @@ correctly. For example:
```
angular.module('myApp', [])
.controller('myCtrl', ['myService', function (myService) {
.controller('MyController', ['myService', function (myService) {
// Do something with myService
}]);
```
@@ -20,7 +20,7 @@ sure each dependency is defined will fix the problem.
```
angular.module('myApp', [])
.service('myService', function () { /* ... */ })
.controller('myCtrl', ['myService', function (myService) {
.controller('MyController', ['myService', function (myService) {
// Do something with myService
}]);
```
+2
View File
@@ -2,6 +2,8 @@
@name Error Reference
@description
# Error Reference
Use the Error Reference manual to find information about error conditions in
your AngularJS app. Errors thrown in production builds of AngularJS will log
links to this site on the console.
+26 -3
View File
@@ -3,9 +3,11 @@
@fullName App Already Bootstrapped with this Element
@description
Occurs when calling angular.bootstrap on an element that has already been bootstrapped.
Occurs when calling {@link angular.bootstrap} on an element that has already been bootstrapped.
This usually happens when you accidentally use both `ng-app` and `angular.bootstrap` to bootstrap an
application.
This usually happens when you accidentally use both `ng-app` and `angular.bootstrap` to bootstrap an application.
```
<html>
@@ -18,7 +20,9 @@ This usually happens when you accidentally use both `ng-app` and `angular.bootst
</html>
```
Note that for bootrapping purposes, the `<html>` element is the same as `document`, so the following will also throw an error.
Note that for bootstrapping purposes, the `<html>` element is the same as `document`, so the following
will also throw an error.
```
<html>
...
@@ -27,3 +31,22 @@ Note that for bootrapping purposes, the `<html>` element is the same as `documen
</script>
</html>
```
You can also get this error if you accidentally load AngularJS itself more than once.
```
<html ng-app>
<head>
<script src="angular.js"></script>
...
</head>
<body>
...
<script src="angular.js"></script>
</body>
</html>
```
@@ -1,5 +1,5 @@
@ngdoc overview
@name Angular Services: Using $location
@name Using $location
@description
# What does it do?
@@ -329,7 +329,7 @@ reload to the original link.
Example: `<a href="http://angularjs.org/">link</a>`
- Links starting with '/' that lead to a different base path when base is defined<br>
Example: `<a href="/not-my-base/link">link</a>`
When running Angular in the root of a domain, along side perhaps a normal application in the same
directory, the "otherwise" route handler will try to handle all the URLs, including ones that map
to static files.
+1 -1
View File
@@ -271,7 +271,7 @@ making calls to it when needed.
```js
myModule.directive('my-directive', ['$animate', function($animate) {
return function(element, scope, attrs) {
element.bind('click', function() {
element.on('click', function() {
if(element.hasClass('clicked')) {
$animate.removeClass(element, 'clicked');
} else {
+33 -20
View File
@@ -2,11 +2,12 @@
@name Bootstrap
@description
# Overview
# Bootstrap
This page explains the Angular initialization process and how you can manually initialize Angular
if necessary.
## Angular `<script>` Tag
This example shows the recommended path for integrating Angular with what we call automatic
@@ -23,7 +24,7 @@ initialization.
</html>
```
* Place the `script` tag at the bottom of the page. Placing script tags at the end of the page
1. Place the `script` tag at the bottom of the page. Placing script tags at the end of the page
improves app load time because the HTML loading is not blocked by loading of the `angular.js`
script. You can get the latest bits from http://code.angularjs.org. Please don't link
your production code to this URL, as it will expose a security hole on your site. For
@@ -32,16 +33,16 @@ initialization.
debugging.
* Choose: `angular-[version].min.js` for a compressed and obfuscated file, suitable for use in
production.
* Place `ng-app` to the root of your application, typically on the `<html>` tag if you want
2. Place `ng-app` to the root of your application, typically on the `<html>` tag if you want
angular to auto-bootstrap your application.
<html ng-app>
* If IE7 support is required add `id="ng-app"`
3. If you require IE7 support add `id="ng-app"`
<html ng-app id="ng-app">
* If you choose to use the old style directive syntax `ng:` then include xml-namespace in `html`
4. If you choose to use the old style directive syntax `ng:` then include xml-namespace in `html`
to make IE happy. (This is here for historical reasons, and we no longer recommend use of
`ng:`.)
@@ -79,7 +80,6 @@ If the {@link ng.directive:ngApp `ng-app`} directive is found then Angular will:
## Manual Initialization
If you need to have more control over the initialization process, you can use a manual
bootstrapping method instead. Examples of when you'd need to do this include using script loaders
or the need to perform an operation before Angular compiles a page.
@@ -88,23 +88,35 @@ Here is an example of manually initializing Angular:
```html
<!doctype html>
<html xmlns:ng="http://angularjs.org">
<body>
Hello {{'World'}}!
<script src="http://code.angularjs.org/angular.js"></script>
<script>
angular.element(document).ready(function() {
angular.module('myApp', []);
angular.bootstrap(document, ['myApp']);
});
</script>
</body>
<html>
<body>
Hello {{'World'}}!
<script src="http://code.angularjs.org/angular.js"></script>
<script>
angular.module('myApp', [])
.controller('MyController', ['$scope', function ($scope) {
$scope.greetMe = 'World';
}]);
angular.element(document).ready(function() {
angular.bootstrap(document, ['myApp']);
});
</script>
</body>
</html>
```
Note that we have provided the name of our application module to be loaded into the injector as the second
parameter of the {@link angular.bootstrap} function. Notice that `angular.bootstrap` will not create modules
on the fly. You must create any custom {@link guide/module modules} before you pass them as a parameter.
Note that we provided the name of our application module to be loaded into the injector as the second
parameter of the {@link angular.bootstrap} function. Notice that `angular.bootstrap` will not create modules
on the fly. You must create any custom {@link guide/module modules} before you pass them as a parameter.
You should call `angular.bootstrap()` *after* you've loaded or defined your modules.
You cannot add controllers, services, directives, etc after an application bootstraps.
<div class="alert alert-warning">
**Note:** You should not use the ng-app directive when manually bootstrapping your app.
</div>
This is the sequence that your code should follow:
@@ -114,6 +126,7 @@ This is the sequence that your code should follow:
2. Call {@link angular.bootstrap} to {@link compiler compile} the element into an
executable, bi-directionally bound application.
## Deferred Bootstrap
This feature enables tools like Batarang and test runners to
+3 -3
View File
@@ -105,8 +105,8 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
}
function mouseup() {
$document.unbind('mousemove', mousemove);
$document.unbind('mouseup', mouseup);
$document.off('mousemove', mousemove);
$document.off('mouseup', mouseup);
}
};
});
@@ -325,7 +325,7 @@ This will not render properly, unless we do some scope magic.
The first issue we have to solve is that the dialog box template expects `title` to be defined.
But we would like the template's scope property `title` to be the result of interpolating the
`<dialog>` element's `title` attribute (i.e. `"Hello {{username}}"`. Furthermore, the buttons expect
`<dialog>` element's `title` attribute (i.e. `"Hello {{username}}"`). Furthermore, the buttons expect
the `onOk` and `onCancel` functions to be present in the scope. This limits the usefulness of the
widget. To solve the mapping issue we use the `locals` to create local variables which the template
expects as follows:
+22 -22
View File
@@ -2,10 +2,10 @@
@name Conceptual Overview
@description
There are some concepts within Angular that you should understand before creating your first application.
This section touches all important parts of Angular really quickly using a simple example.
However, it won't explain all details.
For a more in-depth explanation, have a look at the {@link tutorial/ tutorial}.
# Conceptual Overview
This section briefly touches on all of the important parts of AngularJS using a simple example.
For a more in-depth explanation, see the {@link tutorial/ tutorial}.
| Concept | Description |
|------------------|------------------------------------------|
@@ -25,16 +25,16 @@ For a more in-depth explanation, have a look at the {@link tutorial/ tutorial}.
|{@link concepts#service Service} | reusable business logic independent of views |
# A first example: Data binding
## A first example: Data binding
In the following example we will build a form to calculate the costs of an invoice in different currencies.
Let's start with input fields for quantity and cost whose values are multiplied to produce the total of the invoice:
<example>
<example name="guide-concepts-1" ng-app-included="true">
<file name="index.html">
<div ng-init="qty=1;cost=2">
<div ng-app ng-init="qty=1;cost=2">
<b>Invoice:</b>
<div>
Quantity: <input type="number" ng-model="qty" required >
@@ -97,12 +97,12 @@ recalculated and the DOM is updated with their values.
The concept behind this is <a name="databinding">"{@link databinding two-way data binding}"</a>.
# Adding UI logic: Controllers
## Adding UI logic: Controllers
Let's add some more logic to the example that allows us to enter and calculate the costs in
different currencies and also pay the invoice.
<example module="invoice1">
<example name="guide-concepts-2" ng-app-included="true" >
<file name="invoice1.js">
angular.module('invoice1', [])
.controller('InvoiceController', function() {
@@ -128,7 +128,7 @@ different currencies and also pay the invoice.
});
</file>
<file name="index.html">
<div ng-controller="InvoiceController as invoice">
<div ng-app="invoice1" ng-controller="InvoiceController as invoice">
<b>Invoice:</b>
<div>
Quantity: <input type="number" ng-model="invoice.qty" required >
@@ -181,17 +181,17 @@ The following graphic shows how everything works together after we introduced th
<img style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-databinding2.png">
# View independent business logic: Services
## View independent business logic: Services
Right now, the `InvoiceController` contains all logic of our example. When the application grows it
is a good practice to move view independent logic from the controller into a so called
<a name="service">"{@link dev_guide.services service}"</a>, so it can be reused by other parts
<a name="service">"{@link services service}"</a>, so it can be reused by other parts
of the application as well. Later on, we could also change that service to load the exchange rates
from the web, e.g. by calling the Yahoo Finance API, without changing the controller.
Let's refactor our example and move the currency conversion into a service in another file:
<example module="invoice2">
<example name="guide-concepts-2" ng-app-included="true">
<file name="finance2.js">
angular.module('finance2', [])
.factory('currencyConverter', function() {
@@ -228,7 +228,7 @@ Let's refactor our example and move the currency conversion into a service in an
}]);
</file>
<file name="index.html">
<div ng-controller="InvoiceController as invoice">
<div ng-app="invoice2" ng-controller="InvoiceController as invoice">
<b>Invoice:</b>
<div>
Quantity: <input type="number" ng-model="invoice.qty" required >
@@ -278,7 +278,7 @@ The code snippet `angular.module('invoice2', ['finance2'])` specifies that the
Now that Angular knows of all the parts of the application, it needs to create them.
In the previous section we saw that controllers are created using a factory function.
For services there are multiple ways to define their factory
(see the {@link dev_guide.services service guide}).
(see the {@link services service guide}).
In the example above, we are using a function that returns the `currencyConverter` function as the factory
for the service.
@@ -297,12 +297,12 @@ Angular uses this array syntax to define the dependencies so that the DI also wo
the code, which will most probably rename the argument name of the controller constructor function
to something shorter like `a`.
# Accessing the backend
## Accessing the backend
Let's finish our example by fetching the exchange rates from the Yahoo Finance API.
The following example shows how this is done with Angular:
<example module="invoice3">
<example name="guide-concepts-3" ng-app-included="true">
<file name="invoice3.js">
angular.module('invoice3', ['finance3'])
.controller('InvoiceController', ['currencyConverter', function(currencyConverter) {
@@ -356,7 +356,7 @@ The following example shows how this is done with Angular:
}]);
</file>
<file name="index.html">
<div ng-controller="InvoiceController as invoice">
<div ng-app="invoice3" ng-controller="InvoiceController as invoice">
<b>Invoice:</b>
<div>
Quantity: <input type="number" ng-model="invoice.qty" required >
@@ -379,8 +379,8 @@ The following example shows how this is done with Angular:
</example>
What changed?
Our `currencyConverter` service of the `finance` module now uses the
{@link ng.$http $http} service, a builtin service provided by Angular
for accessing the backend. It is a wrapper around [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
and [JSONP](http://en.wikipedia.org/wiki/JSONP) transports. Details can be found in the api docs of that service.
Our `currencyConverter` service of the `finance` module now uses the {@link ng.$http `$http`}, a
built-in service provided by Angular for accessing a server backend. `$http` is a wrapper around
[`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
and [JSONP](http://en.wikipedia.org/wiki/JSONP) transports.
+63 -65
View File
@@ -4,19 +4,31 @@
# Understanding Controllers
In Angular, a Controller is a JavaScript **constructor function** that is used to augment the
In Angular, a Controller is a JavaScript **constructor function** that is used to augment the
{@link scope Angular Scope}.
When a Controller is attached to the DOM via the {@link ng.directive:ngController ng-controller}
directive, Angular will instantiate a new Controller object, using the specified Controller's
**constructor function**. A new **child scope** will be available as an injectable parameter to the
**constructor function**. A new **child scope** will be available as an injectable parameter to the
Controller's constructor function as `$scope`.
Use Controllers to:
Use controllers to:
- Set up the initial state of the `$scope` object.
- Add behavior to the `$scope` object.
Do not use controllers to:
- Manipulate DOM — Controllers should contain only business logic.
Putting any presentation logic into Controllers significantly affects its testability. Angular
has {@link databinding databinding} for most cases and {@link guide/directive directives} to
encapsulate manual DOM manipulation.
- Format input — Use {@link forms angular form controls} instead.
- Filter output — Use {@link guide/filter angular filters} instead.
- Share code or state across controllers — Use {@link services angular
services} instead.
- Manage the life-cycle of other components (for example, to create service instances).
# 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
@@ -25,22 +37,22 @@ The properties contain the **view model** (the model that will be presented by t
`$scope` properties will be available to the template at the point in the DOM where the Controller
is registered.
The following example shows a very simple constructor function for a Controller, `GreetingCtrl`,
The following example shows a very simple constructor function for a Controller, `GreetingController`,
which attaches a `greeting` property containing the string `'Hola!'` to the `$scope`:
```js
function GreetingCtrl($scope) {
$scope.greeting = 'Hola!';
}
function GreetingController($scope) {
$scope.greeting = 'Hola!';
}
```
Once the Controller has been attached to the DOM, the `greeting` property can be data-bound to the
template:
```js
<div ng-controller="GreetingCtrl">
{{ greeting }}
</div>
<div ng-controller="GreetingController">
{{ greeting }}
</div>
```
**NOTE**: Although Angular allows you to create Controller functions in the global scope, this is
@@ -48,15 +60,15 @@ not recommended. In a real application you should use the `.controller` method
{@link module Angular Module} for your application as follows:
```js
var myApp = angular.module('myApp',[]);
var myApp = angular.module('myApp',[]);
myApp.controller('GreetingCtrl', ['$scope', function($scope) {
$scope.greeting = 'Hola!';
}]);
myApp.controller('GreetingController', ['$scope', function($scope) {
$scope.greeting = 'Hola!';
}]);
```
We have used an **inline injection annotation** to explicitly specify the dependency
of the Controller on the `$scope` service provided by Angular. See the guide on
of the Controller on the `$scope` service provided by Angular. See the guide on
[Dependency Injection](http://docs.angularjs.org/guide/di) for more information.
@@ -69,20 +81,20 @@ then available to be called from the template/view.
The following example uses a Controller to add a method to the scope, which doubles a number:
```js
var myApp = angular.module('myApp',[]);
var myApp = angular.module('myApp',[]);
myApp.controller('DoubleCtrl', ['$scope', function($scope) {
$scope.double = function(value) { return value * 2; };
}]);
myApp.controller('DoubleController', ['$scope', function($scope) {
$scope.double = function(value) { return value * 2; };
}]);
```
Once the Controller has been attached to the DOM, the `double` method can be invoked in an Angular
expression in the template:
```js
<div ng-controller="DoubleCtrl">
Two times <input ng-model="num"> equals {{ double(num) }}
</div>
<div ng-controller="DoubleController">
Two times <input ng-model="num"> equals {{ double(num) }}
</div>
```
As discussed in the {@link concepts Concepts} section of this guide, any
@@ -97,23 +109,9 @@ needed for a single view.
The most common way to keep Controllers slim is by encapsulating work that doesn't belong to
controllers into services and then using these services in Controllers via dependency injection.
This is discussed in the {@link di Dependency Injection} {@link dev_guide.services
This is discussed in the {@link di Dependency Injection} {@link services
Services} sections of this guide.
Do not use Controllers for:
- Any kind of DOM manipulation — Controllers should contain only business logic. DOM
manipulation (the presentation logic of an application) is well known for being hard to test.
Putting any presentation logic into Controllers significantly affects testability of the business
logic. Angular offers {@link databinding databinding} for automatic DOM manipulation. If
you have to perform your own manual DOM manipulation, encapsulate the presentation logic in
{@link guide/directive directives}.
- Input formatting — Use {@link forms angular form controls} instead.
- Output filtering — Use {@link guide/filter angular filters} instead.
- Sharing stateless or stateful code across Controllers — Use {@link dev_guide.services angular
services} instead.
- Managing the life-cycle of other components (for example, to create service instances).
# Associating Controllers with Angular Scope Objects
@@ -136,7 +134,7 @@ string "very". Depending on which button is clicked, the `spice` model is set to
<example module="spicyApp1">
<file name="index.html">
<div ng-controller="SpicyCtrl">
<div ng-controller="SpicyController">
<button ng-click="chiliSpicy()">Chili</button>
<button ng-click="jalapenoSpicy()">Jalapeño</button>
<p>The food is {{spice}} spicy!</p>
@@ -145,13 +143,13 @@ string "very". Depending on which button is clicked, the `spice` model is set to
<file name="app.js">
var myApp = angular.module('spicyApp1', []);
myApp.controller('SpicyCtrl', ['$scope', function($scope){
myApp.controller('SpicyController', ['$scope', function($scope) {
$scope.spice = 'very';
$scope.chiliSpicy = function() {
$scope.spice = 'chili';
};
$scope.jalapenoSpicy = function() {
$scope.spice = 'jalapeño';
};
@@ -162,9 +160,9 @@ string "very". Depending on which button is clicked, the `spice` model is set to
Things to notice in the example above:
- The `ng-controller` directive is used to (implicitly) create a scope for our template, and the
scope is augmented (managed) by the `SpicyCtrl` Controller.
- `SpicyCtrl` is just a plain JavaScript function. As an (optional) naming convention the name
starts with capital letter and ends with "Ctrl" or "Controller".
scope is augmented (managed) by the `SpicyController` Controller.
- `SpicyController` is just a plain JavaScript function. As an (optional) naming convention the name
starts with capital letter and ends with "Controller" or "Controller".
- Assigning a property to `$scope` creates or updates the model.
- Controller methods can be created through direct assignment to scope (see the `chiliSpicy` method)
- The Controller methods and properties are available in the template (for the `<div>` element and
@@ -177,7 +175,7 @@ previous example.
<example module="spicyApp2">
<file name="index.html">
<div ng-controller="SpicyCtrl">
<div ng-controller="SpicyController">
<input ng-model="customSpice">
<button ng-click="spicy('chili')">Chili</button>
<button ng-click="spicy(customSpice)">Custom spice</button>
@@ -187,25 +185,25 @@ previous example.
<file name="app.js">
var myApp = angular.module('spicyApp2', []);
myApp.controller('SpicyCtrl', ['$scope', function($scope){
myApp.controller('SpicyController', ['$scope', function($scope) {
$scope.customSpice = "wasabi";
$scope.spice = 'very';
$scope.spicy = function(spice){
$scope.spicy = function(spice) {
$scope.spice = spice;
};
}]);
</file>
</example>
Notice that the `SpicyCtrl` Controller now defines just one method called `spicy`, which takes one
Notice that the `SpicyController` Controller now defines just one method called `spicy`, which takes one
argument called `spice`. The template then refers to this Controller method and passes in a string
constant `'chili'` in the binding for the first button and a model property `customSpice` (bound to an
input box) in the second button.
## Scope Inheritance Example
It is common to attach Controllers at different levels of the DOM hierarchy. Since the
It is common to attach Controllers at different levels of the DOM hierarchy. Since the
{@link ng.directive:ngController ng-controller} directive creates a new child scope, we get a
hierarchy of scopes that inherit from each other. The `$scope` that each Controller receives will
have access to properties and methods defined by Controllers higher up the hierarchy.
@@ -215,13 +213,13 @@ more information about scope inheritance.
<example module="scopeInheritance">
<file name="index.html">
<div class="spicy">
<div ng-controller="MainCtrl">
<div ng-controller="MainController">
<p>Good {{timeOfDay}}, {{name}}!</p>
<div ng-controller="ChildCtrl">
<div ng-controller="ChildController">
<p>Good {{timeOfDay}}, {{name}}!</p>
<div ng-controller="GrandChildCtrl">
<div ng-controller="GrandChildController">
<p>Good {{timeOfDay}}, {{name}}!</p>
</div>
</div>
@@ -236,14 +234,14 @@ more information about scope inheritance.
</file>
<file name="app.js">
var myApp = angular.module('scopeInheritance', []);
myApp.controller('MainCtrl', ['$scope', function($scope){
myApp.controller('MainController', ['$scope', function($scope) {
$scope.timeOfDay = 'morning';
$scope.name = 'Nikki';
}]);
myApp.controller('ChildCtrl', ['$scope', function($scope){
myApp.controller('ChildController', ['$scope', function($scope) {
$scope.name = 'Mattie';
}]);
myApp.controller('GrandChildCtrl', ['$scope', function($scope){
myApp.controller('GrandChildController', ['$scope', function($scope) {
$scope.timeOfDay = 'evening';
$scope.name = 'Gingerbreak Baby';
}]);
@@ -254,11 +252,11 @@ Notice how we nested three `ng-controller` directives in our template. This will
scopes being created for our view:
- The root scope
- The `MainCtrl` scope, which contains `timeOfDay` and `name` properties
- The `ChildCtrl` scope, which inherits the `timeOfDay` property but overrides (hides) the `name`
- The `MainController` scope, which contains `timeOfDay` and `name` properties
- The `ChildController` scope, which inherits the `timeOfDay` property but overrides (hides) the `name`
property from the previous
- The `GrandChildCtrl` scope, which overrides (hides) both the `timeOfDay` property defined in `MainCtrl`
and the `name` property defined in `ChildCtrl`
- The `GrandChildController` scope, which overrides (hides) both the `timeOfDay` property defined in `MainController`
and the `name` property defined in `ChildController`
Inheritance works with methods in the same way as it does with properties. So in our previous
examples, all of the properties could be replaced with methods that return string values.
@@ -275,8 +273,8 @@ involves injecting the {@link ng.$rootScope $rootScope} and {@link ng.$controlle
myApp.controller('MyController', function($scope) {
$scope.spices = [{"name":"pasilla", "spiciness":"mild"},
{"name":"jalapeno", "spiceiness":"hot hot hot!"},
{"name":"habanero", "spiceness":"LAVA HOT!!"}];
{"name":"jalapeno", "spiciness":"hot hot hot!"},
{"name":"habanero", "spiciness":"LAVA HOT!!"}];
$scope.spice = "habanero";
});
```
@@ -318,11 +316,11 @@ describe('state', function() {
beforeEach(inject(function($rootScope, $controller) {
mainScope = $rootScope.$new();
$controller('MainCtrl', {$scope: mainScope});
$controller('MainController', {$scope: mainScope});
childScope = mainScope.$new();
$controller('ChildCtrl', {$scope: childScope});
$controller('ChildController', {$scope: childScope});
grandChildScope = childScope.$new();
$controller('GrandChildCtrl', {$scope: grandChildScope});
$controller('GrandChildController', {$scope: grandChildScope});
}));
it('should have over and selected', function() {
@@ -1,5 +1,5 @@
@ngdoc overview
@name Templates: Working With CSS in Angular
@name Working With CSS
@description
@@ -8,7 +8,7 @@ Angular sets these CSS classes. It is up to your application to provide useful s
# CSS classes used by angular
* `ng-scope`
- **Usage:** angular applies this class to any element that where a new {@link ng.$rootScope.Scope scope}
- **Usage:** angular applies this class to any element for which a new {@link api/ng.$rootScope.Scope scope}
is defined. (see {@link guide/scope scope} guide for more information about scopes)
* `ng-binding`
+7 -8
View File
@@ -2,7 +2,7 @@
@name Data Binding
@description
Data-binding in Angular web apps is the automatic synchronization of data between the model and view
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.
When the model changes, the view reflects the change, and vice versa.
@@ -11,7 +11,7 @@ When the model changes, the view reflects the change, and vice versa.
<img class="right" src="img/One_Way_Data_Binding.png"/>
Most templating systems bind data in only one direction: they merge template and model components
together into a view, as illustrated in the diagram. After the merge occurs, changes to the model
together into a view. After the merge occurs, changes to the model
or related sections of the view are NOT automatically reflected in the view. Worse, any changes
that the user makes to the view are not reflected in the model. This means that the developer has
to write code that constantly syncs the view with the model and the model with the view.
@@ -19,12 +19,11 @@ to write code that constantly syncs the view with the model and the model with t
## Data Binding in Angular Templates
<img class="right" src="img/Two_Way_Data_Binding.png"/>
The way Angular templates works is different, as illustrated in the diagram. They are different
because first the template (which is the uncompiled HTML along with any additional markup or
directives) is compiled on the browser, and second, the compilation step produces a live view. We
say live because any changes to the view are immediately reflected in the model, and any changes in
the model are propagated to the view. This makes the model always the single-source-of-truth for
the application state, greatly simplifying the programming model for the developer. You can think of
Angular templates work differently. First the template (which is the uncompiled HTML along with
any additional markup or directives) is compiled on the browser. The compilation step produces a
live view. Any changes to the view are immediately reflected in the model, and any changes in
the model are propagated to the view. The model is the single-source-of-truth for the application
state, greatly simplifying the programming model for the developer. You can think of
the view as simply an instant projection of your model.
Because the view is just a projection of the model, the controller is completely separated from the
@@ -1,313 +0,0 @@
@workInProgress
@ngdoc overview
@name E2E Testing
@description
**Angular Scenario Runner is in maintenance mode - If you're starting a new Angular project,
consider using [Protractor](https://github.com/angular/protractor).**
As applications grow in size and complexity, it becomes unrealistic to rely on manual testing to
verify the correctness of new features, catch bugs and notice regressions.
To solve this problem, we have built an Angular Scenario Runner which simulates user interactions
that will help you verify the health of your Angular application.
# Overview
You will write scenario tests in JavaScript, which describe how your application should behave,
given a certain interaction in a specific state. A scenario is comprised of one or more `it` blocks
(you can think of these as the requirements of your application), which in turn are made of
**commands** and **expectations**. Commands tell the Runner to do something with the application
(such as navigate to a page or click on a button), and expectations tell the Runner to assert
something about the state (such as the value of a field or the current URL). If any expectation
fails, the runner marks the `it` as "failed" and continues on to the next one. Scenarios may also
have **beforeEach** and **afterEach** blocks, which will be run before (or after) each `it` block,
regardless of whether they pass or fail.
<img src="img/guide/scenario_runner.png">
In addition to the above elements, scenarios may also contain helper functions to avoid duplicating
code in the `it` blocks.
Here is an example of a simple scenario:
```js
describe('Buzz Client', function() {
it('should filter results', function() {
input('user').enter('jacksparrow');
element(':button').click();
expect(repeater('ul li').count()).toEqual(10);
input('filterText').enter('Bees');
expect(repeater('ul li').count()).toEqual(1);
});
});
```
Note that
[`input('user')`](https://github.com/angular/angular.js/blob/master/docs/content/guide/dev_guide.e2e-testing.ngdoc#L119)
finds the `<input>` element with `ng-model="user"` not `name="user"`.
This scenario describes the requirements of a Buzz Client, specifically, that it should be able to
filter the stream of the user. It starts by entering a value in the input field with ng-model="user", clicking
the only button on the page, and then it verifies that there are 10 items listed. It then enters
'Bees' in the input field with ng-model='filterText' and verifies that the list is reduced to a single item.
The API section below lists the available commands and expectations for the Runner.
# API
Source: https://github.com/angular/angular.js/blob/master/src/ngScenario/dsl.js
## pause()
Pauses the execution of the tests until you call `resume()` in the console (or click the resume
link in the Runner UI).
## sleep(seconds)
Pauses the execution of the tests for the specified number of `seconds`.
## browser().navigateTo(url)
Loads the `url` into the test frame.
## browser().navigateTo(url, fn)
Loads the URL returned by `fn` into the testing frame. The given `url` is only used for the test
output. Use this when the destination URL is dynamic (that is, the destination is unknown when you
write the test).
## browser().reload()
Refreshes the currently loaded page in the test frame.
## browser().window().href()
Returns the window.location.href of the currently loaded page in the test frame.
## browser().window().path()
Returns the window.location.pathname of the currently loaded page in the test frame.
## browser().window().search()
Returns the window.location.search of the currently loaded page in the test frame.
## browser().window().hash()
Returns the window.location.hash (without `#`) of the currently loaded page in the test frame.
## browser().location().url()
Returns the {@link ng.$location $location.url()} of the currently loaded page in
the test frame.
## browser().location().path()
Returns the {@link ng.$location $location.path()} of the currently loaded page in
the test frame.
## browser().location().search()
Returns the {@link ng.$location $location.search()} of the currently loaded page
in the test frame.
## browser().location().hash()
Returns the {@link ng.$location $location.hash()} of the currently loaded page in
the test frame.
## expect(future).{matcher}
Asserts the value of the given `future` satisfies the `matcher`. All API statements return a
`future` object, which get a `value` assigned after they are executed. Matchers are defined using
`angular.scenario.matcher`, and they use the value of futures to run the expectation. For example:
`expect(browser().location().href()).toEqual('http://www.google.com')`. Available matchers
are presented further down this document.
## expect(future).not().{matcher}
Asserts the value of the given `future` satisfies the negation of the `matcher`.
## using(selector, label)
Scopes the next DSL element selection.
## binding(name)
Returns the value of the first binding matching the given `name`.
## input(name).enter(value)
Enters the given `value` in the text field with the corresponding ng-model `name`.
## input(name).check()
Checks/unchecks the checkbox with the corresponding ng-model `name`.
## input(name).select(value)
Selects the given `value` in the radio button with the corresponding ng-model `name`.
## input(name).val()
Returns the current value of an input field with the corresponding ng-model `name`.
## repeater(selector, label).count()
Returns the number of rows in the repeater matching the given jQuery `selector`. The `label` is
used for test output.
## repeater(selector, label).row(index)
Returns an array with the bindings in the row at the given `index` in the repeater matching the
given jQuery `selector`. The `label` is used for test output.
## repeater(selector, label).column(binding)
Returns an array with the values in the column with the given `binding` in the repeater matching
the given jQuery `selector`. The `label` is used for test output.
## select(name).option(value)
Picks the option with the given `value` on the select with the given ng-model `name`.
## select(name).options(value1, value2...)
Picks the options with the given `values` on the multi select with the given ng-model `name`.
## element(selector, label).count()
Returns the number of elements that match the given jQuery `selector`. The `label` is used for test
output.
## element(selector, label).click()
Clicks on the element matching the given jQuery `selector`. The `label` is used for test output.
## element(selector, label).query(fn)
Executes the function `fn(selectedElements, done)`, where selectedElements are the elements that
match the given jQuery `selector` and `done` is a function that is called at the end of the `fn`
function. The `label` is used for test output.
## element(selector, label).{method}()
Returns the result of calling `method` on the element matching the given jQuery `selector`, where
`method` can be any of the following jQuery methods: `val`, `text`, `html`, `height`,
`innerHeight`, `outerHeight`, `width`, `innerWidth`, `outerWidth`, `position`, `scrollLeft`,
`scrollTop`, `offset`. The `label` is used for test output.
## element(selector, label).{method}(value)
Executes the `method` passing in `value` on the element matching the given jQuery `selector`, where
`method` can be any of the following jQuery methods: `val`, `text`, `html`, `height`,
`innerHeight`, `outerHeight`, `width`, `innerWidth`, `outerWidth`, `position`, `scrollLeft`,
`scrollTop`, `offset`. The `label` is used for test output.
## element(selector, label).{method}(key)
Returns the result of calling `method` passing in `key` on the element matching the given jQuery
`selector`, where `method` can be any of the following jQuery methods: `attr`, `prop`, `css`. The
`label` is used for test output.
## element(selector, label).{method}(key, value)
Executes the `method` passing in `key` and `value` on the element matching the given jQuery
`selector`, where `method` can be any of the following jQuery methods: `attr`, `prop`, `css`. The
`label` is used for test output.
# Matchers
Matchers are used in combination with the `expect(...)` function as described above and can
be negated with `not()`. For instance: `expect(element('h1').text()).not().toEqual('Error')`.
Source: https://github.com/angular/angular.js/blob/master/src/ngScenario/matchers.js
```js
// value and Object comparison following the rules of angular.equals().
expect(value).toEqual(value)
// a simpler value comparison using ===
expect(value).toBe(value)
// checks that the value is defined by checking its type.
expect(value).toBeDefined()
// the following two matchers are using JavaScript's standard truthiness rules
expect(value).toBeTruthy()
expect(value).toBeFalsy()
// verify that the value matches the given regular expression. The regular
// expression may be passed in form of a string or a regular expression
// object.
expect(value).toMatch(expectedRegExp)
// a check for null using ===
expect(value).toBeNull()
// Array.indexOf(...) is used internally to check whether the element is
// contained within the array.
expect(value).toContain(expected)
// number comparison using < and >
expect(value).toBeLessThan(expected)
expect(value).toBeGreaterThan(expected)
```
# Example
See the [angular-seed](https://github.com/angular/angular-seed) project for more examples.
## Conditional actions with element(...).query(fn)
E2E testing with angular scenario is highly asynchronous and hides a lot of complexity by
queueing actions and expectations that can handle futures. From time to time, you might need
conditional assertions or element selection. Even though you should generally try to avoid this
(as it is can be sign for unstable tests), you can add conditional behavior with
`element(...).query(fn)`. The following code listing shows how this function can be used to delete
added entries (where an entry is some domain object) using the application's web interface.
Imagine the application to be structured into two views:
1. *Overview view* which lists all the added entries in a table and
2. a *detail view* which shows the entries' details and contains a delete button. When clicking the
delete button, the user is redirected back to the *overview page*.
```js
beforeEach(function () {
var deleteEntry = function () {
browser().navigateTo('/entries');
// we need to select the <tbody> element as it might be the case that there
// are no entries (and therefore no rows). When the selector does not
// result in a match, the test would be marked as a failure.
element('table tbody').query(function (tbody, done) {
// ngScenario gives us a jQuery lite wrapped element. We call the
// `children()` function to retrieve the table body's rows
var children = tbody.children();
if (children.length > 0) {
// if there is at least one entry in the table, click on the link to
// the entry's detail view
element('table tbody a').click();
// and, after a route change, click the delete button
element('.btn-danger').click();
}
// if there is more than one entry shown in the table, queue another
// delete action.
if (children.length > 1) {
deleteEntry();
}
// remember to call `done()` so that ngScenario can continue
// test execution.
done();
});
};
// start deleting entries
deleteEntry();
});
```
In order to understand what is happening, we should emphasize that ngScenario calls are not
immediately executed, but queued (in ngScenario terms, we would be talking about adding
future actions). If we had only one entry in our table, then the following future actions
would be queued:
```js
// delete entry 1
browser().navigateTo('/entries');
element('table tbody').query(function (tbody, done) { ... });
element('table tbody a');
element('.btn-danger').click();
```
For two entries, ngScenario would have to work on the following queue:
```js
// delete entry 1
browser().navigateTo('/entries');
element('table tbody').query(function (tbody, done) { ... });
element('table tbody a');
element('.btn-danger').click();
// delete entry 2
// indented to represent "recursion depth"
browser().navigateTo('/entries');
element('table tbody').query(function (tbody, done) { ... });
element('table tbody a');
element('.btn-danger').click();
```
# Caveats
ngScenario does not work with apps that manually bootstrap using angular.bootstrap. You must use the ng-app directive.
@@ -1,106 +0,0 @@
@ngdoc overview
@name Angular Services: Creating Services
@description
While Angular offers several useful services, for any nontrivial application you'll find it useful
to write your own custom services. To do this you begin by registering a service factory function
with a module either via the {@link angular.module Module#factory api} or directly
via the {@link auto.$provide $provide} api inside of module config function.
All Angular services participate in {@link di dependency injection (DI)} by registering
themselves with Angular's DI system (injector) under a `name` (id) as well as by declaring
dependencies which need to be provided for the factory function of the registered service. The
ability to swap dependencies for mocks/stubs/dummies in tests allows for services to be highly
testable.
# Registering Services
To register a service, you must have a module that this service will be part of. Afterwards, you
can register the service with the module either via the {@link angular.Module Module api} or
by using the {@link auto.$provide $provide} service in the module configuration
function. The following pseudo-code shows both approaches:
Using the angular.Module api:
```js
var myModule = angular.module('myModule', []);
myModule.factory('serviceId', function() {
var shinyNewServiceInstance;
//factory function body that constructs shinyNewServiceInstance
return shinyNewServiceInstance;
});
```
Using the $provide service:
```js
angular.module('myModule', [], function($provide) {
$provide.factory('serviceId', function() {
var shinyNewServiceInstance;
//factory function body that constructs shinyNewServiceInstance
return shinyNewServiceInstance;
});
});
```
Note that you are not registering a service instance, but rather a factory function that will
create this instance when called.
# Dependencies
Services can not only be depended upon, but can also have their own dependencies. These can be specified
as arguments of the factory function. {@link di Read more} about dependency injection (DI)
in Angular and the use of array notation and the $inject property to make DI annotation
minification-proof.
Following is an example of a very simple service. This service depends on the `$window` service
(which is passed as a parameter to the factory function) and is just a function. The service simply
stores all notifications; after the third one, the service displays all of the notifications by
window alert.
```js
angular.module('myModule', [], function($provide) {
$provide.factory('notify', ['$window', function(win) {
var msgs = [];
return function(msg) {
msgs.push(msg);
if (msgs.length == 3) {
win.alert(msgs.join("\n"));
msgs = [];
}
};
}]);
});
```
# Instantiating Angular Services
All services in Angular are instantiated lazily. This means that a service will be created
only when it is needed for instantiation of a service or an application component that depends on it.
In other words, Angular won't instantiate services unless they are requested directly or
indirectly by the application.
# Services as singletons
Lastly, it is important to realize that all Angular services are application singletons. This means
that there is only one instance of a given service per injector. Since Angular is lethally allergic
to global state, it is possible to create multiple injectors, each with its own instance of a
given service, but that is rarely needed, except in tests where this property is crucially
important.
## Related Topics
* {@link dev_guide.services.understanding_services Understanding Angular Services}
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
* {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers }
* {@link dev_guide.services.testing_services Testing Angular Services}
## Related API
* {@link ng Angular Service API}
@@ -1,123 +0,0 @@
@ngdoc overview
@name Angular Services: Injecting Services Into Controllers
@description
Using services as dependencies for controllers is very similar to using services as dependencies
for another service.
Since JavaScript is a dynamic language, DI can't figure out which services to inject by static
types (like in static typed languages). Therefore, you can specify the service name by using the
`$inject` property, which is an array containing strings with names of services to be injected.
The name must match the corresponding service ID registered with angular. The order of the service
IDs matters: the order of the services in the array will be used when calling the factory function
with injected parameters. The names of parameters in factory function don't matter, but by
convention they match the service IDs, which has added benefits discussed below.
```js
function myController($loc, $log) {
this.firstMethod = function() {
// use $location service
$loc.setHash();
};
this.secondMethod = function() {
// use $log service
$log.info('...');
};
}
// which services to inject ?
myController.$inject = ['$location', '$log'];
```
<example module="MyServiceModule">
<file name="index.html">
<div id="simple" ng-controller="myController">
<p>Let's try this simple notify service, injected into the controller...</p>
<input ng-init="message='test'" ng-model="message" >
<button ng-click="callNotify(message);">NOTIFY</button>
<p>(you have to click 3 times to see an alert)</p>
</div>
</file>
<file name="script.js">
angular.
module('MyServiceModule', []).
factory('notify', ['$window', function(win) {
var msgs = [];
return function(msg) {
msgs.push(msg);
if (msgs.length == 3) {
win.alert(msgs.join("\n"));
msgs = [];
}
};
}]);
function myController(scope, notifyService) {
scope.callNotify = function(msg) {
notifyService(msg);
};
}
myController.$inject = ['$scope','notify'];
</file>
<file name="protractor.js" type="protractor">
it('should test service', function() {
expect(element(by.id('simple')).element(by.model('message')).getAttribute('value'))
.toEqual('test');
});
</file>
</example>
## Implicit Dependency Injection
A new feature of Angular DI allows it to determine the dependency from the name of the parameter.
Let's rewrite the above example to show the use of this implicit dependency injection of
`$window`, `$scope`, and our `notify` service:
<example module="MyServiceModuleDI">
<file name="index.html">
<div id="implicit" ng-controller="myController">
<p>Let's try the notify service, that is implicitly injected into the controller...</p>
<input ng-init="message='test'" ng-model="message">
<button ng-click="callNotify(message);">NOTIFY</button>
<p>(you have to click 3 times to see an alert)</p>
</div>
</file>
<file name="script.js">
angular.
module('MyServiceModuleDI', []).
factory('notify', function($window) {
var msgs = [];
return function(msg) {
msgs.push(msg);
if (msgs.length == 3) {
$window.alert(msgs.join("\n"));
msgs = [];
}
};
});
function myController($scope, notify) {
$scope.callNotify = function(msg) {
notify(msg);
};
}
</file>
</example>
However, if you plan to [minify](http://en.wikipedia.org/wiki/Minification_(programming)) your
code, your variable names will get renamed in which case you will still need to explicitly specify
dependencies with the `$inject` property.
## Related Topics
{@link dev_guide.services.understanding_services Understanding Angular Services}
{@link dev_guide.services.creating_services Creating Angular Services}
{@link dev_guide.services.managing_dependencies Managing Service Dependencies}
{@link dev_guide.services.testing_services Testing Angular Services}
## Related API
{@link ng Angular Service API}
@@ -1,113 +0,0 @@
@ngdoc overview
@name Angular Services: Managing Service Dependencies
@description
Angular allows services to declare other services as dependencies needed for construction of their
instances.
To declare dependencies, you specify them in the factory function signature and annotate the
function with the inject annotations either using by setting the `$inject` property, as an array of
string identifiers or using the array notation. Optionally the `$inject` property declaration can be
dropped (see "Inferring `$inject`" but note that that is currently an experimental feature).
Using the array notation:
```js
function myModuleCfgFn($provide) {
$provide.factory('myService', ['dep1', 'dep2', function(dep1, dep2) {}]);
}
```
Using the $inject property:
```js
function myModuleCfgFn($provide) {
var myServiceFactory = function(dep1, dep2) {};
myServiceFactory.$inject = ['dep1', 'dep2'];
$provide.factory('myService', myServiceFactory);
}
```
Using DI inference (incompatible with minifiers):
```js
function myModuleCfgFn($provide) {
$provide.factory('myService', function(dep1, dep2) {});
}
```
Here is an example of two services, one of which depends on the other and both
of which depend on other services that are provided by the Angular framework:
```js
/**
* batchLog service allows for messages to be queued in memory and flushed
* to the console.log every 50 seconds.
*
* @param {*} message Message to be logged.
*/
function batchLogModule($provide){
$provide.factory('batchLog', ['$interval', '$log', function($interval, $log) {
var messageQueue = [];
function log() {
if (messageQueue.length) {
$log.log('batchLog messages: ', messageQueue);
messageQueue = [];
}
}
// start periodic checking
$interval(log, 50000);
return function(message) {
messageQueue.push(message);
}
}]);
/**
* routeTemplateMonitor monitors each $route change and logs the current
* template via the batchLog service.
*/
$provide.factory('routeTemplateMonitor',
['$route', 'batchLog', '$rootScope',
function($route, batchLog, $rootScope) {
$rootScope.$on('$routeChangeSuccess', function() {
batchLog($route.current ? $route.current.template : null);
});
}]);
}
// get the main service to kick off the application
angular.injector([batchLogModule]).get('routeTemplateMonitor');
```
Things to notice in this example:
* The `batchLog` service depends on the built-in {@link ng.$interval $interval} and
{@link ng.$log $log} services, and allows messages to be logged into the
`console.log` in batches.
* The `routeTemplateMonitor` service depends on the built-in {@link ngRoute.$route
$route} service as well as our custom `batchLog` service.
* Both of our services use the factory function signature and array notation for inject annotations
to declare their dependencies. It is important that the order of the string identifiers in the array
is the same as the order of argument names in the signature of the factory function. Unless the
dependencies are inferred from the function signature, it is this array with IDs and their order
that the injector uses to determine which services and in which order to inject.
## Related Topics
* {@link dev_guide.services.understanding_services Understanding Angular Services}
* {@link dev_guide.services.creating_services Creating Angular Services}
* {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers }
* {@link dev_guide.services.testing_services Testing Angular Services}
## Related API
* {@link ./ng Angular Service API}
* {@link angular.injector Angular Injector API}
@@ -1,20 +0,0 @@
@ngdoc overview
@name Angular Services
@description
Services are a feature that Angular brings to client-side web apps from the server side, where
services have been commonly used for a long time. Services in Angular apps are substitutable
objects that are wired together using {@link di dependency injection (DI)}.
## Related Topics
* {@link dev_guide.services.understanding_services Understanding Angular Services}
* {@link dev_guide.services.creating_services Creating Angular Services}
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
* {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers}
* {@link dev_guide.services.testing_services Testing Angular Services}
## Related API
* {@link ./ng Angular Service API}
@@ -1,62 +0,0 @@
@ngdoc overview
@name Angular Services: Testing Angular Services
@description
The following is a unit test for the 'notify' service in the 'Dependencies' example in {@link
dev_guide.services.creating_services Creating Angular Services}. The unit test example uses Jasmine
spy (mock) instead of a real browser alert.
```js
var mock, notify;
beforeEach(function() {
mock = {alert: jasmine.createSpy()};
module(function($provide) {
$provide.value('$window', mock);
});
inject(function($injector) {
notify = $injector.get('notify');
});
});
it('should not alert first two notifications', function() {
notify('one');
notify('two');
expect(mock.alert).not.toHaveBeenCalled();
});
it('should alert all after third notification', function() {
notify('one');
notify('two');
notify('three');
expect(mock.alert).toHaveBeenCalledWith("one\ntwo\nthree");
});
it('should clear messages after alert', function() {
notify('one');
notify('two');
notify('third');
notify('more');
notify('two');
notify('third');
expect(mock.alert.callCount).toEqual(2);
expect(mock.alert.mostRecentCall.args).toEqual(["more\ntwo\nthird"]);
});
```
## Related Topics
* {@link dev_guide.services.understanding_services Understanding Angular Services}
* {@link dev_guide.services.creating_services Creating Angular Services}
* {@link dev_guide.services.managing_dependencies Managing Service Dependencies}
* {@link dev_guide.services.injecting_controllers Injecting Services Into Controllers}
## Related API
* {@link ./ng Angular Service API}
@@ -1,60 +0,0 @@
@ngdoc overview
@name Angular Services: Understanding Angular Services
@description
## What are Angular Services?
Angular services are singletons objects or functions that carry out specific tasks common to web apps.
Angular has a number of built in services, such as the {@link ng.$http $http service}, which
provides access to the browser's `XMLHttpRequest` object for making requests to a server. Like other core
Angular variables and identifiers, the built-in services always start with `$` (such as `$http` mentioned
above). You can also create your own custom services.
## Using a Service
To use an Angular service, you identify it as a dependency for the component (controller, service,
filter or directive) that depends on the service. Angular's dependency injection subsystem takes
care of the rest. The Angular injector subsystem is in charge of service instantiation, resolution
of dependencies, and provision of dependencies to components as requested.
Angular injects dependencies using
["constructor" injection](http://misko.hevery.com/2009/02/19/constructor-injection-vs-setter-injection/).
The dependency is passed to the component's factory/constructor function. Because JavaScript is a dynamically
typed language, Angular's dependency injection subsystem cannot use static types to identify service
dependencies. For this reason a component must, explicitly, define its dependencies by using one of the
{@link di injection annotation} methods. For example, by providing a `$inject` property:
var MyController = function($location) { ... };
MyController.$inject = ['$location'];
myModule.controller('MyController', MyController);
Or by providing an "inline" injection annotation:
var myService = function($http) { ... };
myModule.factory('myService', ['$http', myService]);
## Defining a Service
Application developers are free to define their own services by registering their name, and **service
factory function**, in Angular modules.
The purpose of the **service factory function** is to generate the single object, or function, that
represents the service to the rest of the application. That object, or function, will then be
injected into any component (controller, service, filter or directive) that specifies a dependency
on the service.
Angular factory functions are executed lazily. That is, they are only executed when needed
to satisfy a dependency, and are then executed exactly once for each service. Everything that is
dependent on this service gets a reference to the single instance generated by the service factory.
## Related Topics
* {@link guide/di About Angular Dependency Injection}
* {@link guide/dev_guide.services.creating_services Creating Angular Services}
* {@link guide/dev_guide.services.managing_dependencies Managing Service Dependencies}
* {@link guide/dev_guide.services.testing_services Testing Angular Services}
## Related API
* {@link ./ng Angular Service API}
* {@link angular.injector Injector API}
+185 -131
View File
@@ -4,45 +4,45 @@
# Dependency Injection
Dependency Injection (DI) is a software design pattern that deals with how code gets hold of its
dependencies.
Dependency Injection (DI) is a software design pattern that deals with how components get hold of
their dependencies.
The Angular injector subsystem is in charge of creating components, resolving their dependencies,
and providing them to other components as requested.
For in-depth discussion about DI, see
[Dependency Injection](http://en.wikipedia.org/wiki/Dependency_injection) at Wikipedia,
[Inversion of Control](http://martinfowler.com/articles/injection.html) by Martin Fowler,
or read about DI in your favorite software design pattern book.
## DI in a nutshell
## DI in a Nutshell
There are only three ways an object or a function can get a hold of its dependencies:
1. The dependency can be created, typically using the `new` operator.
2. The dependency can be looked up by referring to a global variable.
3. The dependency can be passed in to where it is needed.
There are only three ways a component (object or function) can get a hold of its dependencies:
1. The component can create the dependency, typically using the `new` operator.
2. The component can look up the dependency, by referring to a global variable.
3. The component can have the dependency passed to it where it is needed.
The first two options of creating or looking up dependencies are not optimal because they hard
code the dependency. This makes it difficult, if not impossible, to modify the dependencies.
This is especially problematic in tests, where it is often desirable to provide mock dependencies
for test isolation.
code the dependency to the component. This makes it difficult, if not impossible, to modify the
dependencies. This is especially problematic in tests, where it is often desirable to provide mock
dependencies for test isolation.
The third option is the most viable, since it removes the responsibility of locating the
dependency from the component. The dependency is simply handed to the component.
```js
function SomeClass(greeter) {
this.greeter = greeter;
}
SomeClass.prototype.doSomething = function(name) {
this.greeter.greet(name);
}
function SomeClass(greeter) {
this.greeter = greeter;
}
SomeClass.prototype.doSomething = function(name) {
this.greeter.greet(name);
}
```
In the above example `SomeClass` is not concerned with locating the `greeter` dependency, it
is simply handed the `greeter` at runtime.
In the above example `SomeClass` is not concerned with creating or locating the `greeter`
dependency, it is simply handed the `greeter` when it is instantiated.
This is desirable, but it puts the responsibility of getting hold of the dependency on the
code that constructs `SomeClass`.
@@ -50,82 +50,98 @@ code that constructs `SomeClass`.
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-module-injector.png">
To manage the responsibility of dependency creation, each Angular application has an {@link
angular.injector injector}. The injector is a service locator that is responsible for
angular.injector injector}. The injector is a
[service locator](http://en.wikipedia.org/wiki/Service_locator_pattern) that is responsible for
construction and lookup of dependencies.
Here is an example of using the injector service:
```js
// Provide the wiring information in a module
angular.module('myModule', []).
// Teach the injector how to build a 'greeter'
// Notice that greeter itself is dependent on '$window'
factory('greeter', function($window) {
// This is a factory function, and is responsible for
// creating the 'greet' service.
return {
greet: function(text) {
$window.alert(text);
}
};
});
// Provide the wiring information in a module
var myModule = angular.module('myModule', []);
```
// New injector is created from the module.
// (This is usually done automatically by angular bootstrap)
var injector = angular.injector(['myModule', 'ng']);
// Request any dependency from the injector
var greeter = injector.get('greeter');
Teach the injector how to build a `greeter` service. Notice that `greeter` is dependent on the
`$window` service. The `greeter` service is an object that contains a `greet` method.
```js
myModule.factory('greeter', function($window) {
return {
greet: function(text) {
$window.alert(text);
}
};
});
```
Create a new injector that can provide components defined in our `myModule` module and request our
`greeter` service from the injector. (This is usually done automatically by angular bootstrap).
```js
var injector = angular.injector(['myModule', 'ng']);
var greeter = injector.get('greeter');
```
Asking for dependencies solves the issue of hard coding, but it also means that the injector needs
to be passed throughout the application. Passing the injector breaks the [Law of Demeter](http://en.wikipedia.org/wiki/Law_of_Demeter). To remedy this, we turn the
dependency lookup responsibility to the injector by declaring the dependencies as in this example:
to be passed throughout the application. Passing the injector breaks the
[Law of Demeter](http://en.wikipedia.org/wiki/Law_of_Demeter). To remedy this, we use a declarative
notation in our HTML templates, to hand the responsibility of creating components over to the
injector, as in this example:
```html
<!-- Given this HTML -->
<div ng-controller="MyController">
<button ng-click="sayHello()">Hello</button>
</div>
```
```js
// And this controller definition
function MyController($scope, greeter) {
$scope.sayHello = function() {
greeter.greet('Hello World');
};
}
// The 'ng-controller' directive does this behind the scenes
injector.instantiate(MyController);
<div ng-controller="MyController">
<button ng-click="sayHello()">Hello</button>
</div>
```
Notice that by having the `ng-controller` instantiate the class, it can satisfy all of the
dependencies of `MyController` without the controller ever knowing about the injector. This is
the best outcome. The application code simply asks for the dependencies it needs, without having to
deal with the injector. This setup does not break the Law of Demeter.
```js
function MyController($scope, greeter) {
$scope.sayHello = function() {
greeter.greet('Hello World');
};
}
```
When Angular compiles the HTML, it processes the `ng-controller` directive, which in turn
asks the injector to create an instance of the controller and its dependencies.
```js
injector.instantiate(MyController);
```
This is all done behinds the scenes. Notice that by having the `ng-controller` ask the injector to
instantiate the class, it can satisfy all of the dependencies of `MyController` without the
controller ever knowing about the injector.
This is the best outcome. The application code simply declares the dependencies it needs, without
having to deal with the injector. This setup does not break the Law of Demeter.
## Dependency Annotation
How does the injector know what service needs to be injected?
**How does the injector know what components need to be injected?**
The application developer needs to provide annotation information that the injector uses in order
to resolve the dependencies. Throughout Angular, certain API functions are invoked using the
injector, as per the API documentation. The injector needs to know what services to inject into
the function. Below are three equivalent ways of annotating your code with service name
information. These can be used interchangeably as you see fit and are equivalent.
the function. There are three equivalent ways of annotating your code with service name
information:
### Inferring Dependencies
- Implicitly from the function parameter names
- Using the `$inject` property annotation
- Using the inline array annotation
These can be used interchangeably as you see fit and are equivalent.
### Implicit Dependencies
The simplest way to get hold of the dependencies, is to assume that the function parameter names
are the names of the dependencies.
```js
function MyController($scope, greeter) {
...
}
function MyController($scope, greeter) {
// ...
}
```
Given a function the injector can infer the names of the service to inject by examining the
@@ -133,108 +149,146 @@ function declaration and extracting the parameter names. In the above example `$
`greeter` are two services which need to be injected into the function.
While straightforward, this method will not work with JavaScript minifiers/obfuscators as they
rename the method parameter names. This makes this way of annotating only useful for [pretotyping](http://www.pretotyping.org/), and demo applications.
rename the method parameter names. This makes this way of annotating only useful for
[pretotyping](http://www.pretotyping.org/), and demo applications.
### `$inject` Annotation
### `$inject` Property Annotation
To allow the minifers to rename the function parameters and still be able to inject right services
To allow the minifiers to rename the function parameters and still be able to inject right services,
the function needs to be annotated with the `$inject` property. The `$inject` property is an array
of service names to inject.
```js
var MyController = function(renamed$scope, renamedGreeter) {
...
}
MyController['$inject'] = ['$scope', 'greeter'];
var MyController = function(renamed$scope, renamedGreeter) {
...
}
MyController['$inject'] = ['$scope', 'greeter'];
```
In this scenario the ordering of the values in the '$inject' array must match the ordering of the arguments to inject.
Using above code snippet as an example, '$scope' will be injected into 'renamed$scope' and 'greeter' into 'renamedGreeter'.
Care must be taken that the `$inject` annotation is kept in sync with the actual arguments in the
function declaration.
In this scenario the ordering of the values in the `$inject` array must match the ordering of the
arguments to inject. Using above code snippet as an example, `$scope` will be injected into
`renamed$scope` and `greeter` into `renamedGreeter`. Care must be taken that the `$inject`
annotation is kept in sync with the actual arguments in the function declaration.
This method of annotation is useful for controller declarations since it assigns the annotation
information with the function.
### Inline Annotation
### Inline Array Annotation
Sometimes using the `$inject` annotation style is not convenient such as when annotating
directives.
directives or services defined inline by a factory function.
For example:
```js
someModule.factory('greeter', function($window) {
...
});
someModule.factory('greeter', function($window) {
// ...
});
```
Results in code bloat due to needing a temporary variable:
```js
var greeterFactory = function(renamed$window) {
...
};
greeterFactory.$inject = ['$window'];
someModule.factory('greeter', greeterFactory);
var greeterFactory = function(renamed$window) {
// ...
};
greeterFactory.$inject = ['$window'];
someModule.factory('greeter', greeterFactory);
```
For this reason the third annotation style is provided as well.
```js
someModule.factory('greeter', ['$window', function(renamed$window) {
...
}]);
someModule.factory('greeter', ['$window', function(renamed$window) {
// ...
}]);
```
Here, instead of simply providing the factory function, we pass an array, whose elements consist of
a list of strings (the names of the dependencies) followed by the function itself.
Keep in mind that all of the annotation styles are equivalent and can be used anywhere in Angular
where injection is supported.
## Where can I use DI?
## Where Can I Use DI?
DI is pervasive throughout Angular. It is typically used in controllers and factory methods.
DI is pervasive throughout Angular. You can use it when defining components or when providing `run`
and `config` blocks for a module.
### DI in controllers
- Components such as services, directives, filters and animations are defined by an injectable factory
method or constructor function. These components can be injected with "service" components as
dependencies.
Controllers are classes which are responsible for application behavior. The recommended way of
declaring controllers is using the array notation:
- The `run` and `config` methods accept a function, which can also be injected with "service"
components as dependencies.
```js
someModule.controller('MyController', ['$scope', 'dep1', 'dep2', function($scope, dep1, dep2) {
...
$scope.aMethod = function() {
...
}
...
}]);
```
This avoids the creation of global functions for controllers and also protects against minification.
- Controllers are defined by a constructor function, which can be injected with any of the "service"
components as dependencies, but they can also be provided with special dependencies. See "DI in
Controllers" below.
### Factory methods
### Factory Methods
Factory methods are responsible for creating most objects in Angular. Examples are directives,
services, and filters. The factory methods are registered with the module, and the recommended way
of declaring factories is:
```js
angular.module('myModule', []).
config(['depProvider', function(depProvider){
...
}]).
factory('serviceId', ['depService', function(depService) {
...
}]).
directive('directiveName', ['depService', function(depService) {
...
}]).
filter('filterName', ['depService', function(depService) {
...
}]).
run(['depService', function(depService) {
...
}]);
angular.module('myModule', [])
.factory('serviceId', ['depService', function(depService) {
...
}])
.directive('directiveName', ['depService', function(depService) {
...
}])
.filter('filterName', ['depService', function(depService) {
...
}]);
```
### Module Methods
We can specify functions to run at configuration and run time for a module by calling the `run` and
`config` methods. These functions are injectable with dependencies just like the factory functions
above.
```js
angular.module('myModule', [])
.config(['depProvider', function(depProvider){
...
}])
.run(['depService', function(depService) {
...
}]);
```
### Controllers
Controllers are "classes" or "constructor functions" that are responsible for providing the
application behavior that supports the declarative markup in the template. The recommended way of
declaring Controllers is using the array notation:
```js
someModule.controller('MyController', ['$scope', 'dep1', 'dep2', function($scope, dep1, dep2) {
...
$scope.aMethod = function() {
...
}
...
}]);
```
This avoids the creation of global functions for controllers and also protects against minification.
Controllers are special in that, unlike services, there can be many instances of them in the
application. For example, there would be one instance for every `ng-controller` directive in the template.
Moreover, additional dependencies are made available to Controllers:
* {@link scope `$scope`}: Controllers are always associated with a point in the DOM and so are provided with
access to the {@link scope scope} at that point. Other components, such as services only have access to the
singleton {@link $rootScope} service.
* {@link $route} resolves: If a controller is instantiated as part of a route, then any values that
are resolved as part of the route are made available for injection into the controller.
+56 -55
View File
@@ -18,7 +18,7 @@ how to implement them.
## What are Directives?
At a high level, directives are markers on a DOM element (such as an attribute, element
name, or CSS class) that tell AngularJS's **HTML compiler** ({@link ng.$compile `$compile`}) to
name, comment or CSS class) that tell AngularJS's **HTML compiler** ({@link ng.$compile `$compile`}) to
attach a specified behavior to that DOM element or even transform the DOM element and its children.
Angular comes with a set of these directives built-in, like `ngBind`, `ngModel`, and `ngView`.
@@ -43,13 +43,13 @@ determines when to use a given directive.
In the following example, we say that the `<input>` element **matches** the `ngModel` directive.
```javascript
```html
<input ng-model="foo">
```
The following also **matches** `ngModel`:
```javascript
```html
<input data-ng:model="foo">
```
@@ -70,12 +70,12 @@ Here are some equivalent examples of elements that match `ngBind`:
<example module="docsBindExample">
<file name="script.js">
angular.module('docsBindExample', [])
.controller('Ctrl1', function Ctrl1($scope) {
.controller('Controller', ['$scope', function($scope) {
$scope.name = 'Max Karl Ernst Ludwig Planck (April 23, 1858 October 4, 1947)';
});
}]);
</file>
<file name="index.html">
<div ng-controller="Ctrl1">
<div ng-controller="Controller">
Hello <input ng-model='name'> <hr/>
<span ng-bind="name"></span> <br/>
<span ng:bind="name"></span> <br/>
@@ -86,7 +86,7 @@ Here are some equivalent examples of elements that match `ngBind`:
</file>
<file name="protractorTest.js">
it('should show off bindings', function() {
expect(element(by.css('div[ng-controller="Ctrl1"] span[ng-bind]')).getText())
expect(element(by.css('div[ng-controller="Controller"] span[ng-bind]')).getText())
.toBe('Max Karl Ernst Ludwig Planck (April 23, 1858 October 4, 1947)');
});
</file>
@@ -218,12 +218,12 @@ Let's create a directive that simply replaces its contents with a static templat
<example module="docsSimpleDirective">
<file name="script.js">
angular.module('docsSimpleDirective', [])
.controller('Ctrl', function($scope) {
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
})
}])
.directive('myCustomer', function() {
return {
template: 'Name: {{customer.name}} Address: {{customer.address}}'
@@ -231,7 +231,7 @@ Let's create a directive that simply replaces its contents with a static templat
});
</file>
<file name="index.html">
<div ng-controller="Ctrl">
<div ng-controller="Controller">
<div my-customer></div>
</div>
</file>
@@ -257,12 +257,12 @@ using `templateUrl` instead:
<example module="docsTemplateUrlDirective">
<file name="script.js">
angular.module('docsTemplateUrlDirective', [])
.controller('Ctrl', function($scope) {
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
})
}])
.directive('myCustomer', function() {
return {
templateUrl: 'my-customer.html'
@@ -270,7 +270,7 @@ using `templateUrl` instead:
});
</file>
<file name="index.html">
<div ng-controller="Ctrl">
<div ng-controller="Controller">
<div my-customer></div>
</div>
</file>
@@ -282,7 +282,7 @@ using `templateUrl` instead:
Great! But what if we wanted to have our directive match the tag name `<my-customer>` instead?
If we simply put a `<my-customer>` element into the HTML, it doesn't work.
<div class="alert alert-waring">
<div class="alert alert-warning">
**Note:** When you create a directive, it is restricted to attribute only by default. In order to
create directives that are triggered by element or class name, you need to use the `restrict` option.
</div>
@@ -293,21 +293,21 @@ The `restrict` option is typically set to:
* `'E'` - only matches element name
* `'C'` - only matches class name
These restictions can all be combined as needed:
These restrictions can all be combined as needed:
* `'AEC'` - matches either attribure or element or class name
* `'AEC'` - matches either attribute or element or class name
Let's change our directive to use `restrict: 'E'`:
<example module="docsRestrictDirective">
<file name="script.js">
angular.module('docsRestrictDirective', [])
.controller('Ctrl', function($scope) {
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
})
}])
.directive('myCustomer', function() {
return {
restrict: 'E',
@@ -317,7 +317,7 @@ Let's change our directive to use `restrict: 'E'`:
</file>
<file name="index.html">
<div ng-controller="Ctrl">
<div ng-controller="Controller">
<my-customer></my-customer>
</div>
</file>
@@ -358,18 +358,18 @@ re-use such a directive:
<example module="docsScopeProblemExample">
<file name="script.js">
angular.module('docsScopeProblemExample', [])
.controller('NaomiCtrl', function($scope) {
.controller('NaomiController', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
})
.controller('IgorCtrl', function($scope) {
}])
.controller('IgorController', ['$scope', function($scope) {
$scope.customer = {
name: 'Igor',
address: '123 Somewhere'
};
})
}])
.directive('myCustomer', function() {
return {
restrict: 'E',
@@ -378,11 +378,11 @@ re-use such a directive:
});
</file>
<file name="index.html">
<div ng-controller="NaomiCtrl">
<div ng-controller="NaomiController">
<my-customer></my-customer>
</div>
<hr>
<div ng-controller="IgorCtrl">
<div ng-controller="IgorController">
<my-customer></my-customer>
</div>
</file>
@@ -400,10 +400,10 @@ we call an **isolate scope**. To do this, we can use a directive's `scope` optio
<example module="docsIsolateScopeDirective">
<file name="script.js">
angular.module('docsIsolateScopeDirective', [])
.controller('Ctrl', function($scope) {
.controller('Controller', ['$scope', function($scope) {
$scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };
$scope.igor = { name: 'Igor', address: '123 Somewhere' };
})
}])
.directive('myCustomer', function() {
return {
restrict: 'E',
@@ -415,7 +415,7 @@ we call an **isolate scope**. To do this, we can use a directive's `scope` optio
});
</file>
<file name="index.html">
<div ng-controller="Ctrl">
<div ng-controller="Controller">
<my-customer info="naomi"></my-customer>
<hr>
<my-customer info="igor"></my-customer>
@@ -473,11 +473,11 @@ within our directive's template:
<example module="docsIsolationExample">
<file name="script.js">
angular.module('docsIsolationExample', [])
.controller('Ctrl', function($scope) {
.controller('Controller', ['$scope', function($scope) {
$scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };
$scope.vojta = { name: 'Vojta', address: '3456 Somewhere Else' };
})
}])
.directive('myCustomer', function() {
return {
restrict: 'E',
@@ -489,7 +489,7 @@ within our directive's template:
});
</file>
<file name="index.html">
<div ng-controller="Ctrl">
<div ng-controller="Controller">
<my-customer info="naomi"></my-customer>
</div>
</file>
@@ -510,8 +510,8 @@ that you explicitly pass in.
<div class="alert alert-warning">
**Note:** Normally, a scope prototypically inherits from its parent. An isolated scope does not.
See the {@link guide/directive#creating-custom-directives_demo_isolating-the-scope-of-a-directive
"Isolating the Scope of a Directive"} section for more information about isolate scopes.
See the {@link api/ng/service/$compile#directive-definition-object
"Directive Definition Object - scope"} section for more information about isolate scopes.
</div>
<div class="alert alert-success">
@@ -537,16 +537,16 @@ where:
In our `link` function, we want to update the displayed time once a second, or whenever a user
changes the time formatting string that our directive binds to. We will use the `$interval` service
to call a handler on a regular basis. This is easier than using `$timeout` but also works better with
end 2 end testing, where we want to ensure that all $timeouts have completed before completing the test.
end-to-end testing, where we want to ensure that all $timeouts have completed before completing the test.
We also want to remove the `$interval` if the directive is deleted so we don't introduce a memory leak.
<example module="docsTimeDirective">
<file name="script.js">
angular.module('docsTimeDirective', [])
.controller('Ctrl2', function($scope) {
.controller('Controller', ['$scope', function($scope) {
$scope.format = 'M/d/yy h:mm:ss a';
})
.directive('myCurrentTime', function($interval, dateFilter) {
}])
.directive('myCurrentTime', ['$interval', 'dateFilter', function($interval, dateFilter) {
function link(scope, element, attrs) {
var format,
@@ -574,10 +574,10 @@ We also want to remove the `$interval` if the directive is deleted so we don't i
return {
link: link
};
});
}]);
</file>
<file name="index.html">
<div ng-controller="Ctrl2">
<div ng-controller="Controller">
Date format: <input ng-model="format"> <hr/>
Current time is: <span my-current-time="format"></span>
</div>
@@ -619,9 +619,9 @@ To do this, we need to use the `transclude` option.
<example module="docsTransclusionDirective">
<file name="script.js">
angular.module('docsTransclusionDirective', [])
.controller('Ctrl', function($scope) {
.controller('Controller', ['$scope', function($scope) {
$scope.name = 'Tobias';
})
}])
.directive('myDialog', function() {
return {
restrict: 'E',
@@ -631,7 +631,7 @@ To do this, we need to use the `transclude` option.
});
</file>
<file name="index.html">
<div ng-controller="Ctrl">
<div ng-controller="Controller">
<my-dialog>Check out the contents, {{name}}!</my-dialog>
</div>
</file>
@@ -650,9 +650,9 @@ that redefines `name` as `Jeff`. What do you think the `{{name}}` binding will r
<example module="docsTransclusionExample">
<file name="script.js">
angular.module('docsTransclusionExample', [])
.controller('Ctrl', function($scope) {
.controller('Controller', ['$scope', function($scope) {
$scope.name = 'Tobias';
})
}])
.directive('myDialog', function() {
return {
restrict: 'E',
@@ -666,7 +666,7 @@ that redefines `name` as `Jeff`. What do you think the `{{name}}` binding will r
});
</file>
<file name="index.html">
<div ng-controller="Ctrl">
<div ng-controller="Controller">
<my-dialog>Check out the contents, {{name}}!</my-dialog>
</div>
</file>
@@ -701,7 +701,7 @@ own behavior to it.
<example module="docsIsoFnBindExample">
<file name="script.js">
angular.module('docsIsoFnBindExample', [])
.controller('Ctrl', function($scope, $timeout) {
.controller('Controller', ['$scope', '$timeout', function($scope, $timeout) {
$scope.name = 'Tobias';
$scope.hideDialog = function () {
$scope.dialogIsHidden = true;
@@ -709,7 +709,7 @@ own behavior to it.
$scope.dialogIsHidden = false;
}, 2000);
};
})
}])
.directive('myDialog', function() {
return {
restrict: 'E',
@@ -722,7 +722,7 @@ own behavior to it.
});
</file>
<file name="index.html">
<div ng-controller="Ctrl">
<div ng-controller="Controller">
<my-dialog ng-hide="dialogIsHidden" on-close="hideDialog()">
Check out the contents, {{name}}!
</my-dialog>
@@ -737,7 +737,7 @@ own behavior to it.
</example>
We want to run the function we pass by invoking it from the directive's scope, but have it run
in the context of the scope where its registered.
in the context of the scope where it's registered.
We saw earlier how to use `=attr` in the `scope` option, but in the above example, we're using
`&attr` instead. The `&` binding allows a directive to trigger evaluation of an expression in
@@ -747,7 +747,8 @@ callback functions to directive behaviors.
When the user clicks the `x` in the dialog, the directive's `close` function is called, thanks to
`ng-click.` This call to `close` on the isolated scope actually evaluates the expression
`hideDialog()` in the context of the original scope, thus running `Ctrl`'s `hideDialog` function.
`hideDialog()` in the context of the original scope, thus running `Controller`'s `hideDialog`
function.
<div class="alert alert-success">
**Best Practice:** use `&attr` in the `scope` option when you want your directive
@@ -766,8 +767,8 @@ element?
<example module="dragModule">
<file name="script.js">
angular.module('dragModule', []).
directive('myDraggable', function($document) {
angular.module('dragModule', [])
.directive('myDraggable', ['$document', function($document) {
return function(scope, element, attr) {
var startX = 0, startY = 0, x = 0, y = 0;
@@ -797,11 +798,11 @@ element?
}
function mouseup() {
$document.unbind('mousemove', mousemove);
$document.unbind('mouseup', mouseup);
$document.off('mousemove', mousemove);
$document.off('mouseup', mouseup);
}
};
});
}]);
</file>
<file name="index.html">
<span my-draggable>Drag ME</span>
+85
View File
@@ -0,0 +1,85 @@
@workInProgress
@ngdoc overview
@name E2E Testing
@description
# E2E Testing
<div class="alert alert-danger">
**Note:** In the past, end to end testing could be done with a deprecated tool called
[Angular Scenario Runner](http://code.angularjs.org/1.2.16/docs/guide/e2e-testing). That tool
is now in maintenance mode.
</div>
As applications grow in size and complexity, it becomes unrealistic to rely on manual testing to
verify the correctness of new features, catch bugs and notice regressions. Unit tests
are the first line of defense for catching bugs, but sometimes issues come up with integration
between components which can't be captured in a unit test. End to end tests are made to find
these problems.
We have built [Protractor](https://github.com/angular/protractor), an end
to end test runner which simulates user interactions that will help you verify the health of your
Angular application.
## Using Protractor
Protractor is a [Node.js](http://nodejs.org) program, and runs end to end tests that are also
written in JavaScript and run with node. Protractor uses [WebDriver](https://code.google.com/p/selenium/wiki/GettingStarted)
to control browsers and simulate user actions.
For more information on Protractor, view [getting started](https://github.com/angular/protractor/blob/master/docs/getting-started.md)
or the [api docs](https://github.com/angular/protractor/blob/master/docs/api.md).
Protractor uses [Jasmine](http://jasmine.github.io/1.3/introduction.html) for its test syntax.
As in unit testing, a test file is comprised of one or
more `it` blocks that describe the requirements of your application. `it` blocks are made of
**commands** and **expectations**. Commands tell Protractor to do something with the application
such as navigate to a page or click on a button. Expectations tell Protractor to assert something
about the application's state, such as the value of a field or the current URL.
If any expectation within an `it` block fails, the runner marks the `it` as "failed" and continues
on to the next block.
Test files may also have `beforeEach` and `afterEach` blocks, which will be run before or after
each `it` block regardless of whether the block passes or fails.
<img src="img/guide/scenario_runner.png">
In addition to the above elements, tests may also contain helper functions to avoid duplicating
code in the `it` blocks.
Here is an example of a simple test:
```js
describe('TODO list', function() {
it('should filter results', function() {
// Find the element with ng-model="user" and type "jacksparrow" into it
element(by.model('user')).sendKeys('jacksparrow');
// Find the first (and only) button on the page and click it
element(by.css(':button')).click();
// Verify that there are 10 tasks
expect(element.all(by.repeater('task in tasks')).count()).toEqual(10);
// Enter 'groceries' into the element with ng-model="filterText"
element(by.model('filterText')).sendKeys('groceries');
// Verify that now there is only one item in the task list
expect(element.all(by.repeater('task in tasks')).count()).toEqual(1);
});
});
```
This test describes the requirements of a ToDo list, specifically, that it should be able to
filter the list of items.
## Example
See the [angular-seed](https://github.com/angular/angular-seed) project for more examples, or look
at the embedded examples in the Angular documentation (For example, [$http](http://docs.angularjs.org/api/ng/service/$http)
has an end to end test in the example under the `protractor.js` tag).
## Caveats
Protractor does not work out-of-the-box with apps that bootstrap manually using
`angular.bootstrap`. You must use the `ng-app` directive.
+72 -26
View File
@@ -2,35 +2,38 @@
@name Expressions
@description
Expressions are JavaScript-like code snippets that are usually placed in bindings such as `{{
expression }}`. Expressions are processed by the {@link ng.$parse $parse}
service. Expressions are often post processed using {@link guide/filter filters} to create a more user-friendly format.
# Angular Expressions
For example, these are all valid expressions in angular:
Angular expressions are JavaScript-like code snippets that are usually placed in bindings such as
`{{ expression }}`.
For example, these are valid expressions in Angular:
* `1+2`
* `a+b`
* `user.name`
* `items[index]`
## Angular Expressions vs. JS Expressions
## Angular Expressions vs. JavaScript Expressions
It might be tempting to think of Angular view expressions as JavaScript expressions, but that is
not entirely correct, since Angular does not use a JavaScript `eval()` to evaluate expressions.
You can think of Angular expressions as JavaScript expressions with following differences:
Angular expressions are like JavaScript expressions with the following differences:
* **Attribute Evaluation:** evaluation of all properties are against the scope doing the
evaluation, unlike in JavaScript where the expressions are evaluated against the global
`window`.
* **Context:** JavaScript expressions are evaluated against the global `window`.
In Angular, expressions are evaluated against a {@link ng.$rootScope.Scope `scope`} object.
* **Forgiving:** expression evaluation is forgiving to `undefined` and `null`, unlike in JavaScript,
where trying to evaluate undefined properties can generate `ReferenceError` or `TypeError`.
* **Forgiving:** In JavaScript, trying to evaluate undefined properties generates `ReferenceError`
or `TypeError`. In Angular, expression evaluation is forgiving to `undefined` and `null`.
* **No Control Flow Statements:** you cannot do any of the following in angular expression:
conditionals, loops, or throw.
* **No Control Flow Statements:** you cannot use the following in an Angular expression:
conditionals, loops, or exceptions.
If, on the other hand, you do want to run arbitrary JavaScript code, you should make it a
controller method and call the method. If you want to `eval()` an angular expression from
JavaScript, use the {@link ng.$rootScope.Scope#$eval `$eval()`} method.
* **Filters:** You can use {@link guide/filter filters} within expressions to format data before
displaying it.
If you want to run more complex JavaScript code, you should make it a controller method and call
the method from your view. If you want to `eval()` an Angular expression yourself, use the
{@link ng.$rootScope.Scope#$eval `$eval()`} method.
## Example
<example>
@@ -87,13 +90,15 @@ You can try evaluating different expressions here:
</example>
# Property Evaluation
## Context
Evaluation of all properties takes place against a scope. Unlike JavaScript, where names default
to global window properties, Angular expressions have to use {@link ng.$window
`$window`} to refer to the global `window` object. For example, if you want to call `alert()`, which is
defined on `window`, in an expression you must use `$window.alert()`. This is done intentionally to
prevent accidental access to the global state (a common source of subtle bugs).
Angular does not use JavaScript's `eval()` to evaluate expressions. Instead Angular's
{@link ng.$parse $parse} service processes these expressions.
Unlike JavaScript, where names default to global `window` properties, Angular expressions must use
{@link ng.$window `$window`} explicitly to refer to the global `window` object. For example, if you
want to call `alert()` in an expression you must use `$window.alert()`. This restriction is
intentional. It prevents accidental access to the global state a common source of subtle bugs.
<example>
<file name="index.html">
@@ -142,12 +147,53 @@ It makes more sense to show nothing than to throw an exception if `a` is undefin
waiting for the server response, and it will become defined soon). If expression evaluation wasn't
forgiving we'd have to write bindings that clutter the code, for example: `{{((a||{}).b||{}).c}}`
Similarly, invoking a function `a.b.c()` on undefined or null simply returns undefined.
Similarly, invoking a function `a.b.c()` on `undefined` or `null` simply returns `undefined`.
## No Control Flow Statements
You cannot write a control flow statement in an expression. The reason behind this is core to the
Angular philosophy that application logic should be in controllers, not in the view. If you need a
Angular philosophy that application logic should be in controllers, not the views. If you need a
conditional, loop, or to throw from a view expression, delegate to a JavaScript method instead.
## `$event`
Directives like {@link ng.directive:ngClick `ngClick`} and {@link ng.directive:ngFocus `ngFocus`}
expose a `$event` object within the scope of that expression.
<example module="eventExampleApp">
<file name="index.html">
<div ng-controller="EventController">
<button ng-click="clickMe($event)">Event</button>
<p><code>$event</code>: <pre> {{$event | json}}</pre></p>
<p><code>clickEvent</code>: <pre>{{clickEvent | json}}</pre></p>
</div>
</file>
<file name="script.js">
angular.module('eventExampleApp', []).
controller('EventController', ['$scope', function($scope) {
/*
* expose the event object to the scope
*/
$scope.clickMe = function(clickEvent) {
$scope.clickEvent = simpleKeys(clickEvent);
console.log(clickEvent);
};
/*
* return a copy of an object with only non-object keys
* we need this to avoid circular references
*/
function simpleKeys (original) {
return Object.keys(original).reduce(function (obj, key) {
obj[key] = typeof original[key] === 'object' ? '{ ... }' : original[key];
return obj;
}, {});
}
}]);
</file>
</example>
Note in the example above how we can pass in `$event` to `clickMe`, but how it does not show up
in `{{$event}}`. This is because `$event` is outside the scope of that binding.
+14 -14
View File
@@ -29,12 +29,12 @@ E.g. the markup `{{ 1234 | number:2 }}` formats the number 1234 with 2 decimal p
{@link ng.filter:number `number`} filter. The resulting value is `1,234.00`.
## Using filters in controllers and services
## Using filters in controllers, services, and directives
You can also use filters in controllers and services. For this, add a dependency with the name `<filterName>Filter`
to your controller or service. E.g. using the dependency `numberFilter` will inject the number filter.
The injected argument is a function that takes the value to format as first argument and filter parameters
starting with the second argument.
You can also use filters in controllers, services, and directives. For this, inject a dependency
with the name `<filterName>Filter` to your controller/service/directive. E.g. using the dependency
`numberFilter` will inject the number filter. The injected argument is a function that takes the
value to format as first argument and filter parameters starting with the second argument.
The example below uses the filter called {@link ng.filter:filter `filter`}.
This filter reduces arrays into sub arrays based on
@@ -89,9 +89,9 @@ function.
The following sample filter reverses a text string. In addition, it conditionally makes the
text upper-case.
<example module="MyReverseModule">
<example module="myReverseModule">
<file name="index.html">
<div ng-controller="Ctrl">
<div ng-controller="Controller">
<input ng-model="greeting" type="text"><br>
No filter: {{greeting}}<br>
Reverse: {{greeting|reverse}}<br>
@@ -100,9 +100,10 @@ text upper-case.
</file>
<file name="script.js">
angular.module('MyReverseModule', []).
filter('reverse', function() {
angular.module('myReverseModule', [])
.filter('reverse', function() {
return function(input, uppercase) {
input = input || '';
var out = "";
for (var i = 0; i < input.length; i++) {
out = input.charAt(i) + out;
@@ -113,11 +114,10 @@ text upper-case.
}
return out;
};
});
function Ctrl($scope) {
$scope.greeting = 'hello';
}
})
.controller('Controller', ['$scope', function($scope) {
$scope.greeting = 'hello';
}]);
</file>
</example>
+94 -20
View File
@@ -56,7 +56,7 @@ Note that `novalidate` is used to disable browser's native form validation.
# Using CSS classes
To allow styling of form as well as controls, `ngModel` add these CSS classes:
To allow styling of form as well as controls, `ngModel` adds these CSS classes:
- `ng-valid`
- `ng-invalid`
@@ -181,6 +181,82 @@ This allows us to extend the above example with these features:
# Custom triggers
By default, any change to the content will trigger a model update and form validation. You can
override this behavior using the {@link ng.directive:ngModelOptions ngModelOptions} directive to
bind only to specified list of events. I.e. `ng-model-options="{ updateOn: 'blur' }"` will update
and validate only after the control loses focus. You can set several events using a space delimited
list. I.e. `ng-model-options="{ updateOn: 'mousedown blur' }"`
If you want to keep the default behavior and just add new events that may trigger the model update
and validation, add "default" as one of the specified events.
I.e. `ng-model-options="{ updateOn: 'default blur' }"`
The following example shows how to override immediate updates. Changes on the inputs within the form will update the model
only when the control loses focus (blur event).
<example>
<file name="index.html">
<div ng-controller="ControllerUpdateOn">
<form>
Name:
<input type="text" ng-model="user.name" ng-model-options="{ updateOn: 'blur' }" /><br />
Other data:
<input type="text" ng-model="user.data" /><br />
</form>
<pre>username = "{{user.name}}"</pre>
</div>
</file>
<file name="script.js">
function ControllerUpdateOn($scope) {
$scope.user = {};
}
</file>
</example>
# Non-immediate (debounced) model updates
You can delay the model update/validation by using the `debounce` key with the
{@link ng.directive:ngModelOptions ngModelOptions} directive. This delay will also apply to
parsers, validators and model flags like `$dirty` or `$pristine`.
I.e. `ng-model-options="{ debounce: 500 }"` will wait for half a second since
the last content change before triggering the model update and form validation.
If custom triggers are used, custom debouncing timeouts can be set for each event using an object
in `debounce`. This can be useful to force immediate updates on some specific circumstances
(like blur events).
I.e. `ng-model-options="{ updateOn: 'default blur', debounce: { default: 500, blur: 0 } }"`
If those attributes are added to an element, they will be applied to all the child elements and controls that inherit from it unless they are
overridden.
This example shows how to debounce model changes. Model will be updated only 250 milliseconds after last change.
<example>
<file name="index.html">
<div ng-controller="ControllerUpdateOn">
<form>
Name:
<input type="text" ng-model="user.name" ng-model-options="{ debounce: 250 }" /><br />
</form>
<pre>username = "{{user.name}}"</pre>
</div>
</file>
<file name="script.js">
function ControllerUpdateOn($scope) {
$scope.user = {};
}
</file>
</example>
# Custom Validation
Angular provides basic implementation for most common html5 {@link ng.directive:input input}
@@ -211,26 +287,24 @@ In the following example we create two directives.
<example module="form-example1">
<file name="index.html">
<div ng-controller="Controller">
<form name="form" class="css-form" novalidate>
<div>
Size (integer 0 - 10):
<input type="number" ng-model="size" name="size"
min="0" max="10" integer />{{size}}<br />
<span ng-show="form.size.$error.integer">This is not valid integer!</span>
<span ng-show="form.size.$error.min || form.size.$error.max">
The value must be in range 0 to 10!</span>
</div>
<form name="form" class="css-form" novalidate>
<div>
Size (integer 0 - 10):
<input type="number" ng-model="size" name="size"
min="0" max="10" integer />{{size}}<br />
<span ng-show="form.size.$error.integer">This is not valid integer!</span>
<span ng-show="form.size.$error.min || form.size.$error.max">
The value must be in range 0 to 10!</span>
</div>
<div>
Length (float):
<input type="text" ng-model="length" name="length" smart-float />
{{length}}<br />
<span ng-show="form.length.$error.float">
This is not a valid float number!</span>
</div>
</form>
</div>
<div>
Length (float):
<input type="text" ng-model="length" name="length" smart-float />
{{length}}<br />
<span ng-show="form.length.$error.float">
This is not a valid float number!</span>
</div>
</form>
</file>
<file name="script.js">
+73 -59
View File
@@ -2,53 +2,54 @@
@name i18n and l10n
@description
# I18n and L10n in AngularJS
# i18n and l10n
**What is i18n and l10n?**
Internationalization (i18n) is the process of developing products in such a way that they can be
localized for languages and cultures easily. Localization (l10n), is the process of adapting
applications and text to enable their usability in a particular cultural or linguistic market. For
application developers, internationalizing an application means abstracting all of the strings and
other locale-specific bits (such as date or currency formats) out of the application. Localizing an
application means providing translations and localized formats for the abstracted bits.
Internationalization, abbreviated i18n, is the process of developing products in such a way that
they can be localized for languages and cultures easily. Localization, abbreviated l10n, is the
process of adapting applications and text to enable their usability in a particular cultural or
linguistic market. For application developers, internationalizing an application means abstracting
all of the strings and other locale-specific bits (such as date or currency formats) out of the
application. Localizing an application means providing translations and localized formats for the
abstracted bits.
**What level of support for i18n/l10n is currently in Angular?**
## How does Angular support i18n/l10n?
Currently, Angular supports i18n/l10n for
[datetime](http://docs.angularjs.org/#!/api/ng.filter:date),
[number](http://docs.angularjs.org/#!/api/ng.filter:number) and
[currency](http://docs.angularjs.org/#!/api/ng.filter:currency) filters.
Angular supports i18n/l10n for {@link ng.filter:date date}, {@link ng.filter:number number} and
{@link ng.filter:currency currency} filters.
Additionally, Angular supports localizable pluralization support provided by the {@link
ng.directive:ngPluralize ngPluralize directive}.
Additionally, Angular supports localizable pluralization support through the {@link
ng.directive:ngPluralize `ngPluralize` directive}.
All localizable Angular components depend on locale-specific rule sets managed by the {@link
ng.$locale $locale service}.
ng.$locale `$locale` service}.
For readers who want to jump straight into examples, we have a few web pages that showcase how to
use Angular filters with various locale rule sets. You can find these examples either on
[Github](https://github.com/angular/angular.js/tree/master/i18n/e2e) or in the i18n/e2e folder of
Angular development package.
There a few examples that showcase how to use Angular filters with various locale rule sets in the
[`i18n/e2e` directory](https://github.com/angular/angular.js/tree/master/i18n/e2e) of the Angular
source code.
**What is a locale id?**
## What is a locale ID?
A locale is a specific geographical, political, or cultural region. The most commonly used locale
ID consists of two parts: language code and country code. For example, en-US, en-AU, zh-CN are all
valid locale IDs that have both language codes and country codes. Because specifying a country code
in locale ID is optional, locale IDs such as en, zh, and sk are also valid. See the
[ICU ](http://userguide.icu-project.org/locale) website for more information about using locale IDs.
ID consists of two parts: language code and country code. For example, `en-US`, `en-AU`, and
`zh-CN` are all valid locale IDs that have both language codes and country codes. Because
specifying a country code in locale ID is optional, locale IDs such as `en`, `zh`, and `sk` are
also valid. See the [ICU](http://userguide.icu-project.org/locale) website for more information
about using locale IDs.
## Supported locales in Angular
**Supported locales in Angular**
Angular separates number and datetime format rule sets into different files, each file for a
particular locale. You can find a list of currently supported locales
[here](https://github.com/angular/angular.js/tree/master/src/ngLocale)
# Providing locale rules to Angular
## Providing locale rules to Angular
There are two approaches to providing locale rules to Angular:
**1. Pre-bundled rule sets**
### 1. Pre-bundled rule sets
You can pre-bundle the desired locale file with Angular by concatenating the content of the
locale-specific file to the end of `angular.js` or `angular.min.js` file.
@@ -61,7 +62,7 @@ locale, you can do the following:
When the application containing `angular_de-de.js` script instead of the generic angular.js script
starts, Angular is automatically pre-configured with localization rules for the german locale.
**2. Including locale js script in index.html page**
### 2. Including a locale script in `index.html`
You can also include the locale specific js file in the index.html page. For example, if one client
requires German locale, you would serve index_de-de.html which will look something like this:
@@ -77,48 +78,61 @@ requires German locale, you would serve index_de-de.html which will look somethi
</html>
```
**Comparison of the two approaches**
Both approaches described above requires you to prepare different index.html pages or js files for
each locale that your app may be localized into. You also need to configure your server to serve
### Comparison of the two approaches
Both approaches described above require you to prepare different `index.html` pages or JavaScript
files for each locale that your app may use. You also need to configure your server to serve
the correct file that correspond to the desired locale.
However, the second approach (Including locale js script in index.html page) is likely to be slower
because an extra script needs to be loaded.
The second approach (including the locale JavaScript file in `index.html`) may be slower because
an extra script needs to be loaded.
# "Gotchas"
## Caveats
**Currency symbol "gotcha"**
Although Angular makes i18n convenient, there are several things you need to be conscious of as you
develop your app.
Angular's [currency filter](http://docs.angularjs.org/#!/api/ng.filter:currency) allows
you to use the default currency symbol from the {@link ng.$locale locale service},
or you can provide the filter with a custom currency symbol. If your app will be used only in one
locale, it is fine to rely on the default currency symbol. However, if you anticipate that viewers
in other locales might use your app, you should provide your own currency symbol to make sure the
actual value is understood.
### Currency symbol
For example, if you want to display an account balance of 1000 dollars with the following binding
containing currency filter: `{{ 1000 | currency }}`, and your app is currently in en-US locale.
'$1000.00' will be shown. However, if someone in a different local (say, Japan) views your app, their
browser will specify the locale as ja, and the balance of '¥1000.00' will be shown instead. This
will really upset your client.
Angular's {@link ng.filter:currency currency filter} allows you to use the default currency symbol
from the {@link ng.$locale locale service}, or you can provide the filter with a custom currency
symbol.
<div class="alert alert-success">
**Best Practice:** If your app will be used only in one locale, it is fine to rely on the default
currency symbol. If you anticipate that viewers in other locales might use your app, you should
explicitly provide a currency symbol.
</div>
Let's say you are writing a banking app and you want to display an account balance of 1000 dollars.
You write the following binding using the currency filter:
```html
{{ 1000 | currency }}
```
If your app is currently in the `en-US` locale, the browser will show `$1000.00`. If someone in the
Japanese locale (`ja`) views your app, their browser will show a balance of `¥1000.00` instead.
This is problematinc because $1000 is not the same as ¥1000.
In this case, you need to override the default currency symbol by providing the
[currency filter](http://docs.angularjs.org/#!/api/ng.filter:currency) with a currency symbol as
a parameter when you configure the filter, for example, {{ 1000 | currency:"USD$"}}. This way,
Angular will always show a balance of 'USD$1000' and disregard any locale changes.
{@link ng.filter:currency} currency filter with a currency symbol as a parameter.
**Translation length "gotcha"**
If we change the above to `{{ 1000 | currency:"USD$"}}`, Angular will always show a balance of
`USD$1000` regardless of locale.
Keep in mind that translated strings/datetime formats can vary greatly in length. For example,
`June 3, 1977` will be translated to Spanish as `3 de junio de 1977`. There are bound to be other
more extreme cases. Hence, when internationalizing your apps, you need to apply CSS rules
accordingly and do thorough testing to make sure UI components do not overlap.
### Translation length
Translated strings/datetime formats can vary greatly in length. For example, `June 3, 1977` will be
translated to Spanish as `3 de junio de 1977`.
**Timezones**
When internationalizing your app, you need to do thorough testing to make sure UI components behave
as expected even when their contents vary greatly in content size.
Keep in mind that Angular datetime filter uses the time zone settings of the browser. So the same
### Timezones
The Angular datetime filter uses the time zone settings of the browser. The same
application will show different time information depending on the time zone settings of the
computer that the application is running on. Neither Javascript nor Angular currently supports
computer that the application is running on. Neither JavaScript nor Angular currently supports
displaying the date with a timezone specified by the developer.
+24 -19
View File
@@ -1,32 +1,37 @@
@ngdoc overview
@name Internet Explorer Compatibility
@name Internet Explorer Compatibility
@description
# Overview
# Internet Explorer Compatibility
<div class="alert alert-warning">
**Note:** AngularJS 1.3 is dropping support for IE8. Read more about it on
[our blog](http://blog.angularjs.org/2013/12/angularjs-13-new-release-approaches.html).
AngularJS 1.2 will continue to support IE8, but the core team does not plan to spend time
addressing issues specific to IE8 or earlier.
</div>
This document describes the Internet Explorer (IE) idiosyncrasies when dealing with custom HTML
attributes and tags. Read this document if you are planning on deploying your Angular application
on IE v8.0 or earlier.
on IE8 or earlier.
The project currently supports and will attempt to fix bugs for IE8 and above. The continuous
integration server runs all the tests against IE8. See http://ci.angularjs.org.
The project currently supports and will attempt to fix bugs for IE9 and above. The continuous
integration server runs all the tests against IE9, IE10, and IE11. See
[Travis CI](https://travis-ci.org/angular/angular.js) and
[ci.angularjs.org](http://ci.angularjs.org).
IE7 and below are not tested and the project makes no guarantee that Angular will work on it.
A subset of the AngularJS functionality may work. It is up to you to test and decide whether
it works for your particular app.
It is very unlikely that issues specific to IE7 or earlier will be given any time by the core team.
[GitHub](https://github.com/angular/angular.js/issues/4974)
We do not run tests on IE8 and below. A subset of the AngularJS functionality may work on these
browsers, but it is up to you to test and decide whether it works for your particular app.
# Short Version
## Short Version
To make your Angular application work on IE please make sure that:
1. You polyfill JSON.stringify for IE7 and below. You can use
[JSON2](https://github.com/douglascrockford/JSON-js) or
[JSON3](http://bestiejs.github.com/json3/) polyfills for this.
```html
<!doctype html>
<html xmlns:ng="http://angularjs.org">
@@ -42,7 +47,7 @@ To make your Angular application work on IE please make sure that:
```
2. add `id="ng-app"` to the root element in conjunction with `ng-app` attribute
```html
<!doctype html>
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
@@ -54,7 +59,7 @@ To make your Angular application work on IE please make sure that:
`<div ng-view>` instead), or
4. if you **do use** custom element tags, then you must take these steps to make IE 8 and below happy:
```html
<!doctype html>
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
@@ -64,7 +69,7 @@ To make your Angular application work on IE please make sure that:
document.createElement('ng-include');
document.createElement('ng-pluralize');
document.createElement('ng-view');
// Optionally these for CSS
document.createElement('ng:include');
document.createElement('ng:pluralize');
@@ -79,7 +84,7 @@ To make your Angular application work on IE please make sure that:
```
5. Use `ng-style` tags instead of `style="{{ someCss }}"`. The later works in Chrome and Firefox
but does not work in Internet Explorer <= 11 (the most recent version at time of writing).
The **important** parts are:
@@ -92,7 +97,7 @@ The **important** parts are:
happy.
# Long Version
## Long Version
IE has issues with element tag names which are not standard HTML tag names. These fall into two
categories, and each category has its own fix.
@@ -165,7 +170,7 @@ In IE, the behavior is that the `BODY` element has three children:
## CSS Styling of Custom Tag Names
To make CSS selectors work with custom elements, the custom element name must be pre-created with
To make CSS selectors work with custom elements, the custom element name must be pre-created with
`document.createElement('my-tag')` regardless of XML namespace.
```html
+4 -2
View File
@@ -57,6 +57,7 @@ In Angular applications, you move the job of filling page templates with data fr
* **Other Languages:** [CoffeeScript](http://www.coffeescriptlove.com/2013/08/angularjs-and-coffeescript-tutorials.html), [Dart](https://github.com/angular/angular.dart.tutorial/wiki)
* **Realtime: **[Socket.io](http://www.creativebloq.com/javascript/angularjs-collaboration-board-socketio-2132885), [OmniBinder](https://github.com/jeffbcross/omnibinder)
* **Visualization:** [SVG](http://gaslight.co/blog/angular-backed-svgs), [D3.js](http://www.ng-newsletter.com/posts/d3-on-angular.html)
* **Local Storage and session:** [ngStorage](https://github.com/gsklee/ngStorage)
## Tools
@@ -69,17 +70,18 @@ In Angular applications, you move the job of filling page templates with data fr
This is a short list of libraries with specific support and documentation for working with Angular. You can find a full list of all known Angular external libraries at [ngmodules.org](http://ngmodules.org/).
* **Internationalization:** [angular-translate](http://pascalprecht.github.io/angular-translate/), [angular-gettext](http://angular-gettext.rocketeer.be/)
* **Internationalization:** [angular-translate](http://angular-translate.github.io), [angular-gettext](http://angular-gettext.rocketeer.be/)
* **RESTful services:** [Restangular](https://github.com/mgonto/restangular)
* **SQL and NoSQL backends:** [BreezeJS](http://www.breezejs.com/), [AngularFire](http://angularfire.com/)
* **UI Widgets: **[KendoUI](http://kendo-labs.github.io/angular-kendo/#/), [UI Bootstrap](http://angular-ui.github.io/bootstrap/), [Wijmo](http://wijmo.com/tag/angularjs-2/)
* **Advanced Routing:** [UI-Router](https://github.com/angular-ui/ui-router)
## Deployment
### General
* **Javascript minification: **[Background](http://thegreenpizza.github.io/2013/05/25/building-minification-safe-angular.js-applications/), [ngmin automation tool](http://www.thinkster.io/pick/XlWneEZCqY/angularjs-ngmin)
* **Tracking:** [Angularyitcs (Google Analytics)](http://ngmodules.org/modules/angularytics), [Logging Client-Side Errors](http://www.bennadel.com/blog/2542-Logging-Client-Side-Errors-With-AngularJS-And-Stacktrace-js.htm)
* **Analytics and Logging:** [Angularyitcs (Google Analytics)](http://ngmodules.org/modules/angularytics), [Angulartics (Analytics)](https://github.com/luisfarzati/angulartics), [Logging Client-Side Errors](http://www.bennadel.com/blog/2542-Logging-Client-Side-Errors-With-AngularJS-And-Stacktrace-js.htm)
* **SEO:** [By hand](http://www.yearofmoo.com/2012/11/angularjs-and-seo.html), [prerender.io](http://prerender.io/), [Brombone](http://www.brombone.com/), [SEO.js](http://getseojs.com/), [SEO4Ajax](http://www.seo4ajax.com/)
### Server-Specific
+3 -3
View File
@@ -7,14 +7,14 @@
AngularJS is a structural framework for dynamic web apps. It lets you use HTML as your template
language and lets you extend HTML's syntax to express your application's components clearly and
succinctly. Out of the box, it eliminates much of the code you currently write through data
binding and dependency injection. And it all happens in JavaScript within the browser, making it
succinctly. Angular's data binding and dependency injection eliminate much of the code you
currently have to write. And it all happens within the browser, making it
an ideal partner with any server technology.
Angular is what HTML would have been had it been designed for applications. HTML is a great
declarative language for static documents. It does not contain much in the way of creating
applications, and as a result building web applications is an exercise in *what do I have to do
to trick the browser into doing what I want.*
to trick the browser into doing what I want?*
The impedance mismatch between dynamic applications and static documents is often solved with:
+88 -12
View File
@@ -2,12 +2,13 @@
@name Migrating from 1.0 to 1.2
@description
# Migrating from 1.0 to 1.2
AngularJS version 1.2 introduces several breaking changes that may require changes to your
application's source code.
Although we try to avoid breaking changes, there are some cases where it is unavoidable.
AngularJS 1.2 has undergone a thourough security review to make applications safer by default,
AngularJS 1.2 has undergone a thorough security review to make applications safer by default,
which has driven many of these changes. Several new features, especially animations, would not
be possible without a few changes. Finally, some outstanding bugs were best fixed by changing
an existing API.
@@ -26,7 +27,7 @@ below should still apply, but you may want to consult the
<li>{@link guide/migration#ngroute-has-been-moved-into-its-own-module ngRoute has been moved into its own module}</li>
<li>{@link guide/migration#templates-no-longer-automatically-unwrap-promises Templates no longer automatically unwrap promises}</li>
<li>{@link guide/migration#syntax-for-named-wildcard-parameters-changed-in Syntax for named wildcard parameters changed in <code>$route</code>}</li>
<li>{@link guide/migration#you-can-only-bind-one-expression-to You can only bind one expression to <code>*[src]</code> or <code>*[ng-src]</code>}</li>
<li>{@link guide/migration#you-can-only-bind-one-expression-to You can only bind one expression to <code>*[src]</code>, <code>*[ng-src]</code> or <code>action</code>}</li>
<li>{@link guide/migration#interpolations-inside-dom-event-handlers-are-now-disallowed Interpolations inside DOM event handlers are now disallowed}</li>
<li>{@link guide/migration#directives-cannot-end-with--start-or--end Directives cannot end with -start or -end}</li>
<li>{@link guide/migration#in-$q,-promisealways-has-been-renamed-promisefinally In $q, promise.always has been renamed promise.finally}</li>
@@ -43,11 +44,13 @@ below should still apply, but you may want to consult the
<li>{@link guide/migration#ngscenario ngScenario}</li>
<li>{@link guide/migration#nginclude-and-ngview-replace-its-entire-element-on-update ngInclude and ngView replace its entire element on update}</li>
<li>{@link guide/migration#urls-are-now-sanitized-against-a-whitelist URLs are now sanitized against a whitelist}</li>
<li>{@link guide/migration#isolate-scope-only-exposed-to-directives-with-property Isolate scope only exposed to directives with <code>scope</code> property}</li>
<li>{@link guide/migration#isolate-scope-only-exposed-to-directives-with-scope-property Isolate scope only exposed to directives with <code>scope</code> property}</li>
<li>{@link guide/migration#change-to-interpolation-priority Change to interpolation priority}</li>
<li>{@link guide/migration#underscore-prefixed/suffixed-properties-are-non-bindable Underscore-prefixed/suffixed properties are non-bindable}</li>
<li>{@link guide/migration#you-cannot-bind-to-select[multiple] You cannot bind to select[multiple]}</li>
<li>{@link guide/migration#uncommon-region-specific-local-files-were-removed-from-i18n Uncommon region-specific local files were removed from i18n}</li>
<li>{@link guide/migration#services-can-now-return-functions Services can now return functions}</li>
<li>{@link guide/migration#modifying-the-dom-outside-digest-cycle Modifying the DOM outside digest cycle}</li>
</ul>
@@ -137,17 +140,18 @@ $routeProvider.when('/Book1/:book/Chapter/:chapter/:highlight*/edit',
See [04cebcc1](https://github.com/angular/angular.js/commit/04cebcc133c8b433a3ac5f72ed19f3631778142b).
## You can only bind one expression to `*[src]` or `*[ng-src]`
## You can only bind one expression to `*[src]`, `*[ng-src]` or `action`
With the exception of `<a>` and `<img>` elements, you cannot bind more than one expression to the
`src` attribute of elements.
`src` or `action` attribute of elements.
This is one of several improvements to security introduces by Angular 1.2.
Concatenating expressions makes it hard to understand whether some combination of concatenated
values are unsafe to use and potentially subject to XSS vulnerabilities. To simplify the task of
auditing for XSS issues, we now require that a single expression be used for `*[src/ng-src]`
bindings such as bindings for `iframe[src]`, `object[src]`, etc.
bindings such as bindings for `iframe[src]`, `object[src]`, etc. In addition, this requirement is
enforced for `form` tags with `action` attributes.
<table class="table table-bordered code-table">
<thead>
@@ -493,7 +497,7 @@ See [31f190d4](https://github.com/angular/angular.js/commit/31f190d4d53921d32253
the priority of ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView has changed. This could affect directives that explicitly specify their priority.
In order to make ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView work together in all common scenarios their directives are being adjusted to achieve the following precendence:
In order to make ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView work together in all common scenarios their directives are being adjusted to achieve the following precedence:
Directive | Old Priority | New Priority
@@ -532,7 +536,7 @@ See [7d69d52a](https://github.com/angular/angular.js/commit/7d69d52acff8578e0f7d
A whitelist configured via `$compileProvider` can be used to configure what URLs are considered safe.
By default all common protocol prefixes are whitelisted including `data:` URIs with mime types `image/*`.
This change sholdn't impact apps that don't contain malicious image links.
This change shouldn't impact apps that don't contain malicious image links.
See [1adf29af](https://github.com/angular/angular.js/commit/1adf29af13890d61286840177607edd552a9df97),
[3e39ac7e](https://github.com/angular/angular.js/commit/3e39ac7e1b10d4812a44dad2f959a93361cd823b).
@@ -540,9 +544,45 @@ See [1adf29af](https://github.com/angular/angular.js/commit/1adf29af13890d612868
## Isolate scope only exposed to directives with `scope` property
Directives without isolate scope do not get the isolate scope from an isolate directive on the
same element. If your code depends on this behavior (non-isolate directive needs to access state
from within the isolate scope), change the isolate directive to use scope locals to pass these explicitly.
If you declare a scope option on a directive, that directive will have an
[isolate scope](https://github.com/angular/angular.js/wiki/Understanding-Scopes). In Angular 1.0, if a
directive with an isolate scope is used on an element, all directives on that same element have access
to the same isolate scope. For example, say we have the following directives:
```
// This directive declares an isolate scope.
.directive('isolateScope', function() {
return {
scope: {},
link: function($scope) {
console.log('one = ' + $scope.$id);
}
};
})
// This directive does not.
.directive('nonIsolateScope', function() {
return {
link: function($scope) {
console.log('two = ' + $scope.$id);
}
};
});
```
Now what happens if we use both directives on the same element?
```
<div isolate-scope non-isolate-scope></div>
```
In Angular 1.0, the nonIsolateScope directive will have access to the isolateScope directives scope. The
log statements will print the same id, because the scope is the same. But in Angular 1.2, the nonIsolateScope
will not use the same scope as isolateScope. Instead, it will inherit the parent scope. The log statements
will print different ids.
If your code depends on the Angular 1.0 behavior (non-isolate directive needs to access state
from within the isolate scope), change the isolate directive to use scope locals to pass these explicitly:
**Before**
@@ -613,7 +653,7 @@ controller.) That's easier said that done for two reasons:
someone on the scope chain for JavaScript use, you also expose it to
Angular expressions
2. The new `controller as` syntax that's now in increased usage exposes the
entire controller on the scope chain greatly increaing the exposed surface.
entire controller on the scope chain greatly increasing the exposed surface.
Though Angular expressions are written and controlled by the developer, they:
@@ -653,3 +693,39 @@ load and use your copy of the locale file provided that you maintain it yourself
See [6382e21f](https://github.com/angular/angular.js/commit/6382e21fb28541a2484ac1a241d41cf9fbbe9d2c).
## Services can now return functions
Previously, the service constructor only returned objects regardless of whether a function was returned.
Now, `$injector.instantiate` (and thus `$provide.service`) behaves the same as the native
`new` operator and allows functions to be returned as a service.
If using a JavaScript preprocessor it's quite possible when upgrading that services could start behaving incorrectly.
Make sure your services return the correct type wanted.
**Coffeescript example**
```
myApp.service 'applicationSrvc', ->
@something = "value"
@someFunct = ->
"something else"
```
pre 1.2 this service would return the whole object as the service.
post 1.2 this service returns `someFunct` as the value of the service
you would need to change this services to
```
myApp.service 'applicationSrvc', ->
@something = "value"
@someFunct = ->
"something else"
return
```
to continue to return the complete instance.
See [c22adbf1](https://github.com/angular/angular.js/commit/c22adbf160f32c1839fbb35382b7a8c6bcec2927).
+72 -64
View File
@@ -4,27 +4,27 @@
# What is a Module?
Most applications have a main method which instantiates, wires, and bootstraps the application.
You can think of a module as a container for the different parts of your app controllers,
services, filters, directives, etc.
# Why?
Most applications have a main method that instantiates and wires together the different parts of
the application.
Angular apps don't have a main method. Instead modules declaratively specify how an application
should be bootstrapped. There are several advantages to this approach:
* The process is more declarative which is easier to understand
* In unit-testing there is no need to load all modules, which may aid in writing unit-tests.
* Additional modules can be loaded in scenario tests, which can override some of the
configuration and help end-to-end test the application
* Third party code can be packaged as reusable modules.
* The modules can be loaded in any/parallel order (due to delayed nature of module execution).
* The declarative process is easier to understand.
* You can package code as reusable modules.
* The modules can be loaded in any order (or even in parallel) because modules delay execution.
* Unit tests only have to load relevant modules, which keeps them fast.
* End-to-end tests can use modules to override configuration.
# The Basics
Ok, I'm in a hurry. How do I get a Hello World module working?
Important things to notice:
* {@link angular.Module Module} API
* Notice the reference to the `myApp` module in the `<html ng-app="myApp">`, it is what
bootstraps the app using your module.
I'm in a hurry. How do I get a Hello World module working?
<example module='myApp'>
<file name="index.html">
@@ -47,6 +47,13 @@ Important things to notice:
</file>
</example>
Important things to notice:
* The {@link angular.Module Module} API
* The reference to `myApp` module in `<html ng-app="myApp">`.
This is what bootstraps the app using your module.
* The empty array in `angular.module('myApp', [])`.
This array is the list of modules `myApp` depends on.
# Recommended Setup
@@ -54,18 +61,16 @@ Important things to notice:
While the example above is simple, it will not scale to large applications. Instead we recommend
that you break your application to multiple modules like this:
* A service module, for service declaration
* A directive module, for directive declaration
* A filter module, for filter declaration
* And an application level module which depends on the above modules, and which has
* A module for each feature
* A module for each reusable component (especially directives and filters)
* And an application level module which depends on the above modules and contains any
initialization code.
The reason for this breakup is that in your tests, it is often necessary to ignore the
initialization code, which tends to be difficult to test. By putting it into a separate module it
can be easily ignored in tests. The tests can also be more focused by only loading the modules
that are relevant to tests.
We've also
[written a document](http://blog.angularjs.org/2014/02/an-angularjs-style-guide-and-best.html)
on how we organize large apps at Google.
The above is only a suggestion, so feel free to tailor it to your needs.
The above is a suggestion. Tailor it to your needs.
<example module='xmpl'>
<file name="index.html">
@@ -133,19 +138,19 @@ angular.module('myModule', []).
// This is an example of config block.
// You can have as many of these as you want.
// You can only inject Providers (not instances)
// into the config blocks.
// into config blocks.
}).
run(function(injectables) { // instance-injector
// This is an example of a run block.
// You can have as many of these as you want.
// You can only inject instances (not Providers)
// into the run blocks
// into run blocks
});
```
## Configuration Blocks
There are some convenience methods on the module which are equivalent to the config block. For
There are some convenience methods on the module which are equivalent to the `config` block. For
example:
```js
@@ -166,8 +171,10 @@ angular.module('myModule', []).
});
```
The configuration blocks get applied in the order in which they are registered. The only exception
to it are constant definitions, which are placed at the beginning of all configuration blocks.
<div class="alert alert-info">
When bootstrapping, first Angular applies all constant definitions.
Then Angular applies configuration blocks in the same order they were registered.
</div>
## Run Blocks
@@ -198,72 +205,73 @@ Beware that using `angular.module('myModule', [])` will create the module `myMod
existing module named `myModule`. Use `angular.module('myModule')` to retrieve an existing module.
```js
var myModule = angular.module('myModule', []);
// add some directives and services
myModule.service('myService', ...);
myModule.directive('myDirective', ...);
var myModule = angular.module('myModule', []);
// overwrites both myService and myDirective by creating a new module
var myModule = angular.module('myModule', []);
// add some directives and services
myModule.service('myService', ...);
myModule.directive('myDirective', ...);
// throws an error because myOtherModule has yet to be defined
var myModule = angular.module('myOtherModule');
// overwrites both myService and myDirective by creating a new module
var myModule = angular.module('myModule', []);
// throws an error because myOtherModule has yet to be defined
var myModule = angular.module('myOtherModule');
```
# Unit Testing
In its simplest form a unit test is a way of instantiating a subset of the application in test and
then applying a stimulus to it. It is important to realize that each module can only be loaded
once per injector. Typically an app has only one injector. But in tests, each test has its own
injector, which means that the modules are loaded multiple times per VM. Properly structured
modules can help with unit testing, as in this example:
A unit test is a way of instantiating a subset of an application to apply stimulus to it.
Small, structured modules help keep unit tests concise and focused.
<div class="did you know...">
Each module can only be loaded once per injector.
Usually an Angular app has only one injector and modules are only loaded once.
Each test has its own injector and modules are loaded multiple times.
</div>
In all of these examples we are going to assume this module definition:
```js
angular.module('greetMod', []).
angular.module('greetMod', []).
factory('alert', function($window) {
return function(text) {
$window.alert(text);
}
}).
factory('alert', function($window) {
return function(text) {
$window.alert(text);
}
}).
value('salutation', 'Hello').
value('salutation', 'Hello').
factory('greet', function(alert, salutation) {
return function(name) {
alert(salutation + ' ' + name + '!');
}
});
factory('greet', function(alert, salutation) {
return function(name) {
alert(salutation + ' ' + name + '!');
}
});
```
Let's write some tests:
Let's write some tests to show how to override configuration in tests.
```js
describe('myApp', function() {
// load the relevant application modules then load a special
// test module which overrides the $window with a mock version,
// so that calling window.alert() will not block the test
// runner with a real alert box. This is an example of overriding
// configuration information in tests.
// load application module (`greetMod`) then load a special
// test module which overrides `$window` with a mock version,
// so that calling `window.alert()` will not block the test
// runner with a real alert box.
beforeEach(module('greetMod', function($provide) {
$provide.value('$window', {
alert: jasmine.createSpy('alert')
});
}));
// The inject() will create the injector and inject the greet and
// $window into the tests. The test need not concern itself with
// wiring of the application, only with testing it.
// inject() will create the injector and inject the `greet` and
// `$window` into the tests.
it('should alert on $window', inject(function(greet, $window) {
greet('World');
expect($window.alert).toHaveBeenCalledWith('Hello World!');
}));
// this is another way of overriding configuration in the
// tests using an inline module and inject methods.
// tests using inline `module` and `inject` methods.
it('should alert using the alert service', function() {
var alertSpy = jasmine.createSpy('alert');
module(function($provide) {
+8 -8
View File
@@ -83,7 +83,7 @@ On to more complex examples!
## Factory Recipe
The Value recipe is very simple to write, but lacks some important features we often need when
creating services. Let's now look at the Value recipe's more powerful sibling, the Factory.The
creating services. Let's now look at the Value recipe's more powerful sibling, the Factory. The
Factory recipe adds the following abilities:
* ability to use other services (have dependencies)
@@ -97,7 +97,7 @@ created by this recipe.
Note: All services in Angular are singletons. That means that the injector uses each recipe at most
once to create the object. The injector then caches the reference for all future needs.
Since Factory is more powerful version of Value recipe, you can construct the same service with it.
Since Factory is more powerful version of the Value recipe, you can construct the same service with it.
Using our previous `clientId` Value recipe example, we can rewrite it as a Factory recipe like
this:
@@ -111,8 +111,8 @@ But given that the token is just a string literal, sticking with the Value recip
appropriate as it makes the code easier to follow.
Let's say, however, that we would also like to create a service that computes a token used for
authentication against a remote API. This token will be called 'apiToken' and will be computed
based on the `clientId` value and a secret stored in browser's local storage:
authentication against a remote API. This token will be called `apiToken` and will be computed
based on the `clientId` value and a secret stored in the browser's local storage:
```javascript
myApp.factory('apiToken', ['clientId', function apiTokenFactory(clientId) {
@@ -132,8 +132,8 @@ In the code above, we see how the `apiToken` service is defined via the Factory
on `clientId` service. The factory service then uses NSA-proof encryption to produce an authentication
token.
Note: It is a best practice to name the factory functions as "<serviceId>Factory"
(e.g. apiTokenFactory). While this names are not required, they help when navigating the code base
Note: It is best practice to name the factory functions as `<serviceId>Factory`
(e.g. apiTokenFactory). While this naming convention is not required, it helps when navigating the code base
or looking at stack traces in the debugger.
Just like with Value recipe, Factory recipe can create a service of any type, whether it be a
@@ -143,7 +143,7 @@ primitive, object literal, function, or even an instance of a custom type.
## Service Recipe
JavaScript developers often use custom types to write object-oriented code. Let's explore how we
could launch a unicorn into the space via our `unicornLauncher` service that is an instance of
could launch a unicorn into space via our `unicornLauncher` service which is an instance of a
custom type:
```javascript
@@ -187,7 +187,7 @@ myApp.service('unicornLauncher', ["apiToken", UnicornLauncher]);
Much simpler!
Note: Yes, we have called one of our service recipes 'Service'. We regret this and know that we'll
be somehow punished for our mis-deed. It's like we named one of our offspring 'Children'. Boy,
be somehow punished for our mis-deed. It's like we named one of our offspring 'Child'. Boy,
that would mess with the teachers.
+9 -9
View File
@@ -268,7 +268,7 @@ the `$digest` phase. This delay is desirable, since it coalesces multiple model
4. **Mutation observation**
At the end `$apply`, Angular performs a {@link ng.$rootScope.Scope#$digest
At the end of `$apply`, Angular performs a {@link ng.$rootScope.Scope#$digest
$digest} cycle on the root scope, which then propagates throughout all child scopes. During
the `$digest` cycle, all `$watch`ed expressions or functions are checked for model mutation
and if a mutation is detected, the `$watch` listener is called.
@@ -350,15 +350,15 @@ The diagram and the example below describe how Angular interacts with the browse
Angular modifies the normal JavaScript flow by providing its own event processing loop. This
splits the JavaScript into classical and Angular execution context. Only operations which are
applied in Angular execution context will benefit from Angular data-binding, exception handling,
property watching, etc... You can also use $apply() to enter Angular execution context from JavaScript. Keep in
applied in the Angular execution context will benefit from Angular data-binding, exception handling,
property watching, etc... You can also use $apply() to enter the Angular execution context from JavaScript. Keep in
mind that in most places (controllers, services) $apply has already been called for you by the
directive which is handling the event. An explicit call to $apply is needed only when
implementing custom event callbacks, or when working with third-party library callbacks.
1. Enter Angular execution context by calling {@link guide/scope scope}`.`{@link
ng.$rootScope.Scope#$apply $apply}`(stimulusFn)`. Where `stimulusFn` is
the work you wish to do in Angular execution context.
1. Enter the Angular execution context by calling {@link guide/scope scope}`.`{@link
ng.$rootScope.Scope#$apply $apply}`(stimulusFn)`, where `stimulusFn` is
the work you wish to do in the Angular execution context.
2. Angular executes the `stimulusFn()`, which typically modifies application state.
3. Angular enters the {@link ng.$rootScope.Scope#$digest $digest} loop. The
loop is made up of two smaller loops which process {@link
@@ -388,7 +388,7 @@ user enters text into the text field.
1. the {@link ng.directive:ngModel ng-model} and {@link
ng.directive:input input} {@link guide/directive
directive} set up a `keydown` listener on the `<input>` control.
2. the {@link ng.$interpolate &#123;&#123;name&#125;&#125; } interpolation
2. the {@link ng.$interpolate interpolation}
sets up a {@link ng.$rootScope.Scope#$watch $watch} to be notified of
`name` changes.
2. During the runtime phase:
@@ -400,8 +400,8 @@ user enters text into the text field.
3. Angular applies the `name = 'X';` to the model.
4. The {@link ng.$rootScope.Scope#$digest $digest} loop begins
5. The {@link ng.$rootScope.Scope#$watch $watch} list detects a change
on the `name` property and notifies the {@link ng.$interpolate
&#123;&#123;name&#125;&#125; } interpolation, which in turn updates the DOM.
on the `name` property and notifies the {@link ng.$interpolate interpolation},
which in turn updates the DOM.
6. Angular exits the execution context, which in turn exits the `keydown` event and with it
the JavaScript execution context.
7. The browser re-renders the view with update text.
+303
View File
@@ -0,0 +1,303 @@
@ngdoc overview
@name Services
@description
# Services
Angular services are substitutable objects that are wired together using {@link di dependency
injection (DI)}. You can use services to organize and share code across your app.
Angular services are:
* Lazily instantiated Angular only instantiates a service when an application component depends
on it.
* Singletons Each component dependent on a service gets a reference to the single instance
generated by the service factory.
Angular offers several useful services (like {@link ng.$http `$http`}), but for most applications
you'll also want to {@link services#creating-services create your own}.
<div class="alert alert-info">
**Note:** Like other core Angular identifiers built-in services always start with `$`
(e.g. `$http`).
</div>
## Using a Service
To use an Angular service, you add it as a dependency for the component (controller, service,
filter or directive) that depends on the service. Angular's {@link di dependency injection}
subsystem takes care of the rest.
<example module="myServiceModule">
<file name="index.html">
<div id="simple" ng-controller="MyController">
<p>Let's try this simple notify service, injected into the controller...</p>
<input ng-init="message='test'" ng-model="message" >
<button ng-click="callNotify(message);">NOTIFY</button>
<p>(you have to click 3 times to see an alert)</p>
</div>
</file>
<file name="script.js">
angular.
module('myServiceModule', []).
controller('MyController', ['$scope','notify', function ($scope, notify) {
$scope.callNotify = function(msg) {
notify(msg);
};
}]).
factory('notify', ['$window', function(win) {
var msgs = [];
return function(msg) {
msgs.push(msg);
if (msgs.length == 3) {
win.alert(msgs.join("\n"));
msgs = [];
}
};
}]);
</file>
<file name="protractor.js" type="protractor">
it('should test service', function() {
expect(element(by.id('simple')).element(by.model('message')).getAttribute('value'))
.toEqual('test');
});
</file>
</example>
<div class="alert alert-info">
**Note:** Angular uses
[**constructor injection**](http://misko.hevery.com/2009/02/19/constructor-injection-vs-setter-injection/).
</div>
### Explicit Dependency Injection
A component should explicitly define its dependencies using one of the {@link di injection
annotation} methods:
1. Inline array injection annotation (preferred):
```js
myModule.controller('MyController', ['$location', function($location) { ... }]);
```
2. `$inject` property:
```js
var MyController = function($location) { ... };
MyController.$inject = ['$location'];
myModule.controller('MyController', MyController);
```
<div class="alert alert-success">
**Best Practice:** Use the array annotation shown above.
</div>
### Implicit Dependency Injection
Even if you don't annotate your dependencies, Angular's DI can determine the dependency from the
name of the parameter. Let's rewrite the above example to show the use of this implicit dependency
injection of `$window`, `$scope`, and our `notify` service:
<example module="myServiceModuleDI">
<file name="index.html">
<div id="implicit" ng-controller="MyController">
<p>Let's try the notify service, that is implicitly injected into the controller...</p>
<input ng-init="message='test'" ng-model="message">
<button ng-click="callNotify(message);">NOTIFY</button>
<p>(you have to click 3 times to see an alert)</p>
</div>
</file>
<file name="script.js">
angular.module('myServiceModuleDI', []).
factory('notify', function($window) {
var msgs = [];
return function(msg) {
msgs.push(msg);
if (msgs.length == 3) {
$window.alert(msgs.join("\n"));
msgs = [];
}
};
}).
controller('MyController', function($scope, notify) {
$scope.callNotify = function(msg) {
notify(msg);
};
});
</file>
</example>
<div class="alert alert-danger">
**Careful:** If you plan to [minify](http://en.wikipedia.org/wiki/Minification_(programming) your
code, your variable names will get renamed unless you use one of the annotation techniques above.
</div>
<div class="alert alert-info">
If you use a tool like [ngmin](https://github.com/btford/ngmin#ngmin) in your workflow you can
use implicit dependency notation within your codebase and let **ngmin** automatically convert such
injectable functions to the array notation prior to minifying.
</div>
## Creating Services
Application developers are free to define their own services by registering the service's name and
**service factory function**, with an Angular module.
The **service factory function** generates the single object or function that represents the
service to the rest of the application. The object or function returned by the service is
injected into any component (controller, service, filter or directive) that specifies a dependency
on the service.
### Registering Services
Services are registered to modules via the {@link angular.Module Module API}.
Typically you use the {@link angular.module Module#factory} API to register a service:
```javascript
var myModule = angular.module('myModule', []);
myModule.factory('serviceId', function() {
var shinyNewServiceInstance;
//factory function body that constructs shinyNewServiceInstance
return shinyNewServiceInstance;
});
```
Note that you are not registering a **service instance**, but rather a **factory function** that
will create this instance when called.
### Dependencies
Services can have their own dependencies. Just like declaring dependencies in a controller, you
declare dependencies by specifying them in the service's factory function signature.
The example module below has two services, each with various dependencies:
```js
var batchModule = angular.module('batchModule', []);
/**
* The `batchLog` service allows for messages to be queued in memory and flushed
* to the console.log every 50 seconds.
*
* @param {*} message Message to be logged.
*/
batchModule.factory('batchLog', ['$interval', '$log', function($interval, $log) {
var messageQueue = [];
function log() {
if (messageQueue.length) {
$log.log('batchLog messages: ', messageQueue);
messageQueue = [];
}
}
// start periodic checking
$interval(log, 50000);
return function(message) {
messageQueue.push(message);
}
}]);
/**
* `routeTemplateMonitor` monitors each `$route` change and logs the current
* template via the `batchLog` service.
*/
batchModule.factory('routeTemplateMonitor', ['$route', 'batchLog', '$rootScope',
function($route, batchLog, $rootScope) {
$rootScope.$on('$routeChangeSuccess', function() {
batchLog($route.current ? $route.current.template : null);
});
}]);
```
In the example, note that:
* The `batchLog` service depends on the built-in {@link ng.$interval `$interval`} and
{@link ng.$log `$log`} services.
* The `routeTemplateMonitor` service depends on the built-in {@link ngRoute.$route `$route`}
service and our custom `batchLog` service.
* Both services use the array notation to declare their dependencies.
* The order of identifiers in the array is the same as the order of argument
names in the factory function.
### Registering a Service with `$provide`
You can also register services via the {@link auto.$provide `$provide`} service inside of a
module's `config` function:
```javascript
angular.module('myModule', []).config(function($provide) {
$provide.factory('serviceId', function() {
var shinyNewServiceInstance;
//factory function body that constructs shinyNewServiceInstance
return shinyNewServiceInstance;
});
});
```
This technique is often used in unit tests to mock out a service's dependencies.
## Unit Testing
The following is a unit test for the `notify` service from the {@link services#creating-services
Creating Angular Services} example above. The unit test example uses a Jasmine spy (mock) instead
of a real browser alert.
```js
var mock, notify;
beforeEach(function() {
mock = {alert: jasmine.createSpy()};
module(function($provide) {
$provide.value('$window', mock);
});
inject(function($injector) {
notify = $injector.get('notify');
});
});
it('should not alert first two notifications', function() {
notify('one');
notify('two');
expect(mock.alert).not.toHaveBeenCalled();
});
it('should alert all after third notification', function() {
notify('one');
notify('two');
notify('three');
expect(mock.alert).toHaveBeenCalledWith("one\ntwo\nthree");
});
it('should clear messages after alert', function() {
notify('one');
notify('two');
notify('third');
notify('more');
notify('two');
notify('third');
expect(mock.alert.callCount).toEqual(2);
expect(mock.alert.mostRecentCall.args).toEqual(["more\ntwo\nthird"]);
});
```
## Related Topics
* {@link guide/di Dependency Injection in AngularJS}
## Related API
* {@link ./ng Angular Service API}
* {@link angular.injector Injector API}
+21 -26
View File
@@ -2,27 +2,21 @@
@name Templates
@description
An Angular template is the declarative specification that, along with information from the model
and controller, becomes the rendered view that a user sees in the browser. It is the static DOM,
containing HTML, CSS, and angular-specific elements and angular-specific element attributes. The
Angular elements and attributes direct angular to add behavior and transform the template DOM into
the dynamic view DOM.
In Angular, templates are written with HTML that contains Angular-specific elements and attributes.
Angular combines the template with information from the model and controller to render the dynamic
view that a user sees in the browser.
These are the types of Angular elements and element attributes you can use in a template:
These are the types of Angular elements and attributes you can use:
* {@link guide/directive Directive} — An attribute or element that
augments an existing DOM element or represents a reusable DOM component - a widget.
* {@link ng.$interpolate Markup} — The double
curly brace notation `{{ }}` to bind expressions to elements is built-in angular markup.
* {@link guide/filter Filter} — Formats your data for display to the user.
* {@link forms Form controls} — Lets you validate user input.
augments an existing DOM element or represents a reusable DOM component.
* {@link ng.$interpolate Markup} — The double curly brace notation `{{ }}` to bind expressions
to elements is built-in Angular markup.
* {@link guide/filter Filter} — Formats data for display.
* {@link forms Form controls} — Validates user input.
Note: In addition to declaring the elements above in templates, you can also access these elements
in JavaScript code.
The following code snippet shows a simple Angular template made up of standard HTML tags along with
Angular {@link guide/directive directives} and curly-brace bindings
with {@link expression expressions}:
The following code snippet shows a template with {@link guide/directive directives} and
curly-brace {@link expression expression} bindings:
```html
<html ng-app>
@@ -38,19 +32,20 @@ with {@link expression expressions}:
</html>
```
In a simple single-page app, the template consists of HTML, CSS, and angular directives contained
in just one HTML file (usually `index.html`). In a more complex app, you can display multiple views
within one main page using "partials", which are segments of template located in separate HTML
files. You "include" the partials in the main page using the {@link ngRoute.$route
$route} service in conjunction with the {@link ngRoute.directive:ngView ngView} directive. An
example of this technique is shown in the {@link tutorial/ angular tutorial}, in steps seven and
eight.
In a simple app, the template consists of HTML, CSS, and Angular directives contained
in just one HTML file (usually `index.html`).
In a more complex app, you can display multiple views within one main page using "partials"
segments of template located in separate HTML files. You can use the
{@link ngRoute.directive:ngView ngView} directive to load partials based on configuration passed
to the {@link ngRoute.$route $route} service. The {@link tutorial/ angular tutorial} shows this
technique in steps seven and eight.
## Related Topics
* {@link guide/filter Angular Filters}
* {@link forms Angular Forms}
* {@link guide/filter Filters}
* {@link forms Forms}
## Related API
@@ -7,11 +7,11 @@ comes with almost no help from the compiler. For this reason we feel very strong
written in JavaScript needs to come with a strong set of tests. We have built many features into
Angular which makes testing your Angular applications easy. So there is no excuse for not testing.
# It is all about NOT mixing concerns
# Separation of Concerns
Unit testing as the name implies is about testing individual units of code. Unit tests try to
answer questions such as "Did I think about the logic correctly?" or "Does the sort function order the list
in the right order?"
answer questions such as "Did I think about the logic correctly?" or "Does the sort function order
the list in the right order?"
In order to answer such a question it is very important that we can isolate the unit of code under test.
That is because when we are testing the sort function we don't want to be forced into creating
@@ -33,8 +33,8 @@ function has mutated the DOM in the right way.
## With great power comes great responsibility
Angular is written with testability in mind, but it still requires that you do the right thing.
We tried to make the right thing easy, but Angular is not magic. If you don't follow these guidelines
you may very well end up with an untestable application.
We tried to make the right thing easy, but if you ignore these guidelines you may end up with an
untestable application.
## Dependency Injection
There are several ways in which you can get a hold of a dependency. You can:
@@ -65,7 +65,7 @@ function MyClass() {
A problem surfaces in tests when we would like to instantiate a `MockXHR` that would
allow us to return fake data and simulate network failures. By calling `new XHR()` we are
permanently bound to the actual XHR and there is no way to replace it. Yes, we could monkey
permanently bound to the actual XHR and there is no way to replace it. Yes, we could monkey
patch, but that is a bad idea for many reasons which are outside the scope of this document.
Here's an example of how the class above becomes hard to test when resorting to monkey patching:
@@ -96,7 +96,7 @@ function MyClass() {
```
While no new dependency instance is created, it is fundamentally the same as `new` in
that no way exists to intercept the call to `global.xhr` for testing purposes, other then
that no way exists to intercept the call to `global.xhr` for testing purposes, other than
through monkey patching. The basic issue for testing is that a global variable needs to be mutated in
order to replace it with call to a mock method. For further explanation of why this is bad see: [Brittle Global
State & Singletons](http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/)
@@ -133,7 +133,7 @@ function MyClass() {
However, where does the serviceRegistry come from? If it is:
* `new`-ed up, the test has no chance to reset the services for testing.
* a global look-up then the service returned is global as well (but resetting is easier, since
* a global look-up then the service returned is global as well (but resetting is easier, since
only one global variable exists to be reset).
The class above is hard to test since we have to change the global state:
@@ -258,7 +258,7 @@ expect($scope.strength).toEqual('weak');
```
Notice that the test is not only much shorter, it is also easier to follow what is happening. We say
that such a test tells a story, rather then asserting random bits which don't seem to be related.
that such a test tells a story, rather than asserting random bits which don't seem to be related.
## Filters
{@link ng.$filterProvider Filters} are functions which transform the data into a user readable
@@ -296,7 +296,7 @@ Now we can add a directive to our app.
app.directive('aGreatEye', function () {
return {
restrict: 'E',
replace: true,
replace: true,
template: '<h1>lidless, wreathed in flame, {{1 + 1}} times</h1>'
};
});
@@ -321,7 +321,7 @@ describe('Unit testing great quotes', function() {
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
it('Replaces the element with the appropriate content', function() {
// Compile a piece of HTML containing the directive
var element = $compile("<a-great-eye></a-great-eye>")($rootScope);
@@ -337,6 +337,12 @@ We inject the $compile service and $rootScope before each jasmine test. The $com
to render the aGreatEye directive. After rendering the directive we ensure that the directive has
replaced the content and "lidless, wreathed in flame, 2 times" is present.
### Testing Directives With External Templates
If your directive uses `templateUrl`, consider using
[karma-ng-html2js-preprocessor](https://github.com/karma-runner/karma-ng-html2js-preprocessor)
to pre-compile HTML templates and thus avoid having to load them over HTTP during test execution.
Otherwise you may run into issues if the test directory hierarchy differs from the application's.
## Sample project
See the [angular-seed](https://github.com/angular/angular-seed) project for an example.
+6 -6
View File
@@ -11,12 +11,12 @@ See the [contributing guidelines](https://github.com/angular/angular.js/blob/mas
for how to contribute your own code to AngularJS.
1. {@link #building-and-testing-angularjs_installing-dependencies Installing Dependencies}
2. {@link #building-and-testing-angularjs_forking-angular-on-github Forking Angular on Github}
3. {@link #building-and-testing-angularjs_building-angularjs Building AngularJS}
4. {@link #building-and-testing-angularjs_running-a-local-development-web-server Running a Local Development Web Server}
5. {@link #building-and-testing-angularjs_running-the-unit-test-suite Running the Unit Test Suite}
6. {@link #building-and-testing-angularjs_running-the-end-to-end-test-suite Running the End-to-end Test Suite}
1. {@link misc/contribute#installing-dependencies Installing Dependencies}
2. {@link misc/contribute#forking-angular-on-github Forking Angular on Github}
3. {@link misc/contribute#building-angularjs Building AngularJS}
4. {@link misc/contribute#running-a-local-development-web-server Running a Local Development Web Server}
5. {@link misc/contribute#running-the-unit-test-suite Running the Unit Test Suite}
6. {@link misc/contribute#running-the-end-to-end-test-suite Running the End-to-end Test Suite}
## Installing Dependencies
+1 -1
View File
@@ -72,7 +72,7 @@ The size of the file is < 36KB compressed and minified.
### Can I use the open-source Closure Library with Angular?
Yes, you can use widgets from the [Closure Library](http://code.google.com/closure/library)
Yes, you can use widgets from the [Closure Library](https://developers.google.com/closure/library/)
in Angular.
### Does Angular use the jQuery library?
+7
View File
@@ -1,3 +1,10 @@
@ngdoc overview
@name Miscellaneous
@description
# Miscellaneous Links
- {@link misc/started Getting Started}
- {@link misc/downloading Downloading AngularJS}
- {@link misc/faq Frequently Asked Questions}
- {@link misc/contribute Building AngularJS}
+2
View File
@@ -2,6 +2,8 @@
@name Getting Started
@description
# Getting Started
We want you to have an easy time while starting to use Angular. We've put together the following steps on your path to
becoming an Angular expert.
+230 -85
View File
@@ -3,33 +3,35 @@
@step -1
@description
# PhoneCat Tutorial App
A great way to get introduced to AngularJS is to work through this tutorial, which walks you through
the construction of an AngularJS web app. The app you will build is a catalog that displays a list
of Android devices, lets you filter the list to see only devices that interest you, and then view
details for any device.
<img class="diagram" src="img/tutorial/catalog_screen.png" width="488" height="413">
<img class="diagram" src="img/tutorial/catalog_screen.png" width="488" height="413" alt="demo
application running in the browser">
Work through the tutorial to see how Angular makes browsers smarter — without the use of extensions
or plug-ins. As you work through the tutorial, you will:
Follow the tutorial to see how Angular makes browsers smarter — without the use of native
extensions or plug-ins:
* See examples of how to use client-side data binding and dependency injection to build dynamic
views of data that change immediately in response to user actions.
* See how Angular creates listeners on your data without the need for DOM manipulation.
* Learn a better, easier way to test your web apps.
* Learn how to use Angular services to make common web tasks, such as getting data into your app,
easier.
And all of this works in any browser without modification to the browser!
* See examples of how to use client-side data binding to build dynamic views of data that change
immediately in response to user actions.
* See how Angular keeps your views in synch with your data without the need for DOM manipulation.
* Learn a better, easier way to test your web apps, with Karma and Protractor.
* Learn how to use dependency injection and services to make common web tasks, such as getting data
into your app, easier.
When you finish the tutorial you will be able to:
* Create a dynamic application that works in any browser.
* Define the differences between Angular and common JavaScript frameworks.
* Understand how data binding works in AngularJS.
* Use the angular-seed project to quickly boot-strap your own projects.
* Create and run tests.
* Create a dynamic application that works in all modern browsers.
* Use data binding to wire up your data model to your views.
* Create and run unit tests, with Karma.
* Create and run end to end tests, with Protractor.
* Move application logic out of the template and into Controllers.
* Get data from a server using Angular services.
* Apply animations to your application, using ngAnimate.
* Identify resources for learning more about AngularJS.
The tutorial guides you through the entire process of building a simple application, including
@@ -40,80 +42,223 @@ You can go through the whole tutorial in a couple of hours or you may want to sp
really digging into it. If you're looking for a shorter introduction to AngularJS, check out the
{@link misc/started Getting Started} document.
# Get Started
The rest of this page explains how you can set up your machine to work with the code on your local
machine. If you just want to read the tutorial then you can just go straight to the first step:
[Step 0 - Bootstrapping](tutorial/step_00).
# Working with the code
You can follow this tutorial and hack on the code in either the Mac/Linux or the Windows
environment. The tutorial relies on the use of Git versioning system for source code management.
You don't need to know anything about Git to follow the tutorial. Select one of the tabs below
and follow the instructions for setting up your computer.
You can follow along with this tutorial and hack on the code in the comfort of your own computer.
In this way you can get hands-on practice of really writing AngularJS code and also on using the
recommended testing tools.
<div class="tabbable" show="true">
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux">
<ol>
<li><p>You'll need Git, which you can get from
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
<li><p>Clone the angular-phonecat repository located at
<a href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
<pre>git clone https://github.com/angular/angular-phonecat.git</pre>
<p>This command creates the <code>angular-phonecat</code> directory in your current
directory.</p></li>
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
<pre>cd angular-phonecat</pre>
<p>The tutorial instructions, from now on, assume you are running all commands from the <code>angular-phonecat</code>
directory.</p></li>
<li><p>You will also need Node.js and Karma to run unit tests, so please verify that you have
<a href="http://nodejs.org/">Node.js</a> v0.10 or better installed
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
command in a terminal window:</p></li>
<pre>node --version</pre>
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> and its plugins if you
don't have it already:</p>
<pre>
npm install
</pre></li>
<li><p>You will need an http server running on your system. Mac and Linux machines typically
have Apache pre-installed, but If you don't already have one installed, you can use <code>node</code>
to run a simple bundled http server: <code>node scripts/web-server.js</code>.</p></li>
</ol>
</div>
The tutorial relies on the use of the [Git][git] versioning system for source code management.
You don't need to know anything about Git to follow the tutorial other than how to install and run
a few git commands.
<div class="tab-pane well" id="git-win" title="Git on Windows">
<ol>
<li><p>You will need Node.js and Karma to run unit tests, so please verify that you have
<a href="http://nodejs.org/">Node.js</a> v0.10 or better installed
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
command in a terminal window:</p>
<pre>node --version</pre>
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> if you
don't have it already:</p>
<pre>npm install -g karma</pre>
</li>
<li><p>You'll also need Git, which you can get from
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
<li><p>Clone the angular-phonecat repository located at <a
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
<pre>git clone https://github.com/angular/angular-phonecat.git</pre>
<p>This command creates the <code>angular-phonecat</code> directory in your current directory.</p></li>
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
<pre>cd angular-phonecat</pre>
<p>The tutorial instructions assume you are running all commands from the <code>angular-phonecat</code>
directory.</p>
<p>You should run all <code>git</code> commands from Git bash.</p>
<p>Other commands like <code>test.bat</code> or <code>e2e-test.bat</code> should be
executed from the Windows command line.</li>
<li><p>You need an http server running on your system, but if you don't already have one
already installed, you can use <code>node</code> to run a simple
bundled http server: <code>node scripts\web-server.js</code>.</p></li>
</ol>
</div>
The last thing to do is to make sure your computer has a web browser and a good text editor
installed. Now, let's get some cool stuff done!
### Install Git
{@link step_00 <span class="btn btn-primary">Get Started!</span>}
You can download and install Git from http://git-scm.com/download. Once installed you should have
access to the `git` command line tool. The main commands that you will need to use are:
- `git clone ...` : clone a remote repository onto your local machine
- `git checkout ...` : check out a particular branch or a tagged version of the code to hack on
### Download angular-phonecat
Clone the [angular-phonecat repository][angular-phonecat] located at GitHub by running the following
command:
```
git clone --depth=14 https://github.com/angular/angular-phonecat.git
```
This command creates the `angular-phonecat` directory in your current directory.
<div class="alert alert-info">The `--depth=14` option just tells Git to pull down only the last 14 commits. This makes the
download much smaller and faster.
</div>
Change your current directory to `angular-phonecat`.
```
cd angular-phonecat
```
The tutorial instructions, from now on, assume you are running all commands from the
`angular-phonecat` directory.
### Install Node.js
If you want to run the preconfigured local web-server and the test tools then you will also need
[Node.js v0.10+][node].
You can download a Node.js installer for your operating system from http://nodejs.org/download/.
Check the version of Node.js that you have installed by running the following command:
```
node --version
```
<div class="alert alert-info">If you need to run a different versions of node.js
in your local environment, consider installing
<a href="https://github.com/creationix/nvm" title="Node Version Manager Github Repo link">
Node Version Manager (nvm)
</a>.
</div>
Once you have Node.js installed on your machine you can download the tool dependencies by running:
```
npm install
```
This command will download the following tools, into the `node_modules` directory:
- [Bower][bower] - client-side code package manager
- [Http-Server][http-server] - simple local static web server
- [Karma][karma] - unit test runner
- [Protractor][protractor] - end 2 end test runner
Running `npm install` will also automatically use bower to download the Angular framework into the
`app/bower_components` directory.
<div class="alert alert-info">
Note the angular-phonecat project is setup to install and run these utilities via npm scripts.
This means that you do not have to have any of these utilities installed globally on your system
to follow the tutorial. See **Installing Helper Tools** below for more information.
</div>
The project is preconfigured with a number of npm helper scripts to make it easy to run the common
tasks that you will need while developing:
- `npm start` : start a local development web-server
- `npm test` : start the Karma unit test runner
- `npm run protractor` : run the Protractor end 2 end tests
- `npm run update-webdriver` : install the drivers needed by Protractor
### Install Helper Tools (optional)
The Bower, Http-Server, Karma and Protractor modules are also executables, which can be installed
globally and run directly from a terminal/command prompt. You don't need to do this to follow the
tutorial, but if you decide you do want to run them directly, you can install these modules globally
using, `sudo npm install -g ...`.
For instance to install the Bower command line executable you would do:
```
sudo npm install -g bower
```
*(Omit the sudo if running on Windows)*
Then you can run the bower tool directly, such as:
```
bower install
```
### Running Development Web Server
While Angular applications are purely client-side code, and it is possible to open them in a web
browser directly from the file system, it is better to serve them from a HTTP web server. In
particular, for security reasons, most modern browsers will not allow JavaScript to make server
requests if the page is loaded directly from the file system.
The angular-phonecat project is configured with a simple static web server for hosting the
application during development. Start the web server by running:
```
npm start
```
This will create a local webserver that is listening to port 8000 on your local machine.
You can now browse to the application at:
```
http://localhost:8000/app/index.html
```
### Running Unit Tests
We use unit tests to ensure that the JavaScript code in our application is operating correctly.
Unit tests focus on testing small isolated parts of the application. The unit tests are kept in the
`test/unit` directory.
The angular-phonecat project is configured to use [Karma][karma] to run the unit tests for the
application. Start Karma by running:
```
npm test
```
This will start the Karma unit test runner. Karma will read the configuration file at
`test/karma.conf.js`. This configuration file tells Karma to:
- open up a Chrome browser and connect it to Karma
- execute all the unit tests in this browser
- report the results of these tests in the terminal/command line window
- watch all the project's JavaScript files and re-run the tests whenever any of these change
It is good to leave this running all the time, in the background, as it will give you immediate
feedback about whether your changes pass the unit tests while you are working on the code.
### Running End to End Tests
We use End to End tests to ensure that the application as a whole operates as expected.
End to End tests are designed to test the whole client side application, in particular that the
views are displaying and behaving correctly. It does this by simulating real user interaction with
the real application running in the browser.
The End to End tests are kept in the `test/e2e` directory.
The angular-phonecat project is configured to use [Protractor][protractor] to run the End to End
tests for the application. Protractor relies upon a set of drivers to allow it to interact with
the browser. You can install these drivers by running:
```
npm run update-webdriver
```
*(You should only need to do this once.)*
Since Protractor works by interacting with a running application, we need to start our web server:
```
npm start
```
Then in a separate terminal/command line window, we can run the Protractor test scripts against the
application by running:
```
npm run protractor
```
Protractor will read the configuration file at `test/protractor-conf.js`. This configuration tells
Protractor to:
- open up a Chrome browser and connect it to the application
- execute all the End to End tests in this browser
- report the results of these tests in the terminal/command line window
- close down the browser and exit
It is good to run the end to end tests whenever you make changes to the HTML views or want to check
that the application as a whole is executing correctly. It is very common to run End to End tests
before pushing a new commit of changes to a remote repository.
[git]: http://git-scm.com/
[node]: http://nodejs.org/
[angular-phonecat]: https://github.com/angular/angular-phonecat
[protractor]: https://github.com/angular/protractor
[bower]: http://bower.io/
[http-server]: https://github.com/nodeapps/http-server
[karma]: https://github.com/karma-runner/karma
+29 -69
View File
@@ -11,65 +11,27 @@ with the most important source code files, learn how to start the development se
angular-seed, and run the application in the browser.
<div class="tabbable" show="true" ng-model="$cookies.platformPreference">
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux" value="gitUnix">
<ol>
<li><p>In <code>angular-phonecat</code> directory, run this command:</p>
<pre>git checkout -f step-0</pre>
<p>This resets your workspace to step 0 of the tutorial app.</p>
<p>You must repeat this for every future step in the tutorial and change the number to
the number of the step you are on. This will cause any changes you made within
your working directory to be lost.</p></li>
In `angular-phonecat` directory, run this command:
<li>To see the app running in a browser, do one of the following:
<ul>
<li><b>For node.js users:</b>
<ol>
<li>In a <i>separate</i> terminal tab or window, run <code>node ./scripts/web-server.js</code> to start the web server.</li>
<li>Open a browser window for the app and navigate to <a
href="http://localhost:8000/app/index.html" target="_blank">`http://localhost:8000/app/index.html`</a></li>
</ol>
</li>
<li><b>For other http servers:</b>
<ol>
<li>Configure the server to serve the files in the <code>angular-phonecat</code> directory.</li>
<li>Navigate in your browser to <code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
</ol>
</li>
</ul>
</li>
</ol>
</div>
```
git checkout -f step-0
```
<div class="tab-pane well" id="git-win" title="Git on Windows" value="gitWin">
<ol>
<li><p>Open Git bash and run this command (in <code>angular-phonecat</code> directory):</p>
<pre>git checkout -f step-0</pre>
<p>This resets your workspace to step 0 of the tutorial app.</p>
<p>You must repeat this for every future step in the tutorial and change the number to
the number of the step you are on. This will cause any changes you made within
your working directory to be lost.</p></li>
<li>To see the app running in a browser, do one of the following:
<ul>
<li><b>For node.js users:</b>
<ol>
<li>In a <i>separate</i> terminal tab or window, run <code>node scripts\web-server.js</code> to start the web server.</li>
<li>Open a browser window for the app and navigate to <a href="http://localhost:8000/app/index.html" target="_blank">`http://localhost:8000/app/index.html`</a></li>
</ol>
</li>
<li><b>For other http servers:</b>
<ol>
<li>Configure the server to serve the files in the <code>angular-phonecat</code> directory.</li>
<li>Navigate in your browser to <code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
</ol>
</li>
</ul>
</li>
</ol>
</div>
</div>
This resets your workspace to step 0 of the tutorial app.
You must repeat this for every future step in the tutorial and change the number to the number of
the step you are on. This will cause any changes you made within your working directory to be lost.
If you haven't already done so you need to install the dependencies by running:
```
npm install
```
To see the app running in a browser, open a *separate* terminal/command line tab or window, then
run `npm start` to start the web server. Now, open a browser window for the app and navigate to
<a href="http://localhost:8000/app/index.html" target="_blank">`http://localhost:8000/app/index.html`</a>
You can now see the page in your browser. It's not very exciting, but that's OK.
@@ -84,9 +46,9 @@ __`app/index.html`:__
<head>
<meta charset="utf-8">
<title>My HTML File</title>
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">
<link rel="stylesheet" href="css/app.css">
<link rel="stylesheet" href="css/bootstrap.css">
<script src="lib/angular/angular.js"></script>
<script src="bower_components/angular/angular.js"></script>
</head>
<body>
@@ -105,7 +67,7 @@ __`app/index.html`:__
<html ng-app>
The `ng-app` attribute represents an Angular directive named `ngApp` (Angular uses
`name-with-dashes` for its custom attributes and `camelCase` for the corresponding directives
`spinal-case` for its custom attributes and `camelCase` for the corresponding directives
which implement them).
This directive is used to flag the html element that Angular should consider to be the root element
of our application.
@@ -114,7 +76,7 @@ __`app/index.html`:__
* AngularJS script tag:
<script src="lib/angular/angular.js">
<script src="bower_components/angular/angular.js">
This code downloads the `angular.js` script and registers a callback that will be executed by the
browser when the containing HTML page is fully downloaded. When the callback is executed, Angular
@@ -171,16 +133,18 @@ and one static binding, and our model is empty. That will soon change!
## What are all these files in my working directory?
Most of the files in your working directory come from the [angular-seed project](https://github.com/angular/angular-seed) which is typically used to bootstrap
new Angular projects. The seed project includes the latest Angular libraries, test libraries,
scripts and a simple example app, all pre-configured for developing a typical web app.
Most of the files in your working directory come from the [angular-seed project][angular-seed] which
is typically used to bootstrap new Angular projects. The seed project is pre-configured to install
the angular framework (via `bower` into the `app/bower_components/` folder) and tools for developing
a typical web app (via `npm`).
For the purposes of this tutorial, we modified the angular-seed with the following changes:
* Removed the example app
* Added phone images to `app/img/phones/`
* Added phone data files (JSON) to `app/phones/`
* Added [Bootstrap](http://getbootstrap.com) files to `app/css/` and `app/img/`
* Added a dependency on [Bootstrap](http://getbootstrap.com) in the `bower.json` file.
@@ -199,9 +163,5 @@ Now let's go to {@link step_01 step 1} and add some content to the web app.
<ul doc-tutorial-nav="0"></ul>
<div style="display: none">
Note: During the bootstrap the injector and the root scope will then be associated with the
element on which the `ngApp` directive was declared, so when debugging the app you can retrieve
them from browser console via `angular.element(rootElement).scope()` and
`angular.element(rootElement).injector()`.
</div>
[angular-seed]: https://github.com/angular/angular-seed
+2 -5
View File
@@ -12,15 +12,12 @@ dynamically display the same result with any set of data.
In this step you will add some basic information about two cell phones to an HTML page.
- The page now contains a list with information about two phones.
<div doc-tutorial-reset="1"></div>
The page now contains a list with information about two phones.
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-0...step-1):
__`app/index.html`:__
**`app/index.html`:**
```html
<ul>
+71 -66
View File
@@ -9,20 +9,16 @@
Now it's time to make the web page dynamic — with AngularJS. We'll also add a test that verifies the
code for the controller we are going to add.
There are many ways to structure the code for an application. For Angular apps, we encourage the
use of [the Model-View-Controller (MVC)
design pattern](http://en.wikipedia.org/wiki/ModelViewController) to decouple the code and to separate concerns. With that in mind, let's use a
little Angular and JavaScript to add model, view, and controller components to our app.
There are many ways to structure the code for an application. For Angular apps, we encourage the use of
[the Model-View-Controller (MVC) design pattern](http://en.wikipedia.org/wiki/ModelViewController)
to decouple the code and to separate concerns. With that in mind, let's use a little Angular and
JavaScript to add model, view, and controller components to our app.
- The list of three phones is now generated dynamically from data
<div doc-tutorial-reset="2"></div>
The app now contains a list with three phones.
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-1...step-2):
## View and Template
In Angular, the __view__ is a projection of the model through the HTML __template__. This means that
@@ -37,7 +33,7 @@ __`app/index.html`:__
<html ng-app="phonecatApp">
<head>
...
<script src="lib/angular/angular.js"></script>
<script src="bower_components/angular/angular.js"></script>
<script src="js/controllers.js"></script>
</head>
<body ng-controller="PhoneListCtrl">
@@ -53,21 +49,21 @@ __`app/index.html`:__
</html>
```
We replaced the hard-coded phone list with the
{@link ng.directive:ngRepeat ngRepeat directive} and two
{@link guide/expression Angular expressions} enclosed in curly braces:
`{{phone.name}}` and `{{phone.snippet}}`:
We replaced the hard-coded phone list with the {@link ng.directive:ngRepeat ngRepeat directive}
and two {@link guide/expression Angular expressions}:
* The `ng-repeat="phone in phones"` statement in the `<li>` tag is an Angular repeater. The
repeater tells Angular to create a `<li>` element for each phone in the list using the first `<li>`
* The `ng-repeat="phone in phones"` attribute in the `<li>` tag is an Angular repeater directive.
The repeater tells Angular to create a `<li>` element for each phone in the list using the `<li>`
tag as the template.
* The expressions wrapped in curly braces (`{{phone.name}}` and `{{phone.snippet}}`) will be replaced
by the value of the expressions.
We have added a new directive, called `ng-controller`, which attaches a `PhoneListCtrl`
__controller__ to the DOM at this point.
__controller__ to the DOM at this point:
* As we've learned in {@link step_00 step 0}, the curly braces around `phone.name` and `phone.snippet` denote
bindings. As opposed to evaluating constants, these expressions are referring to our application
model, which was set up in our `PhoneListCtrl` controller.
* The expressions in curly braces (`{{phone.name}}` and `{{phone.snippet}}` denote
bindings, which are referring to our application model, which is set up in our `PhoneListCtrl`
controller.
<img class="diagram" src="img/tutorial/tutorial_02.png">
@@ -128,23 +124,19 @@ To learn more about Angular scopes, see the {@link ng.$rootScope.Scope angular s
## Tests
The "Angular way" of separating controller from the view, makes it easy to test code as it is being
developed. If our controller is available on the global namespace then we can simply instantiate it
with a mock `scope` object. Take a look at the following unit test for our controller:
__`test/unit/controllersSpec.js`:__
developed. If our controller is available on the global namespace then we could simply instantiate it
with a mock `scope` object:
```js
describe('PhoneCat controllers', function() {
describe('PhoneListCtrl', function(){
describe('PhoneListCtrl', function(){
it('should create "phones" model with 3 phones', function() {
var scope = {},
ctrl = new PhoneListCtrl(scope);
it('should create "phones" model with 3 phones', function() {
var scope = {},
ctrl = new PhoneListCtrl(scope);
expect(scope.phones.length).toBe(3);
});
expect(scope.phones.length).toBe(3);
});
});
```
@@ -154,67 +146,72 @@ Angular. Since testing is such a critical part of software development, we make
tests in Angular so that developers are encouraged to write them.
### Testing non-Global Controllers
In practice, you will not want to have your controller functions in the global namespace. Instead,
we have registered our controllers in the `phonecatApp` module. In this case Angular provides a
service, `$controller`, which will retrieve your controller by name. Here is the same test using
`$controller`:
In practice, you will not want to have your controller functions in the global namespace. Instead,
you can see that we have registered it via an anonymous constructor function on the `phonecatApp`
module.
In this case Angular provides a service, `$controller`, which will retrieve your controller by name.
Here is the same test using `$controller`:
__`test/unit/controllersSpec.js`:__
```js
describe('PhoneCat controllers', function() {
describe('PhoneListCtrl', function(){
beforeEach(module('phonecatApp'));
describe('PhoneListCtrl', function(){
it('should create "phones" model with 3 phones', inject(function($controller) {
var scope = {},
ctrl = $controller('PhoneListCtrl', {$scope:scope});
it('should create "phones" model with 3 phones', inject(function($controller) {
var scope = {},
ctrl = $controller('PhoneListCtrl', { $scope: scope });
expect(scope.phones.length).toBe(3);
}));
expect(scope.phones.length).toBe(3);
}));
});
});
```
Don't forget that we need to load up the `phonecatApp` module into the test so that the controller
is available to be injected.
* Before each test we tell Angular to load the `phonecatApp` module.
* We ask Angular to `inject` the `$controller` service into our test function
* We use `$controller` to create an instance of the `PhoneListCtrl`
* With this instance, we verify that the phones array property on the scope contains three records.
### Writing and Running Tests
Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework when
writing tests. Although Angular does not require you to use Jasmine, we wrote all of the tests in
this tutorial in Jasmine. You can learn about Jasmine on the [Jasmine home page](http://pivotal.github.com/jasmine/) and at the [Jasmine docs](http://pivotal.github.io/jasmine/).
this tutorial in Jasmine v1.3. You can learn about Jasmine on the [Jasmine home page][jasmine] and
at the [Jasmine docs][jasmine-docs].
The angular-seed project is pre-configured to run all unit tests using [Karma](http://karma-runner.github.io/). Ensure that the necessary karma plugins are installed.
You can do this by issuing `npm install` into your terminal.
The angular-seed project is pre-configured to run unit tests using [Karma][karma] but you will need
to ensure that Karma and its necessary plugins are installed. You can do this by running
`npm install`.
To run the tests, and then watch the files for changes: `npm test`.
To run the test, do the following:
1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run
`./scripts/test.sh` (if you are on Windows, run scripts\test.bat) to start the Karma server (the
config file necessary to start the server is located at `./config/karma.conf.js`).
2. Karma will start a new instance of Chrome browser automatically. Just ignore it and let it run in
* Karma will start a new instance of Chrome browser automatically. Just ignore it and let it run in
the background. Karma will use this browser for test execution.
* You should see the following or similar output in the terminal:
3. You should see the following or similar output in the terminal:
info: Karma server started at http://localhost:9876/
info (launcher): Starting browser "Chrome"
info (Chrome 22.0): Connected on socket id tPUm9DXcLHtZTKbAEO-n
Chrome 22.0: Executed 1 of 1 SUCCESS (0.093 secs / 0.004 secs)
<pre>
info: Karma server started at http://localhost:9876/
info (launcher): Starting browser "Chrome"
info (Chrome 22.0): Connected on socket id tPUm9DXcLHtZTKbAEO-n
Chrome 22.0: Executed 1 of 1 SUCCESS (0.093 secs / 0.004 secs)
</pre>
Yay! The test passed! Or not...
4. To rerun the tests, just change any of the source or test .js files. Karma will notice the change
* To rerun the tests, just change any of the source or test .js files. Karma will notice the change
and will rerun the tests for you. Now isn't that sweet?
# Experiments
* Add another binding to `index.html`. For example:
<p>Total number of phones: {{phones.length}}</p>
```html
<p>Total number of phones: {{phones.length}}</p>
```
* Create a new model property in the controller and bind to it from the template. For example:
@@ -226,7 +223,11 @@ To run the test, do the following:
Refresh your browser and verify that it says "Hello, World!".
* Create a repeater that constructs a simple table:
* Update the unit test for the controller in ./tests/unit/controllersSpec.js to reflect the previous change. For example by adding:
expect(scope.name).toBe('World');
* Create a repeater in `index.html` that constructs a simple table:
<table>
<tr><th>row number</th></tr>
@@ -251,3 +252,7 @@ to the app.
<ul doc-tutorial-nav="2"></ul>
[jasmine]: http://jasmine.github.io/
[jasmine-docs]: http://jasmine.github.io/1.3/introduction.html
[karma]: http://karma-runner.github.io/
+66 -74
View File
@@ -11,15 +11,10 @@ simple; we will add full text search (yes, it will be simple!). We will also wri
test, because a good end-to-end test is a good friend. It stays with your app, keeps an eye on it,
and quickly detects regressions.
<div doc-tutorial-reset="3"></div>
The app now has a search box. Notice that the phone list on the page changes depending on what a
* The app now has a search box. Notice that the phone list on the page changes depending on what a
user types into the search box.
The most important differences between Steps 2 and 3 are listed below. You can see the full diff on
[GitHub](https://github.com/angular/angular-phonecat/compare/step-2...step-3):
<div doc-tutorial-reset="3"></div>
## Controller
@@ -33,14 +28,14 @@ __`app/index.html`:__
```html
<div class="container-fluid">
<div class="row-fluid">
<div class="span2">
<div class="row">
<div class="col-md-2">
<!--Sidebar content-->
Search: <input ng-model="query">
</div>
<div class="span10">
<div class="col-md-10">
<!--Body content-->
<ul class="phones">
@@ -96,104 +91,101 @@ describe('PhoneCat App', function() {
describe('Phone list view', function() {
beforeEach(function() {
browser().navigateTo('../../app/index.html');
browser.get('app/index.html');
});
it('should filter the phone list as user types into the search box', function() {
expect(repeater('.phones li').count()).toBe(3);
input('query').enter('nexus');
expect(repeater('.phones li').count()).toBe(1);
var phoneList = element.all(by.repeater('phone in phones'));
var query = element(by.model('query'));
input('query').enter('motorola');
expect(repeater('.phones li').count()).toBe(2);
expect(phoneList.count()).toBe(3);
query.sendKeys('nexus');
expect(phoneList.count()).toBe(1);
query.clear();
query.sendKeys('motorola');
expect(phoneList.count()).toBe(2);
});
});
});
```
Even though the syntax of this test looks very much like our controller unit test written with
Jasmine, the end-to-end test uses APIs of {@link guide/dev_guide.e2e-testing Angular's end-to-end
test runner}.
To run the end-to-end test, open one of the following in a new browser tab:
* node.js users: http://localhost:8000/test/e2e/runner.html
* users with other http servers:
`http://localhost:[port-number]/[context-path]/test/e2e/runner.html`
* casual reader: http://angular.github.com/angular-phonecat/step-3/test/e2e/runner.html
Previously we've seen how Karma can be used to execute unit tests. Well, it can also run the
end-to-end tests! Use `./scripts/e2e-test.sh` (if you are on Windows, run `scripts\e2e-test.bat`) script for that. End-to-end tests are slow, so unlike
with unit tests, Karma will exit after the test run and will not automatically rerun the test
suite on every file change. To rerun the test suite, execute the `e2e-test.sh` or `e2e-test.bat` script again.
Note: You must ensure you've installed the karma-ng-scenario framework plugin prior to running the
`e2e-test.sh` script. You can do this by issuing `npm install` into your terminal.
This test verifies that the search box and the repeater are correctly wired together. Notice how
easy it is to write end-to-end tests in Angular. Although this example is for a simple test, it
really is that easy to set up any functional, readable, end-to-end test.
### Running End to End Tests with Protractor
Even though the syntax of this test looks very much like our controller unit test written with
Jasmine, the end-to-end test uses APIs of [Protractor](https://github.com/angular/protractor). Read
about the Protractor APIs at https://github.com/angular/protractor/blob/master/docs/api.md.
Much like Karma is the test runner for unit tests, we use Protractor to run end-to-end tests.
Try it with `npm run protractor`. End-to-end tests are slow, so unlike with unit tests, Protractor
will exit after the test run and will not automatically rerun the test suite on every file change.
To rerun the test suite, execute `npm run protractor` again.
<div class="alert alert-info">
Note: You must ensure you've installed the protractor and updated webdriver prior to running the
`npm run protractor`. You can do this by issuing `npm install` and `npm run update-webdriver` into
your terminal.
</div>
# Experiments
* Display the current value of the `query` model by adding a `{{query}}` binding into the
### Display Current Query
Display the current value of the `query` model by adding a `{{query}}` binding into the
`index.html` template, and see how it changes when you type in the input box.
* Let's see how we can get the current value of the `query` model to appear in the HTML page title.
### Display Query in Title
Let's see how we can get the current value of the `query` model to appear in the HTML page title.
You might think you could just add the `{{query}}` to the title tag element as follows:
* Add the following end-to-end test into the `describe` block within `test/e2e/scenarios.js`:
<title>Google Phone Gallery: {{query}}</title>
```js
it('should display the current filter value in the title bar', function() {
expect(browser.getTitle()).toMatch(/Google Phone Gallery:\s*$/);
element(by.model('query')).sendKeys('nexus');
expect(browser.getTitle()).toMatch(/Google Phone Gallery: nexus$/);
});
```
Run protractor (`npm run protractor`) to see this test fail.
* You might think you could just add the `{{query}}` to the title tag element as follows:
<title>Google Phone Gallery: {{query}}</title>
However, when you reload the page, you won't see the expected result. This is because the "query"
model lives in the scope, defined by the `ng-controller="PhoneListCtrl"` directive, on the body element:
model lives in the scope, defined by the `ng-controller="PhoneListCtrl"` directive, on the body
element:
<body ng-controller="PhoneListCtrl">
If you want to bind to the query model from the `<title>` element, you must __move__ the
`ngController` declaration to the HTML element because it is the common parent of both the body
and title elements:
`ngController` declaration to the HTML element because it is the common parent of both the body
and title elements:
<html ng-app="phonecatApp" ng-controller="PhoneListCtrl">
Be sure to __remove__ the `ng-controller` declaration from the body element.
While using double curlies works fine within the title element, you might have noticed that
* Re-run `npm run protractor` to see the test now pass.
* While using double curlies works fine within the title element, you might have noticed that
for a split second they are actually displayed to the user while the page is loading. A better
solution would be to use the {@link ng.directive:ngBind
ngBind} or {@link ng.directive:ngBindTemplate
ngBindTemplate} directives, which are invisible to the user while the page is loading:
solution would be to use the {@link ng.directive:ngBind ngBind} or
{@link ng.directive:ngBindTemplate ngBindTemplate} directives, which are invisible to the user
while the page is loading:
<title ng-bind-template="Google Phone Gallery: {{query}}">Google Phone Gallery</title>
* Add the following end-to-end test into the `describe` block within `test/e2e/scenarios.js`:
```js
it('should display the current filter value within an element with id "status"',
function() {
expect(element('#status').text()).toMatch(/Current filter: \s*$/);
input('query').enter('nexus');
expect(element('#status').text()).toMatch(/Current filter: nexus\s*$/);
//alternative version of the last assertion that tests just the value of the binding
using('#status').expect(binding('query')).toBe('nexus');
});
```
Refresh the browser tab with the end-to-end test runner to see the test fail. To make the test
pass, edit the `index.html` template to add a `div` or `p` element with `id` `"status"` and content
with the `query` binding, prefixed by "Current filter:". For instance:
<div id="status">Current filter: {{query}}</div>
* Add a `pause()` statement inside of an end-to-end test and rerun it. You'll see the runner pause;
this gives you the opportunity to explore the state of your application while it is displayed in
the browser. The app is live! You can change the search query to prove it. Notice how useful this
is for troubleshooting end-to-end tests.
<title ng-bind-template="Google Phone Gallery: {{query}}">Google Phone Gallery</title>
# Summary
+29 -25
View File
@@ -10,17 +10,13 @@ In this step, you will add a feature to let your users control the order of the
list. The dynamic ordering is implemented by creating a new model property, wiring it together with
the repeater, and letting the data binding magic do the rest of the work.
* In addition to the search box, the app displays a drop down menu that allows users to control the
order in which the phones are listed.
<div doc-tutorial-reset="4"></div>
You should see that in addition to the search box, the app displays a drop down menu that allows
users to control the order in which the phones are listed.
The most important differences between Steps 3 and 4 are listed below. You can see the full diff on
[GitHub](https://github.com/angular/angular-phonecat/compare/step-3...step-4):
## Template
__`app/index.html`:__
@@ -91,7 +87,7 @@ phonecatApp.controller('PhoneListCtrl', function ($scope) {
record. This property is used to order phones by age.
* We added a line to the controller that sets the default value of `orderProp` to `age`. If we had
not set a default value here, the `orderBy` filter would remain uninitialized until our
not set a default value here, the `orderBy` filter would remain uninitialized until our
user picked an option from the drop down menu.
This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the
@@ -117,7 +113,7 @@ describe('PhoneCat controllers', function() {
var scope, ctrl;
beforeEach(module('phonecatApp'));
beforeEach(inject(function($controller) {
scope = {};
ctrl = $controller('PhoneListCtrl', {$scope:scope});
@@ -143,7 +139,7 @@ shared by all tests in the parent `describe` block.
You should now see the following output in the Karma tab:
Chrome 22.0: Executed 2 of 2 SUCCESS (0.021 secs / 0.001 secs)
<pre>Chrome 22.0: Executed 2 of 2 SUCCESS (0.021 secs / 0.001 secs)</pre>
Let's turn our attention to the end-to-end test.
@@ -152,28 +148,36 @@ __`test/e2e/scenarios.js`:__
```js
...
it('should be possible to control phone order via the drop down select box',
function() {
//let's narrow the dataset to make the test assertions shorter
input('query').enter('tablet');
it('should be possible to control phone order via the drop down select box', function() {
expect(repeater('.phones li', 'Phone List').column('phone.name')).
toEqual(["Motorola XOOM\u2122 with Wi-Fi",
"MOTOROLA XOOM\u2122"]);
var phoneNameColumn = element.all(by.repeater('phone in phones').column('{{phone.name}}'));
var query = element(by.model('query'));
select('orderProp').option('Alphabetical');
function getNames() {
return phoneNameColumn.map(function(elm) {
return elm.getText();
});
}
expect(repeater('.phones li', 'Phone List').column('phone.name')).
toEqual(["MOTOROLA XOOM\u2122",
"Motorola XOOM\u2122 with Wi-Fi"]);
});
...
query.sendKeys('tablet'); //let's narrow the dataset to make the test assertions shorter
expect(getNames()).toEqual([
"Motorola XOOM\u2122 with Wi-Fi",
"MOTOROLA XOOM\u2122"
]);
element(by.model('orderProp')).findElement(by.css('option[value="name"]')).click();
expect(getNames()).toEqual([
"MOTOROLA XOOM\u2122",
"Motorola XOOM\u2122 with Wi-Fi"
]);
});...
```
The end-to-end test verifies that the ordering mechanism of the select box is working correctly.
You can now rerun `./scripts/e2e-test.sh` or refresh the browser tab with the end-to-end test
`runner.html` to see the tests run, or you can see them running on [Angular's server](http://angular.github.com/angular-phonecat/step-4/test/e2e/runner.html).
You can now rerun `npm run protractor` to see the tests run.
# Experiments
+29 -31
View File
@@ -7,20 +7,16 @@
Enough of building an app with three phones in a hard-coded dataset! Let's fetch a larger dataset
from our server using one of Angular's built-in {@link guide/dev_guide.services services} called {@link
from our server using one of Angular's built-in {@link guide/services services} called {@link
ng.$http $http}. We will use Angular's {@link guide/di dependency
injection (DI)} to provide the service to the `PhoneListCtrl` controller.
* There are now a list of 20 phones, loaded from the server.
<div doc-tutorial-reset="5"></div>
You should now see a list of 20 phones.
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-4...step-5):
## Data
The `app/phones/phones.json` file in your project is a dataset that contains a larger list of phones
stored in the JSON format.
@@ -44,7 +40,7 @@ Following is a sample of the file:
We'll use Angular's {@link ng.$http $http} service in our controller to make an HTTP
request to your web server to fetch the data in the `app/phones/phones.json` file. `$http` is just
one of several built-in {@link guide/dev_guide.services angular services} that handle common operations
one of several built-in {@link guide/services Angular services} that handle common operations
in web apps. Angular injects these services for you where you need them.
Services are managed by Angular's {@link guide/di DI subsystem}. Dependency injection
@@ -74,10 +70,10 @@ tutorial.)
The `$http` service returns a {@link ng.$q promise object} with a `success`
method. We call this method to handle the asynchronous response and assign the phone data to the
scope controlled by this controller, as a model called `phones`. Notice that angular detected the
scope controlled by this controller, as a model called `phones`. Notice that Angular detected the
json response and parsed it for us!
To use a service in angular, you simply declare the names of the dependencies you need as arguments
To use a service in Angular, you simply declare the names of the dependencies you need as arguments
to the controller's constructor function, as follows:
phonecatApp.controller('PhoneListCtrl', function ($scope, $http) {...}
@@ -96,7 +92,7 @@ dependencies.
### `$` Prefix Naming Convention
You can create your own services, and in fact we will do exactly that in step 11. As a naming
convention, angular's built-in services, Scope methods and a few other Angular APIs have a `$`
convention, Angular's built-in services, Scope methods and a few other Angular APIs have a `$`
prefix in front of the name.
The `$` prefix is there to namespace Angular-provided services.
@@ -109,28 +105,30 @@ properties are considered private, and should not be accessed or modified.
### A Note on Minification
Since Angular infers the controller's dependencies from the names of arguments to the controller's
constructor function, if you were to [minify](http://goo.gl/SAnnsm) the JavaScript code for `PhoneListCtrl` controller, all of its function arguments would be
minified as well, and the dependency injector would not be able to identify services correctly.
constructor function, if you were to [minify](http://goo.gl/SAnnsm) the JavaScript code for
`PhoneListCtrl` controller, all of its function arguments would be minified as well, and the
dependency injector would not be able to identify services correctly.
There are two ways to overcome issues caused by minification:
We can overcome this problem by annotating the function with the names of the dependencies, provided
as strings, which will not get minified. There are two ways to provide these injection annotations:
* You can create a `$inject` property on the controller function which holds an array of strings.
* Create a `$inject` property on the controller function which holds an array of strings.
Each string in the array is the name of the service to inject for the corresponding parameter.
In the case of our example we would write:
In our example we would write:
```js
function PhoneListCtrl($scope, $http) {...}
PhoneListCtrl.$inject = ['$scope', '$http'];
phonecatApp.controller('PhoneListCtrl', PhoneListCtrl);
```
```js
function PhoneListCtrl($scope, $http) {...}
PhoneListCtrl.$inject = ['$scope', '$http'];
phonecatApp.controller('PhoneListCtrl', PhoneListCtrl);
```
* Use the inline bracket notation which wraps the function to be injected into an array of strings
(representing the dependency names) followed by the function to be injected:
* Use an inline annotation where, instead of just providing the function, you provide an array.
This array contains a list of the service names, followed by the function itself.
```js
function PhoneListCtrl($scope, $http) {...}
phonecatApp.controller('PhoneListCtrl', ['$scope', '$http', PhoneListCtrl]);
```
```js
function PhoneListCtrl($scope, $http) {...}
phonecatApp.controller('PhoneListCtrl', ['$scope', '$http', PhoneListCtrl]);
```
Both of these methods work with any function that can be injected by Angular, so it's up to your
project's style guide to decide which one you use.
@@ -166,9 +164,9 @@ __`test/unit/controllersSpec.js`:__
Because we started using dependency injection and our controller has dependencies, constructing the
controller in our tests is a bit more complicated. We could use the `new` operator and provide the
constructor with some kind of fake `$http` implementation. However, the recommended (and easier) way
is to create a controller in the test environment in the same way that angular does it in the
production code behind the scenes, as follows:
constructor with some kind of fake `$http` implementation. However, Angular provides a mock `$http`
service that we can use in unit tests. We configure "fake" responses to server requests by calling
methods on a service called $httpBackend:
```js
describe('PhoneCat controllers', function() {
@@ -252,7 +250,7 @@ Finally, we verify that the default value of `orderProp` is set correctly:
You should now see the following output in the Karma tab:
Chrome 22.0: Executed 2 of 2 SUCCESS (0.028 secs / 0.007 secs)
<pre>Chrome 22.0: Executed 2 of 2 SUCCESS (0.028 secs / 0.007 secs)</pre>
@@ -269,7 +267,7 @@ to the first 5 in the list. Use the following code in the `$http` callback:
# Summary
Now that you have learned how easy it is to use angular services (thanks to Angular's dependency
Now that you have learned how easy it is to use Angular services (thanks to Angular's dependency
injection), go to {@link step_06 step 6}, where you will add some
thumbnail images of phones and some links.
+9 -12
View File
@@ -10,15 +10,10 @@ In this step, you will add thumbnail images for the phones in the phone list, an
now, will go nowhere. In subsequent steps you will use the links to display additional information
about the phones in the catalog.
* There are now links and images of the phones in the list.
<div doc-tutorial-reset="6"></div>
You should now see links and images of the phones in the list.
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-5...step-6):
## Data
Note that the `phones.json` file contains unique ids and image urls for each of the phones. The
@@ -63,7 +58,7 @@ the element attribute.
We also added phone images next to each record using an image tag with the {@link
ng.directive:ngSrc ngSrc} directive. That directive prevents the
browser from treating the angular `{{ expression }}` markup literally, and initiating a request to
browser from treating the Angular `{{ expression }}` markup literally, and initiating a request to
invalid url `http://localhost:8000/app/{{phone.imageUrl}}`, which it would have done if we had only
specified an attribute binding in a regular `src` attribute (`<img src="{{phone.imageUrl}}">`).
Using the `ngSrc` directive prevents the browser from making an http request to an invalid location.
@@ -76,9 +71,12 @@ __`test/e2e/scenarios.js`__:
```js
...
it('should render phone specific links', function() {
input('query').enter('nexus');
element('.phones li a').click();
expect(browser().location().url()).toBe('/phones/nexus-s');
var query = element(by.model('query'));
query.sendKeys('nexus');
element(by.css('.phones li a')).click();
browser.getLocationAbsUrl().then(function(url) {
expect(url.split('#')[1]).toBe('/phones/nexus-s');
});
});
...
```
@@ -86,8 +84,7 @@ __`test/e2e/scenarios.js`__:
We added a new end-to-end test to verify that the app is generating correct links to the phone
views that we will implement in the upcoming steps.
You can now rerun `./scripts/e2e-test.sh` or refresh the browser tab with the end-to-end test
runner to see the tests run, or you can see them running on [Angular's server](http://angular.github.com/angular-phonecat/step-6/test/e2e/runner.html).
You can now rerun `npm run protractor` to see the tests run.
# Experiments
+169 -124
View File
@@ -7,18 +7,50 @@
In this step, you will learn how to create a layout template and how to build an app that has
multiple views by adding routing.
multiple views by adding routing, using an Angular module called 'ngRoute'.
* When you now navigate to `app/index.html`, you are redirected to `app/index.html#/phones`
and the phone list appears in the browser.
* When you click on a phone link the url changes to one specific to that phone and the stub of a
phone detail page is displayed.
<div doc-tutorial-reset="7"></div>
## Dependencies
Note that when you now navigate to `app/index.html`, you are redirected to `app/index.html#/phones`
and the same phone list appears in the browser. When you click on a phone link the stub of a phone
detail page is displayed.
The routing functionality added by this step is provided by angular in the `ngRoute` module, which
is distributed separately from the core Angular framework.
We are using [Bower][bower] to install client side dependencies. This step updates the
`bower.json` configuration file to include the new dependency:
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-6...step-7).
```json
{
"name": "angular-seed",
"description": "A starter project for AngularJS",
"version": "0.0.0",
"homepage": "https://github.com/angular/angular-seed",
"license": "MIT",
"private": true,
"dependencies": {
"angular": "1.2.x",
"angular-mocks": "~1.2.x",
"bootstrap": "~3.1.1",
"angular-route": "~1.2.x"
}
}
```
The new dependency `"angular-route": "~1.2.x"` tells bower to install a version of the
angular-route component that is compatible with version 1.2.x. We must tell bower to download
and install this dependency.
If you have bower installed globally then you can run `bower install` but for this project we have
preconfigured npm to run bower install for us:
```
npm install
```
## Multiple Views, Routing and Layout Template
@@ -34,12 +66,11 @@ template into what we call a "layout template". This is a template that is commo
our application. Other "partial templates" are then included into this layout template depending on
the current "route" — the view that is currently displayed to the user.
Application routes in Angular are declared via the
{@link ngRoute.$routeProvider $routeProvider}, which is the provider of the
{@link ngRoute.$route $route service}. This service makes it easy to wire together
controllers, view templates, and the current
URL location in the browser. Using this feature we can implement [deep linking](http://en.wikipedia.org/wiki/Deep_linking), which lets us utilize the browser's
history (back and forward navigation) and bookmarks.
Application routes in Angular are declared via the {@link ngRoute.$routeProvider $routeProvider},
which is the provider of the {@link ngRoute.$route $route service}. This service makes it easy to
wire together controllers, view templates, and the current URL location in the browser. Using this
feature we can implement [deep linking](http://en.wikipedia.org/wiki/Deep_linking), which lets us
utilize the browser's history (back and forward navigation) and bookmarks.
### A Note About DI, Injector and Providers
@@ -68,107 +99,8 @@ of configuring the injector. As opposed to AMD or require.js modules, Angular mo
solve the problem of script load ordering or lazy script fetching. These goals are totally independent and
both module systems can live side by side and fulfil their goals.
## The App Module
__`app/js/app.js`:__
```js
var phonecatApp = angular.module('phonecatApp', [
'ngRoute',
'phonecatControllers'
]);
phonecatApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/phones', {
templateUrl: 'partials/phone-list.html',
controller: 'PhoneListCtrl'
}).
when('/phones/:phoneId', {
templateUrl: 'partials/phone-detail.html',
controller: 'PhoneDetailCtrl'
}).
otherwise({
redirectTo: '/phones'
});
}]);
```
In order to configure our application with routes, we need to create a module for our application.
We call this module `phonecatApp`. Notice the second argument passed to `angular.module`:
`['ngRoute', 'phonecatControllers']`. This array lists the modules that `phonecatApp` depends on.
Above, we added `angular-route.js` to `index.html`. That's not all we need to do to be able to use
it, however. We also have to add `ngRoute` as a dependency of our app. To improve the organization
of the app, we're going to move the controllers into their own file (as shown below), and call the
module `phonecatControllers`. By listing these two modules as dependencies of `phonecatApp`, we
can use the directives and services they provide.
Thus using the `config` API we request the `$routeProvider` to be injected into our config function
and use the {@link ngRoute.$routeProvider#when `$routeProvider.when`} API to define our routes.
Our application routes are defined as follows:
* The phone list view will be shown when the URL hash fragment is `/phones`. To construct this
view, Angular will use the `phone-list.html` template and the `PhoneListCtrl` controller.
* The phone details view will be shown when the URL hash fragment matches '/phone/:phoneId', where
`:phoneId` is a variable part of the URL. To construct the phone details view, angular will use the
`phone-detail.html` template and the `PhoneDetailCtrl` controller.
We reused the `PhoneListCtrl` controller that we constructed in previous steps and we added a new,
empty `PhoneDetailCtrl` controller to the `app/js/controllers.js` file for the phone details view.
`$routeProvider.otherwise({redirectTo: '/phones'})` triggers a redirection to `/phones` when the browser
address doesn't match either of our routes.
Note the use of the `:phoneId` parameter in the second route declaration. The `$route` service uses
the route declaration — `'/phones/:phoneId'` — as a template that is matched against the current
URL. All variables defined with the `:` notation are extracted into the
{@link ngRoute.$routeParams `$routeParams`} object.
In order for our application to bootstrap with our newly created module we'll also need to specify
the module name as the value of the {@link ng.directive:ngApp ngApp}
directive:
__`app/index.html`:__
```html
<!doctype html>
<html lang="en" ng-app="phonecatApp">
...
```
## Controllers
__`app/js/controllers.js`:__
```js
var phonecatControllers = angular.module('phonecatControllers', []);
phonecatControllers.controller('PhoneListCtrl', ['$scope', '$http',
function ($scope, $http) {
$http.get('phones/phones.json').success(function(data) {
$scope.phones = data;
});
$scope.orderProp = 'age';
}]);
phonecatControllers.controller('PhoneDetailCtrl', ['$scope', '$routeParams',
function($scope, $routeParams) {
$scope.phoneId = $routeParams.phoneId;
}]);
```
Again, note that we created a new module called `phonecatControllers`. For small AngularJS applications,
it's common to create just one module for all of your controllers if there are just a few. For larger apps,
you will probably want to create separate modules for each major feature of your app.
Because our example app is relatively small, we'll add all of our controllers to this module.
To deepen your understanding of DI on Angular, see
[Understanding Dependency Injection](https://github.com/angular/angular.js/wiki/Understanding-Dependency-Injection).
## Template
@@ -177,9 +109,8 @@ ngView} directive. The role of the `ngView` directive is to include the view tem
route into the layout template. This makes it a perfect fit for our `index.html` template.
<div class="alert alert-info">
**Note:** Starting with AngularJS version 1.2, `ngRoute` is in its own module and must be loaded by loading
the `angular-route.js` file distributed with Angular. The easiest way to load the file is to add a `<script>`
tag to your `index.html` file as shown below.
**Note:** Starting with AngularJS version 1.2, `ngRoute` is in its own module and must be loaded by
loading the additional `angular-route.js` file, which we download via Bower above.
</div>
__`app/index.html`:__
@@ -189,8 +120,8 @@ __`app/index.html`:__
<html lang="en" ng-app="phonecatApp">
<head>
...
<script src="lib/angular/angular.js"></script>
<script src="lib/angular/angular-route.js"></script>
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/angular-route/angular-route.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
</head>
@@ -202,6 +133,12 @@ __`app/index.html`:__
</html>
```
We have added two new `<script>` tags in our index file to load up extra JavaScript files into our
application:
- `angular-route.js` : defines the Angular `ngRoute` module, which provides us with routing.
- `app.js` : this file now holds the root module of our application.
Note that we removed most of the code in the `index.html` template and replaced it with a single
line containing a div with the `ng-view` attribute. The code that we removed was placed into the
`phone-list.html` template:
@@ -251,7 +188,113 @@ __`app/partials/phone-detail.html`:__
TBD: detail view for {{phoneId}}
```
Note how we are using `phoneId` model defined in the `PhoneDetailCtrl` controller.
Note how we are using the `phoneId` expression which will be defined in the `PhoneDetailCtrl` controller.
## The App Module
To improve the organization of the app, we are making use of Angular's `ngRoute` module and we've
moved the controllers into their own module `phonecatControllers` (as shown below).
We added `angular-route.js` to `index.html` and created a new `phonecatControllers` module in
`controllers.js`. That's not all we need to do to be able to use their code, however. We also have
to add the modules dependencies of our app. By listing these two modules as dependencies of
`phonecatApp`, we can use the directives and services they provide.
__`app/js/app.js`:__
```js
var phonecatApp = angular.module('phonecatApp', [
'ngRoute',
'phonecatControllers'
]);
...
```
Notice the second argument passed to `angular.module`, `['ngRoute', 'phonecatControllers']`. This
array lists the modules that `phonecatApp` depends on.
```js
...
phonecatApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/phones', {
templateUrl: 'partials/phone-list.html',
controller: 'PhoneListCtrl'
}).
when('/phones/:phoneId', {
templateUrl: 'partials/phone-detail.html',
controller: 'PhoneDetailCtrl'
}).
otherwise({
redirectTo: '/phones'
});
}]);
```
Using the `phonecatApp.config()` method, we request the `$routeProvider` to be injected into our
config function and use the {@link ngRoute.$routeProvider#when `$routeProvider.when()`} method to
define our routes.
Our application routes are defined as follows:
* `when('/phones')`: The phone list view will be shown when the URL hash fragment is `/phones`. To
construct this view, Angular will use the `phone-list.html` template and the `PhoneListCtrl`
controller.
* `when('/phones/:phoneId')`: The phone details view will be shown when the URL hash fragment
matches '/phones/:phoneId', where `:phoneId` is a variable part of the URL. To construct the phone
details view, Angular will use the `phone-detail.html` template and the `PhoneDetailCtrl`
controller.
* `otherwise({redirectTo: '/phones'})`: triggers a redirection to `/phones` when the browser
address doesn't match either of our routes.
We reused the `PhoneListCtrl` controller that we constructed in previous steps and we added a new,
empty `PhoneDetailCtrl` controller to the `app/js/controllers.js` file for the phone details view.
Note the use of the `:phoneId` parameter in the second route declaration. The `$route` service uses
the route declaration — `'/phones/:phoneId'` — as a template that is matched against the current
URL. All variables defined with the `:` notation are extracted into the
{@link ngRoute.$routeParams `$routeParams`} object.
## Controllers
__`app/js/controllers.js`:__
```js
var phonecatControllers = angular.module('phonecatControllers', []);
phonecatControllers.controller('PhoneListCtrl', ['$scope', '$http',
function ($scope, $http) {
$http.get('phones/phones.json').success(function(data) {
$scope.phones = data;
});
$scope.orderProp = 'age';
}]);
phonecatControllers.controller('PhoneDetailCtrl', ['$scope', '$routeParams',
function($scope, $routeParams) {
$scope.phoneId = $routeParams.phoneId;
}]);
```
Again, note that we created a new module called `phonecatControllers`. For small AngularJS
applications, it's common to create just one module for all of your controllers if there are just a
few. As your application grows it is quite common to refactor your code into additional modules.
For larger apps, you will probably want to create separate modules for each major feature of
your app.
Because our example app is relatively small, we'll just add all of our controllers to the
`phonecatControllers` module.
## Test
@@ -267,22 +310,21 @@ to various URLs and verify that the correct view was rendered.
});
...
describe('Phone detail view', function() {
describe('Phone detail view', function() {
beforeEach(function() {
browser().navigateTo('app/index.html#/phones/nexus-s');
browser.get('app/index.html#/phones/nexus-s');
});
it('should display placeholder page with phoneId', function() {
expect(binding('phoneId')).toBe('nexus-s');
expect(element(by.binding('phoneId')).getText()).toBe('nexus-s');
});
});
});
```
You can now rerun `./scripts/e2e-test.sh` or refresh the browser tab with the end-to-end test
runner to see the tests run, or you can see them running on [Angular's server](http://angular.github.com/angular-phonecat/step-7/test/e2e/runner.html).
You can now rerun `npm run protractor` to see the tests run.
# Experiments
@@ -308,3 +350,6 @@ step 8} to implement the phone details view.
<ul doc-tutorial-nav="7"></ul>
[bower]: http://bower.io
+18 -18
View File
@@ -9,18 +9,15 @@
In this step, you will implement the phone details view, which is displayed when a user clicks on a
phone in the phone list.
* When you click on a phone on the list, the phone details page with phone-specific information
is displayed.
To implement the phone details view we used {@link ng.$http $http} to fetch our data, and we
fleshed out the `phone-detail.html` view template.
<div doc-tutorial-reset="8"></div>
Now when you click on a phone on the list, the phone details page with phone-specific information
is displayed.
To implement the phone details view we will use {@link ng.$http $http} to fetch
our data, and we'll flesh out the `phone-detail.html` view template.
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-7...step-8):
## Data
In addition to `phones.json`, the `app/phones/` directory also contains one json file for each
@@ -79,7 +76,7 @@ route by the `$route` service.
## Template
The TBD placeholder line has been replaced with lists and bindings that comprise the phone details.
Note where we use the angular `{{expression}}` markup and `ngRepeat` to project phone data from
Note where we use the Angular `{{expression}}` markup and `ngRepeat` to project phone data from
our model into the view.
@@ -107,7 +104,7 @@ __`app/partials/phone-detail.html`:__
</dl>
</li>
...
</li>
<li>
<span>Additional Features</span>
<dd>{{phone.additionalFeatures}}</dd>
</li>
@@ -127,7 +124,11 @@ step 5.
__`test/unit/controllersSpec.js`:__
```js
...
beforeEach(module('phonecatApp'));
...
describe('PhoneDetailCtrl', function(){
var scope, $httpBackend, ctrl;
@@ -153,7 +154,7 @@ __`test/unit/controllersSpec.js`:__
You should now see the following output in the Karma tab:
Chrome 22.0: Executed 3 of 3 SUCCESS (0.039 secs / 0.012 secs)
<pre>Chrome 22.0: Executed 3 of 3 SUCCESS (0.039 secs / 0.012 secs)</pre>
We also added a new end-to-end test that navigates to the Nexus S detail page and verifies that the
@@ -166,26 +167,25 @@ __`test/e2e/scenarios.js`:__
describe('Phone detail view', function() {
beforeEach(function() {
browser().navigateTo('../../app/index.html#/phones/nexus-s');
browser.get('app/index.html#/phones/nexus-s');
});
it('should display nexus-s page', function() {
expect(binding('phone.name')).toBe('Nexus S');
expect(element(by.binding('phone.name')).getText()).toBe('Nexus S');
});
});
...
```
You can now rerun `./scripts/e2e-test.sh` or refresh the browser tab with the end-to-end test
runner to see the tests run, or you can see them running on [Angular's server](http://angular.github.com/angular-phonecat/step-8/test/e2e/runner.html).
You can now rerun `npm run protractor` to see the tests run.
# Experiments
* Using the {@link guide/dev_guide.e2e-testing Angular's end-to-end test runner API}, write a test
that verifies that we display 4 thumbnail images on the Nexus S details page.
* Using the [Protractor API](https://github.com/angular/protractor/blob/master/docs/api.md),
write a test that verifies that we display 4 thumbnail images on the Nexus S details page.
# Summary
+9 -14
View File
@@ -5,20 +5,13 @@
<ul doc-tutorial-nav="9"></ul>
In this step you will learn how to create your own custom display filter.
<div doc-tutorial-reset="9"></div>
Navigate to one of the detail pages.
In the previous step, the details page displayed either "true" or "false" to indicate whether
* In the previous step, the details page displayed either "true" or "false" to indicate whether
certain phone features were present or not. We have used a custom filter to convert those text
strings into glyphs: ✓ for "true", and ✘ for "false". Let's see what the filter code looks like.
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-8...step-9):
<div doc-tutorial-reset="9"></div>
## Custom Filter
@@ -40,13 +33,13 @@ The name of our filter is "checkmark". The `input` evaluates to either `true` or
return one of the two unicode characters we have chosen to represent true (`\u2713` -> ✓) or false (`\u2718` -> ✘).
Now that our filter is ready, we need to register the `phonecatFilters` module as a dependency for
our main `phonecat` module.
our main `phonecatApp` module.
__`app/js/app.js`:__
```js
...
angular.module('phonecatApp', ['phonecatFilters']).
angular.module('phonecatApp', ['ngRoute','phonecatControllers','phonecatFilters']);
...
```
@@ -118,7 +111,7 @@ access to the filter that we want to test. See {@link angular.mock.inject angul
You should now see the following output in the Karma tab:
Chrome 22.0: Executed 4 of 4 SUCCESS (0.034 secs / 0.012 secs)
<pre>Chrome 22.0: Executed 4 of 4 SUCCESS (0.034 secs / 0.012 secs)</pre>
# Experiments
@@ -131,9 +124,11 @@ following bindings to `index.html`:
* `{{ 1304375948024 | date:"MM/dd/yyyy @ h:mma" }}`
* We can also create a model with an input element, and combine it with a filtered binding. Add
the following to index.html:
the following to index.html:
<input ng-model="userInput"> Uppercased: {{ userInput | uppercase }}
```html
<input ng-model="userInput"> Uppercased: {{ userInput | uppercase }}
```
# Summary
+9 -15
View File
@@ -8,16 +8,11 @@
In this step, you will add a clickable phone image swapper to the phone details page.
<div doc-tutorial-reset="10"></div>
The phone details view displays one large image of the current phone and several smaller thumbnail
* The phone details view displays one large image of the current phone and several smaller thumbnail
images. It would be great if we could replace the large image with any of the thumbnails just by
clicking on the desired thumbnail image. Let's have a look at how we can do this with Angular.
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-9...step-10):
<div doc-tutorial-reset="10"></div>
## Controller
@@ -90,23 +85,22 @@ __`test/e2e/scenarios.js`:__
...
it('should display the first phone image as the main phone image', function() {
expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg');
expect(element(by.css('img.phone')).getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/);
});
it('should swap main image if a thumbnail image is clicked on', function() {
element('.phone-thumbs li:nth-child(3) img').click();
expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.2.jpg');
element(by.css('.phone-thumbs li:nth-child(3) img')).click();
expect(element(by.css('img.phone')).getAttribute('src')).toMatch(/img\/phones\/nexus-s.2.jpg/);
element('.phone-thumbs li:nth-child(1) img').click();
expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg');
element(by.css('.phone-thumbs li:nth-child(1) img')).click();
expect(element(by.css('img.phone')).getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/);
});
});
});
```
You can now rerun `./scripts/e2e-test.sh` or refresh the browser tab with the end-to-end test
runner to see the tests run, or you can see them running on [Angular's server](http://angular.github.com/angular-phonecat/step-10/test/e2e/runner.html).
You can now rerun `npm run protractor` to see the tests run.
# Experiments
+59 -19
View File
@@ -6,37 +6,74 @@
<ul doc-tutorial-nav="11"></ul>
In this step, you will improve the way our app fetches data.
In this step, you will change the way our app fetches data.
* We defined a custom service that represents a [RESTful][restful] client. Using this client we
can make requests to the server for data in an easier way, without having to deal with the
lower-level {@link ng.$http $http} API, HTTP methods and URLs.
<div doc-tutorial-reset="11"></div>
## Dependencies
The next improvement we will make to our app is to define a custom service that represents a [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) client. Using this client we
can make XHR requests for data in an easier way, without having to deal with the lower-level {@link
ng.$http $http} API, HTTP methods and URLs.
The RESTful functionality is provided by Angular in the `ngResource` module, which is distributed
separately from the core Angular framework.
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-10...step-11):
We are using [Bower][bower] to install client side dependencies. This step updates the
`bower.json` configuration file to include the new dependency:
```
{
"name": "angular-seed",
"description": "A starter project for AngularJS",
"version": "0.0.0",
"homepage": "https://github.com/angular/angular-seed",
"license": "MIT",
"private": true,
"dependencies": {
"angular": "1.2.x",
"angular-mocks": "~1.2.x",
"bootstrap": "~3.1.1",
"angular-route": "~1.2.x",
"angular-resource": "~1.2.x"
}
}
```
The new dependency `"angular-resource": "~1.2.x"` tells bower to install a version of the
angular-resource component that is compatible with version 1.2.x. We must ask bower to download
and install this dependency. We can do this by running:
```
npm install
```
<div class="alert alert-info">
If you have bower installed globally then you can run `bower install` but for this project we have
preconfigured `npm install` to run bower for us.
</div>
## Template
The custom service is defined in `app/js/services.js` so we need to include this file in our layout
template. Additionally, we also need to load the `angular-resource.js` file, which contains the
{@link api/ngResource ngResource} module and in it the {@link api/ngResource.$resource $resource}
service, that we'll soon use:
Our custom resource service will be defined in `app/js/services.js` so we need to include this file
in our layout template. Additionally, we also need to load the `angular-resource.js` file, which
contains the {@link api/ngResource ngResource} module:
__`app/index.html`.__
```html
...
<script src="bower_components/angular-resource/angular-resource.js"></script>
<script src="js/services.js"></script>
<script src="lib/angular/angular-resource.js"></script>
...
```
## Service
We create our own service to provide access to the phone data on the server:
__`app/js/services.js`.__
```js
@@ -52,13 +89,12 @@ phonecatServices.factory('Phone', ['$resource',
We used the module API to register a custom service using a factory function. We passed in the name
of the service - 'Phone' - and the factory function. The factory function is similar to a
controller's constructor in that both can declare dependencies via function arguments. The Phone
service declared a dependency on the `$resource` service.
controller's constructor in that both can declare dependencies to be injected via function
arguments. The Phone service declared a dependency on the `$resource` service.
The {@link ngResource.$resource `$resource`} service makes it easy to create a
[RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) client with just a few
lines of code. This client can then be used in our application, instead of the lower-level {@link
ng.$http $http} service.
[RESTful][restful] client with just a few lines of code. This client can then be used in our
application, instead of the lower-level {@link ng.$http $http} service.
__`app/js/app.js`.__
@@ -135,8 +171,8 @@ service correctly.
The {@link ngResource.$resource $resource} service augments the response object
with methods for updating and deleting the resource. If we were to use the standard `toEqual`
matcher, our tests would fail because the test values would not match the responses exactly. To
solve the problem, we use a newly-defined `toEqualData` [Jasmine matcher](https://github.com/pivotal/jasmine/wiki/Matchers). When the
`toEqualData` matcher compares two objects, it takes only object properties into account and
solve the problem, we use a newly-defined `toEqualData` [Jasmine matcher][jasmine-matchers]. When
the `toEqualData` matcher compares two objects, it takes only object properties into account and
ignores methods.
@@ -171,7 +207,7 @@ describe('PhoneCat controllers', function() {
it('should create "phones" model with 2 phones fetched from xhr', function() {
expect(scope.phones).toEqual([]);
expect(scope.phones).toEqualData([]);
$httpBackend.flush();
expect(scope.phones).toEqualData(
@@ -217,7 +253,7 @@ describe('PhoneCat controllers', function() {
You should now see the following output in the Karma tab:
Chrome 22.0: Executed 4 of 4 SUCCESS (0.038 secs / 0.01 secs)
<pre>Chrome 22.0: Executed 4 of 4 SUCCESS (0.038 secs / 0.01 secs)</pre>
# Summary
@@ -227,3 +263,7 @@ learn how to improve this application with animations.
<ul doc-tutorial-nav="11"></ul>
[restful]: http://en.wikipedia.org/wiki/Representational_State_Transfer
[jasmine-matchers]: https://github.com/pivotal/jasmine/wiki/Matchers
[bower]: http://bower.io/
+132 -47
View File
@@ -9,21 +9,60 @@
In this final step, we will enhance our phonecat web application by attaching CSS and JavaScript
animations on top of the template code we created before.
* Used the `ngAnimate` to enable animations throughout the application.
* Common `ng` directives automatically trigger hooks for animations to tap into.
* When an animation is found then the animation will run in between the standard DOM operation that
is being issued on the element at the given time (e.g. inserting and removing nodes on
{@link api/ng.directive:ngRepeat `ngRepeat`} or adding and removing classes on
{@link api/ng.directive:ngClass `ngClass`}).
<div doc-tutorial-reset="12"></div>
## Dependencies
Now that everything is set in place for a fully functional web application, we can attach CSS and JavaScript
animations to common directives that are used to render our application. AngularJS comes bundled with an
additional JavaScript file called `angular-animate.js` which, when included into the website and set as
a dependency with the application module, will enable animations throughout the application.
The animation functionality is provided by Angular in the `ngAnimate` module, which is distributed
separately from the core Angular framework. In addition we will use `JQuery` in this project to do
extra JavaScript animations.
Common `ng` directives automatically trigger hooks for animations to tap into. When an animation is found
then the animation will run in between the standard DOM operation that is being issued on the element at
the given time (e.g. inserting and removing nodes on ngRepeat or adding and removing classes on ngClass).
We are using [Bower][bower] to install client side dependencies. This step updates the
`bower.json` configuration file to include the new dependency:
The most important changes are listed below. You can see the full diff on
[GitHub](https://github.com/angular/angular-phonecat/compare/step-11...step-12):
```
{
"name": "angular-seed",
"description": "A starter project for AngularJS",
"version": "0.0.0",
"homepage": "https://github.com/angular/angular-seed",
"license": "MIT",
"private": true,
"dependencies": {
"angular": "1.2.x",
"angular-mocks": "~1.2.x",
"bootstrap": "~3.1.1",
"angular-route": "~1.2.x",
"angular-resource": "~1.2.x",
"jquery": "1.10.2",
"angular-animate": "~1.2.x"
}
}
```
* `"angular-animate": "~1.2.x"` tells bower to install a version of the
angular-animate component that is compatible with version 1.2.x.
* `"jquery": "1.10.2"` tells bower to install the 1.1.2 version of JQuery. Note that this is not an
Angular library, it is the standard JQuery library. We can use bower to install a wide range of 3rd
party libraries.
We must ask bower to download and install this dependency. We can do this by running:
```
npm install
```
<div class="alert alert-info">
If you have bower installed globally then you can run `bower install` but for this project we have
preconfigured `npm install` to run bower for us.
</div>
## How Animations work with `ngAnimate`
@@ -34,27 +73,33 @@ To get an idea of how animations work with AngularJS, please read the
## Template
The changes required within the HTML template code is to link the asset files which define the animations as well
as the `angular-animate.js` file. The animation module, known as `ngAnimate`, is defined within
`angular-animate.js` and contains the code necessary to make your application become animation aware.
The changes required within the HTML template code is to link the asset files which define the animations as
well as the `angular-animate.js` file. The animation module, known as {@link api/ngAnimate `ngAnimate`}, is
defined within `angular-animate.js` and contains the code necessary to make your application become animation
aware.
Here's what needs to changed in the index file:
Here's what needs to be changed in the index file:
__`app/index.html`.__
```html
...
<!-- for CSS Transitions and/or Keyframe Animations -->
<link rel="stylesheet" href="css/animations.css">
...
<!-- jQuery is used for JavaScript animations (include this before angular.js) -->
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="bower_components/jquery/jquery.js"></script>
...
<!-- required module to enable animation support in AngularJS -->
<script src="lib/angular/angular-animate.js"></script>
<script src="bower_components/angular-animate/angular-animate.js"></script>
<!-- for JavaScript Animations -->
<script src="js/animations.js"></script>
<!-- for CSS Transitions and/or Keyframe Animations -->
<link rel="stylesheet" href="css/animations.css">
...
```
@@ -71,7 +116,7 @@ with `ngResource`.
__`app/js/animations.js`.__
```js
angular.module('phonecatAnimations', ['ngAnimate']).
angular.module('phonecatAnimations', ['ngAnimate']);
// ...
// this module will later be used to define animations
// ...
@@ -83,14 +128,14 @@ __`app/js/app.js`.__
```js
// ...
angular.module('phonecat', [
angular.module('phonecatApp', [
'ngRoute',
'phonecatAnimations',
'phonecatControllers',
'phonecatFilters',
'phonecatServices',
]).
]);
// ...
```
@@ -197,7 +242,7 @@ which are described in detail below.
## Animating `ngView` with CSS Keyframe Animations
Next let's add an animation for transitions between route changes in `ngView`.
Next let's add an animation for transitions between route changes in {@link api/ngRoute.directive:ngView `ngView`}.
To start, let's add a new CSS class to our HTML like we did in the example above.
This time, instead of the `ng-repeat` element, let's add it to the element containing the ng-view directive.
@@ -278,18 +323,22 @@ __`app/css/animations.css`.__
/* don't forget about the vendor-prefixes! */
```
Nothing crazy here! Just a simple fade in and fade out effect between pages. The only out of the ordinary thing
here is that we're using absolute positioning to position the next page (identified via `ng-enter`) on top of the
previous page (the one that has the `ng-leave` class) while performing a cross fade animation in between. So
as the previous page is just about to be removed, it fades out while the new page fades in right on top of it.
Once the leave animation is over then element is removed and once the enter animation is complete then the
`ng-enter` and `ng-enter-active` CSS classes are removed from the element rendering it to be position itself
with its default CSS code (so no more absolute positioning once the animation is over). This works fluidly so
that pages flow naturally between route changes without anything jumping around.
Nothing crazy here! Just a simple fade in and fade out effect between pages. The only out of the
ordinary thing here is that we're using absolute positioning to position the next page (identified
via `ng-enter`) on top of the previous page (the one that has the `ng-leave` class) while performing
a cross fade animation in between. So as the previous page is just about to be removed, it fades out
while the new page fades in right on top of it.
The CSS classes applied (the start and end classes) are much the same as with `ng-repeat`. Each time a new page is
loaded the ng-view directive will create a copy of itself, download the template and append the contents. This
ensures that all views are contained within a single HTML element which allows for easy animation control.
Once the leave animation is over then element is removed and once the enter animation is complete
then the `ng-enter` and `ng-enter-active` CSS classes are removed from the element rendering it to
be position itself with its default CSS code (so no more absolute positioning once the animation is
over). This works fluidly so that pages flow naturally between route changes without anything
jumping around.
The CSS classes applied (the start and end classes) are much the same as with `ng-repeat`. Each time
a new page is loaded the ng-view directive will create a copy of itself, download the template and
append the contents. This ensures that all views are contained within a single HTML element which
allows for easy animation control.
For more on CSS animations, see the
[Web Platform documentation](http://docs.webplatform.org/wiki/css/properties/animations).
@@ -301,14 +350,14 @@ Let's add another animation to our application. Switching to our `phone-detail.h
we see that we have a nice thumbnail swapper. By clicking on the thumbnails listed on the page,
the profile phone image changes. But how can we change this around to add animations?
Let's think about it first. Basically, when you click on a thumbnail image, you're changing the state of the profile image to reflect the newly
selected thumbnail image.
Let's think about it first. Basically, when you click on a thumbnail image, you're changing the
state of the profile image to reflect the newly selected thumbnail image.
The best way to specify state changes within HTML is to use classes.
Much like before, how we used a CSS class to specify
an animation, this time the animation will occur whenever the CSS class itself changes.
Much like before, how we used a CSS class to specify an animation, this time the animation will
occur whenever the CSS class itself changes.
Whenever a new phone thumbnail is selected, the state changes and the `.active` CSS class is added to the matching
profile image and the animation plays.
Whenever a new phone thumbnail is selected, the state changes and the `.active` CSS class is added
to the matching profile image and the animation plays.
Let's get started and tweak our HTML code on the `phone-detail.html` page first:
@@ -334,18 +383,54 @@ __`app/partials/phone-detail.html`.__
</ul>
```
Just like with the thumbnails, we're using a repeater to display **all** the profile images as a list, however we're
not animating any repeat-related animations. Instead, we're keeping our eye on the ng-class directive since whenever
the `active` class is true then it will be applied to the element and will render as visible. Otherwise, the profile image
is hidden. In our case, there is always one element that has the active class, and, therefore, there will always
be one phone profile image visible on screen at all times.
Just like with the thumbnails, we're using a repeater to display **all** the profile images as a
list, however we're not animating any repeat-related animations. Instead, we're keeping our eye on
the ng-class directive since whenever the `active` class is true then it will be applied to the
element and will render as visible. Otherwise, the profile image is hidden. In our case, there is
always one element that has the active class, and, therefore, there will always be one phone profile
image visible on screen at all times.
When the active class is added to the element, the `active-add` and the `active-add-active` classes
are added just before to signal AngularJS to fire off an animation. When removed, the
`active-remove` and the `active-remove-active` classes are applied to the element which in turn
trigger another animation.
To ensure that the phone images are displayed correctly when the page is first loaded we also tweak
the detail page CSS styles:
__`app/css/app.css`__
```css
.phone-images {
background-color: white;
width: 450px;
height: 450px;
overflow: hidden;
position: relative;
float: left;
}
...
img.phone {
float: left;
margin-right: 3em;
margin-bottom: 2em;
background-color: white;
padding: 2em;
height: 400px;
width: 400px;
display: none;
}
img.phone:first-child {
display: block;
}
```
When the active class is added to the element, the `active-add` and the `active-add-active` classes are added just before
to signal AngularJS to fire off an animation. When removed, the `active-remove` and the `active-remove-active` classes
are applied to the element which in turn trigger another animation.
You may be thinking that we're just going to create another CSS-enabled animation.
Although we could do that, let's take the opportunity to learn how to create JavaScript-enabled animations with the `animation()` module method.
Although we could do that, let's take the opportunity to learn how to create JavaScript-enabled
animations with the `animation()` module method.
__`app/js/animations.js`.__
+13 -11
View File
@@ -1,13 +1,12 @@
var path = require('canonical-path');
var gruntUtils = require('../lib/grunt/utils');
var versionInfo = require('../lib/versions/version-info');
var basePath = __dirname;
var basePackage = require('./config');
module.exports = function(config) {
var version = gruntUtils.getVersion();
var cdnUrl = "//ajax.googleapis.com/ajax/libs/angularjs/" + version.cdn;
var cdnUrl = "//ajax.googleapis.com/ajax/libs/angularjs/" + versionInfo.cdnVersion;
var getVersion = function(component, sourceFolder, packageFile) {
sourceFolder = sourceFolder || '../bower_components';
@@ -25,9 +24,12 @@ module.exports = function(config) {
{ pattern: '**/*.ngdoc', basePath: path.resolve(basePath, 'content') }
]);
config.set('processing.stopOnError', true);
config.set('processing.errors.minerrInfoPath', path.resolve(basePath, '../build/errors.json'));
config.set('rendering.outputFolder', '../build/docs');
config.set('rendering.contentsFolder', 'partials');
config.set('logging.level', 'info');
@@ -38,7 +40,7 @@ module.exports = function(config) {
commonFiles: {
scripts: [ '../../../angular.js' ]
},
dependencyPath: '../../..'
dependencyPath: '../../../'
},
scripts: [
'../angular.js',
@@ -60,7 +62,7 @@ module.exports = function(config) {
'js/docs.js'
],
stylesheets: [
'components/bootstrap-' + getVersion('bootstrap') + '/dist/css/bootstrap.css',
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/docs.css',
@@ -73,7 +75,7 @@ module.exports = function(config) {
commonFiles: {
scripts: [ '../../../angular.min.js' ]
},
dependencyPath: '../../..'
dependencyPath: '../../../'
},
scripts: [
'../angular.min.js',
@@ -95,7 +97,7 @@ module.exports = function(config) {
'js/docs.js'
],
stylesheets: [
'components/bootstrap-' + getVersion('bootstrap') + '/dist/css/bootstrap.min.css',
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/docs.css',
@@ -111,7 +113,7 @@ module.exports = function(config) {
'../../../angular.js'
]
},
dependencyPath: '../../..'
dependencyPath: '../../../'
},
scripts: [
'components/jquery-' + getVersion('jquery') + '/jquery.js',
@@ -134,7 +136,7 @@ module.exports = function(config) {
'js/docs.js'
],
stylesheets: [
'components/bootstrap-' + getVersion('bootstrap') + '/dist/css/bootstrap.min.css',
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/docs.css',
@@ -147,7 +149,7 @@ module.exports = function(config) {
commonFiles: {
scripts: [ cdnUrl + '/angular.min.js' ]
},
dependencyPath: cdnUrl
dependencyPath: cdnUrl + '/'
},
scripts: [
cdnUrl + '/angular.min.js',
@@ -169,7 +171,7 @@ module.exports = function(config) {
'js/docs.js'
],
stylesheets: [
'components/bootstrap-' + getVersion('bootstrap') + '/dist/css/bootstrap.min.css',
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/docs.css',
+8 -4
View File
@@ -2,7 +2,7 @@ var gulp = require('gulp');
var concat = require('gulp-concat');
var jshint = require('gulp-jshint');
var bower = require('bower');
var docGenerator = require('dgeni');
var dgeni = require('dgeni');
var merge = require('event-stream').merge;
var path = require('canonical-path');
@@ -38,18 +38,22 @@ gulp.task('build-app', function() {
gulp.task('assets', ['bower'], function() {
return merge(
gulp.src(['app/assets/**/*']).pipe(gulp.dest(outputFolder)),
copyComponent('bootstrap'),
copyComponent('bootstrap', '/dist/**/*'),
copyComponent('open-sans-fontface'),
copyComponent('lunr.js','/*.js'),
copyComponent('google-code-prettify'),
copyComponent('jquery'),
copyComponent('jquery', '/jquery.*'),
copyComponent('marked', '/**/*.js', '../node_modules', 'package.json')
);
});
gulp.task('doc-gen', function() {
return docGenerator('docs.config.js').generateDocs();
var generateDocs = dgeni.generator('docs.config.js');
return generateDocs()
.catch(function(error) {
process.exit(1);
});
});
// JSHint the example and protractor test files
+1 -1
View File
@@ -5,7 +5,7 @@
]>
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
x="0px" y="0px" width="687px" height="176px" viewBox="0 0 687 176" overflow="visible" enable-background="new 0 0 687 176"
x="0" y="0" width="687px" height="176px" viewBox="0 0 687 176" overflow="visible" enable-background="new 0 0 687 176"
xml:space="preserve">
<defs>
</defs>

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

+1 -1
View File
@@ -33,7 +33,7 @@ describe("convertDatetimeData", function() {
AMPMS: ['AM', 'PM'],
DATEFORMATS: ['a', 'b', 'c', 'd'],
TIMEFORMATS: ['e', 'f', 'g', 'h'] };
it('should convert empty datetime obj', function() {
var processedData = convert(dataObj);
expect(processedData.MONTH).toEqual(['Enero', 'Pebrero']);
+2 -2
View File
@@ -7,10 +7,10 @@ echo "#################################"
# Enable tracing and exit on first failure
set -xe
# Define reasonable set of browsers in case we are running manually from commandline
# This is the default set of browsers to use on the CI server unless overridden via env variable
if [[ -z "$BROWSERS" ]]
then
BROWSERS="Chrome,Firefox,Opera,/Users/jenkins/bin/safari.sh,/Users/jenkins/bin/ie8.sh,/Users/jenkins/bin/ie9.sh"
BROWSERS="Chrome,Firefox,Opera,/Users/jenkins/bin/safari.sh"
fi
# CLEAN #
+3 -14
View File
@@ -32,7 +32,8 @@ module.exports = function(config, specificOptions) {
customLaunchers: {
'SL_Chrome': {
base: 'SauceLabs',
browserName: 'chrome'
browserName: 'chrome',
version: '34'
},
'SL_Firefox': {
base: 'SauceLabs',
@@ -45,12 +46,6 @@ module.exports = function(config, specificOptions) {
platform: 'OS X 10.9',
version: '7'
},
'SL_IE_8': {
base: 'SauceLabs',
browserName: 'internet explorer',
platform: 'Windows 7',
version: '8'
},
'SL_IE_9': {
base: 'SauceLabs',
browserName: 'internet explorer',
@@ -88,13 +83,6 @@ module.exports = function(config, specificOptions) {
os: 'Windows',
os_version: '8'
},
'BS_IE_8': {
base: 'BrowserStack',
browser: 'ie',
browser_version: '8.0',
os: 'Windows',
os_version: '7'
},
'BS_IE_9': {
base: 'BrowserStack',
browser: 'ie',
@@ -125,6 +113,7 @@ module.exports = function(config, specificOptions) {
config.logLevel = config.LOG_DEBUG;
config.transports = ['websocket', 'xhr-polling'];
config.captureTimeout = 0; // rely on SL timeout
config.browserStack.build = buildLabel;
config.browserStack.startTunnel = false;
+1 -1
View File
@@ -35,7 +35,7 @@ module.exports = function(grunt) {
grunt.registerTask('docs', 'create angular docs', function(){
var gruntProc = shelljs.exec('node_modules/gulp/bin/gulp.js --gulpfile docs/gulpfile.js');
var gruntProc = shelljs.exec('"node_modules/.bin/gulp" --gulpfile docs/gulpfile.js');
if (gruntProc.code !== 0) {
throw new Error('doc generation failed');
}

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