entangled 0.0.9 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +5 -3
- data/README.md +6 -1
- data/{spec/dummy/public/js/bower.json → bower.json} +10 -1
- data/entangled.gemspec +1 -1
- data/{spec/dummy/public/js/entangled.js → entangled.js} +13 -2
- data/lib/entangled.rb +1 -0
- data/lib/entangled/controller.rb +4 -2
- data/lib/entangled/model.rb +2 -2
- data/lib/entangled/version.rb +1 -1
- data/spec/dummy/app/controllers/messages_controller.rb +6 -0
- data/spec/dummy/config/environments/test.rb +15 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +18370 -0
- data/spec/dummy/public/Gruntfile.js +30 -0
- data/spec/dummy/public/app/app.js +20 -0
- data/spec/dummy/public/app/controllers/message.js +16 -0
- data/spec/dummy/public/app/controllers/messages.js +23 -0
- data/spec/dummy/public/app/entangled/entangled.js +191 -0
- data/spec/dummy/public/app/services/message.js +21 -0
- data/spec/dummy/public/bower.json +22 -0
- data/spec/dummy/public/bower_components/angular-route/README.md +2 -11
- data/spec/dummy/public/bower_components/angular-route/angular-route.js +1 -1
- data/spec/dummy/public/bower_components/angular-route/angular-route.min.js +1 -1
- data/spec/dummy/public/bower_components/angular-route/bower.json +2 -2
- data/spec/dummy/public/bower_components/angular-route/package.json +2 -2
- data/spec/dummy/public/bower_components/angular/README.md +2 -5
- data/spec/dummy/public/bower_components/angular/angular.js +88 -37
- data/spec/dummy/public/bower_components/angular/angular.min.js +223 -223
- data/spec/dummy/public/bower_components/angular/angular.min.js.gzip +0 -0
- data/spec/dummy/public/bower_components/angular/angular.min.js.map +2 -2
- data/spec/dummy/public/bower_components/angular/bower.json +1 -1
- data/spec/dummy/public/bower_components/angular/package.json +2 -2
- data/spec/dummy/public/index.html +5 -2
- data/spec/dummy/public/package.json +25 -0
- data/spec/dummy/public/test/controllers/messages_test.js +100 -0
- metadata +19196 -12
- data/spec/dummy/public/bower_components/angular-route/.bower.json +0 -20
- data/spec/dummy/public/bower_components/angular/.bower.json +0 -18
- data/spec/dummy/public/js/app.js +0 -70
@@ -0,0 +1,30 @@
|
|
1
|
+
// Created with the help of
|
2
|
+
// http://paislee.io/testing-angularjs-with-grunt-karma-and-jasmine/
|
3
|
+
|
4
|
+
module.exports = function(grunt) {
|
5
|
+
|
6
|
+
grunt.loadNpmTasks('grunt-karma');
|
7
|
+
|
8
|
+
grunt.initConfig({
|
9
|
+
karma: {
|
10
|
+
unit: {
|
11
|
+
options: {
|
12
|
+
frameworks: ['jasmine'],
|
13
|
+
singleRun: true,
|
14
|
+
browsers: ['Chrome'], // 'PhantomJS'
|
15
|
+
files: [
|
16
|
+
'bower_components/angular/angular.js',
|
17
|
+
'bower_components/angular-route/angular-route.js',
|
18
|
+
'bower_components/angular-mocks/angular-mocks.js',
|
19
|
+
'app/**/*.js',
|
20
|
+
'test/**/*.js'
|
21
|
+
]
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
});
|
26
|
+
|
27
|
+
grunt.registerTask('test', [
|
28
|
+
'karma'
|
29
|
+
]);
|
30
|
+
};
|
@@ -0,0 +1,20 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
angular.module('entangledTest', [
|
4
|
+
'ngRoute',
|
5
|
+
'entangled'
|
6
|
+
])
|
7
|
+
|
8
|
+
.config(function($routeProvider) {
|
9
|
+
$routeProvider
|
10
|
+
.when('/', {
|
11
|
+
templateUrl: 'views/messages/index.html',
|
12
|
+
controller: 'MessagesCtrl'
|
13
|
+
}).when('/messages/:id', {
|
14
|
+
templateUrl: 'views/messages/show.html',
|
15
|
+
controller: 'MessageCtrl'
|
16
|
+
})
|
17
|
+
.otherwise({
|
18
|
+
redirectTo: '/'
|
19
|
+
});
|
20
|
+
})
|
@@ -0,0 +1,16 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
angular.module('entangledTest')
|
4
|
+
|
5
|
+
.controller('MessageCtrl', function($scope, $routeParams, Message) {
|
6
|
+
$scope.update = function() {
|
7
|
+
$scope.message.$save();
|
8
|
+
};
|
9
|
+
|
10
|
+
Message.find($routeParams.id, function(message) {
|
11
|
+
$scope.$apply(function() {
|
12
|
+
$scope.message = message;
|
13
|
+
console.log('Show callback called!');
|
14
|
+
});
|
15
|
+
});
|
16
|
+
});
|
@@ -0,0 +1,23 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
angular.module('entangledTest')
|
4
|
+
|
5
|
+
.controller('MessagesCtrl', function($scope, Message) {
|
6
|
+
$scope.message = Message.new();
|
7
|
+
|
8
|
+
$scope.create = function() {
|
9
|
+
$scope.message.$save(function() {
|
10
|
+
$scope.message = Message.new();
|
11
|
+
});
|
12
|
+
};
|
13
|
+
|
14
|
+
$scope.destroy = function(message) {
|
15
|
+
message.$destroy();
|
16
|
+
};
|
17
|
+
|
18
|
+
Message.all(function(messages) {
|
19
|
+
$scope.$apply(function() {
|
20
|
+
$scope.messages = messages;
|
21
|
+
});
|
22
|
+
});
|
23
|
+
});
|
@@ -0,0 +1,191 @@
|
|
1
|
+
// Register Angular module and call it 'entangled'
|
2
|
+
angular.module('entangled', [])
|
3
|
+
|
4
|
+
// Register service and call it 'Entangled'
|
5
|
+
.factory('Entangled', function() {
|
6
|
+
// Every response coming from the server will be wrapped
|
7
|
+
// in a Resource constructor to represent a CRUD-able
|
8
|
+
// resource that can be saved and destroyed using the
|
9
|
+
// methods $save() and $destroy. A Resource also
|
10
|
+
// stores the socket's URL it was retrieved from so it
|
11
|
+
// can be reused for other requests.
|
12
|
+
var Resource = function(params, webSocketUrl) {
|
13
|
+
// Assign proerties
|
14
|
+
for (var key in params) {
|
15
|
+
// Skip inherent object properties
|
16
|
+
if (params.hasOwnProperty(key)) {
|
17
|
+
this[key] = params[key];
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
this.webSocketUrl = webSocketUrl;
|
22
|
+
};
|
23
|
+
|
24
|
+
// $save() will send a request to the server
|
25
|
+
// to either create a new record or update
|
26
|
+
// an existing, depending on whether or not
|
27
|
+
// an id is present.
|
28
|
+
Resource.prototype.$save = function(callback) {
|
29
|
+
var that = this;
|
30
|
+
|
31
|
+
if (this.id) {
|
32
|
+
// Update
|
33
|
+
var socket = new WebSocket(that.webSocketUrl + '/' + that.id + '/update');
|
34
|
+
socket.onopen = function() {
|
35
|
+
socket.send(JSON.stringify(that));
|
36
|
+
|
37
|
+
if (callback) callback();
|
38
|
+
};
|
39
|
+
} else {
|
40
|
+
// Create
|
41
|
+
var socket = new WebSocket(that.webSocketUrl + '/create');
|
42
|
+
|
43
|
+
socket.onopen = function() {
|
44
|
+
socket.send(JSON.stringify(that));
|
45
|
+
|
46
|
+
if (callback) callback();
|
47
|
+
};
|
48
|
+
}
|
49
|
+
};
|
50
|
+
|
51
|
+
// $destroy() will send a request to the server to
|
52
|
+
// destroy an existing record.
|
53
|
+
Resource.prototype.$destroy = function(callback) {
|
54
|
+
var socket = new WebSocket(this.webSocketUrl + '/' + this.id + '/destroy');
|
55
|
+
socket.onopen = function() {
|
56
|
+
// It's fine to send an empty message since the
|
57
|
+
// socket's URL contains all the information
|
58
|
+
// needed to destroy the record (the id).
|
59
|
+
socket.send(null);
|
60
|
+
|
61
|
+
if (callback) callback();
|
62
|
+
};
|
63
|
+
};
|
64
|
+
|
65
|
+
// Resources wraps all individual Resource objects
|
66
|
+
// in a collection.
|
67
|
+
var Resources = function(resources, webSocketUrl) {
|
68
|
+
this.all = [];
|
69
|
+
|
70
|
+
for (var i = 0; i < resources.length; i++) {
|
71
|
+
var resource = new Resource(resources[i], webSocketUrl);
|
72
|
+
this.all.push(resource);
|
73
|
+
}
|
74
|
+
};
|
75
|
+
|
76
|
+
// Entangled is the heart of this service. It contains
|
77
|
+
// several methods to instantiate a new Resource,
|
78
|
+
// retrieve an existing Resource from the server,
|
79
|
+
// and retrieve a collection of existing Resources
|
80
|
+
// from the server.
|
81
|
+
//
|
82
|
+
// Entangled is a constructor that takes the URL
|
83
|
+
// of the index action on the server where the
|
84
|
+
// Resources can be retrieved.
|
85
|
+
var Entangled = function(webSocketUrl) {
|
86
|
+
// Store the root URL that sockets
|
87
|
+
// will connect to
|
88
|
+
this.webSocketUrl = webSocketUrl;
|
89
|
+
};
|
90
|
+
|
91
|
+
// Function to instantiate a new Resource, optionally
|
92
|
+
// with given parameters
|
93
|
+
Entangled.prototype.new = function(params) {
|
94
|
+
return new Resource(params, this.webSocketUrl);
|
95
|
+
};
|
96
|
+
|
97
|
+
// Retrieve all Resources from the root of the socket's
|
98
|
+
// URL
|
99
|
+
Entangled.prototype.all = function(callback) {
|
100
|
+
var socket = new WebSocket(this.webSocketUrl);
|
101
|
+
|
102
|
+
socket.onmessage = function(event) {
|
103
|
+
// If the message from the server isn't empty
|
104
|
+
if (event.data.length) {
|
105
|
+
// Convert message to JSON
|
106
|
+
var data = JSON.parse(event.data);
|
107
|
+
|
108
|
+
// If the collection of Resources was sent
|
109
|
+
if (data.resources) {
|
110
|
+
// Store retrieved Resources in property
|
111
|
+
this.resources = new Resources(data.resources, socket.url);
|
112
|
+
} else if (data.action) {
|
113
|
+
if (data.action === 'create') {
|
114
|
+
// If new Resource was created, add it to the
|
115
|
+
// collection
|
116
|
+
this.resources.all.push(new Resource(data.resource, socket.url));
|
117
|
+
} else if (data.action === 'update') {
|
118
|
+
// If an existing Resource was updated,
|
119
|
+
// update it in the collection as well
|
120
|
+
var index;
|
121
|
+
|
122
|
+
for (var i = 0; i < this.resources.all.length; i++) {
|
123
|
+
if (this.resources.all[i].id === data.resource.id) {
|
124
|
+
index = i;
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
128
|
+
this.resources.all[index] = new Resource(data.resource, socket.url);
|
129
|
+
} else if (data.action === 'destroy') {
|
130
|
+
// If a Resource was destroyed, remove it
|
131
|
+
// from Resources as well
|
132
|
+
var index;
|
133
|
+
|
134
|
+
for (var i = 0; i < this.resources.all.length; i++) {
|
135
|
+
if (this.resources.all[i].id === data.resource.id) {
|
136
|
+
index = i;
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
this.resources.all.splice(index, 1);
|
141
|
+
} else {
|
142
|
+
console.log('Something else other than CRUD happened...');
|
143
|
+
console.log(data);
|
144
|
+
}
|
145
|
+
}
|
146
|
+
}
|
147
|
+
|
148
|
+
// Run the callback and pass in the
|
149
|
+
// resulting collection
|
150
|
+
callback(this.resources.all);
|
151
|
+
};
|
152
|
+
};
|
153
|
+
|
154
|
+
// Find an individual Resource on the server
|
155
|
+
Entangled.prototype.find = function(id, callback) {
|
156
|
+
var webSocketUrl = this.webSocketUrl;
|
157
|
+
var socket = new WebSocket(webSocketUrl + '/' + id);
|
158
|
+
|
159
|
+
socket.onmessage = function(event) {
|
160
|
+
// If the message from the server isn't empty
|
161
|
+
if (event.data.length) {
|
162
|
+
// Parse message and convert to JSON
|
163
|
+
var data = JSON.parse(event.data);
|
164
|
+
|
165
|
+
if (data.resource && !data.action) {
|
166
|
+
// If the Resource was sent from the server,
|
167
|
+
// store it
|
168
|
+
this.resource = new Resource(data.resource, webSocketUrl);
|
169
|
+
} else if (data.action) {
|
170
|
+
if (data.action === 'update') {
|
171
|
+
// If the stored Resource was updated,
|
172
|
+
// update it here as well
|
173
|
+
this.resource = new Resource(data.resource, webSocketUrl);
|
174
|
+
} else if (data.action === 'destroy') {
|
175
|
+
// If the stored Resource was destroyed,
|
176
|
+
// remove it from here as well
|
177
|
+
this.resource = undefined;
|
178
|
+
}
|
179
|
+
} else {
|
180
|
+
console.log('Something else other than CRUD happened...');
|
181
|
+
console.log(data);
|
182
|
+
}
|
183
|
+
}
|
184
|
+
|
185
|
+
// Run callback with retrieved Resource
|
186
|
+
callback(this.resource);
|
187
|
+
};
|
188
|
+
};
|
189
|
+
|
190
|
+
return Entangled;
|
191
|
+
});
|
@@ -0,0 +1,21 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
angular.module('entangledTest')
|
4
|
+
|
5
|
+
.factory('Message', function(Entangled) {
|
6
|
+
var entangled = new Entangled('ws://localhost:3000/messages');
|
7
|
+
|
8
|
+
var Message = {
|
9
|
+
new: function(params) {
|
10
|
+
return entangled.new(params);
|
11
|
+
},
|
12
|
+
all: function(callback) {
|
13
|
+
return entangled.all(callback);
|
14
|
+
},
|
15
|
+
find: function(id, callback) {
|
16
|
+
return entangled.find(id, callback);
|
17
|
+
}
|
18
|
+
};
|
19
|
+
|
20
|
+
return Message;
|
21
|
+
});
|
@@ -0,0 +1,22 @@
|
|
1
|
+
{
|
2
|
+
"name": "dummy",
|
3
|
+
"version": "0.0.0",
|
4
|
+
"homepage": "https://github.com/dchacke/entangled",
|
5
|
+
"authors": [
|
6
|
+
"Dennis Charles Hackethal <dennis.hackethal@gmail.com>"
|
7
|
+
],
|
8
|
+
"license": "MIT",
|
9
|
+
"private": true,
|
10
|
+
"ignore": [
|
11
|
+
"**/.*",
|
12
|
+
"node_modules",
|
13
|
+
"bower_components",
|
14
|
+
"test",
|
15
|
+
"tests"
|
16
|
+
],
|
17
|
+
"dependencies": {
|
18
|
+
"angular-route": "~1.3.14",
|
19
|
+
"angular": "~1.3.14",
|
20
|
+
"angular-mocks": "~1.3.14"
|
21
|
+
}
|
22
|
+
}
|
@@ -14,21 +14,12 @@ You can install this package either with `npm` or with `bower`.
|
|
14
14
|
npm install angular-route
|
15
15
|
```
|
16
16
|
|
17
|
-
Add a `<script>` to your `index.html`:
|
18
|
-
|
19
|
-
```html
|
20
|
-
<script src="/node_modules/angular-route/angular-route.js"></script>
|
21
|
-
```
|
22
|
-
|
23
17
|
Then add `ngRoute` as a dependency for your app:
|
24
18
|
|
25
19
|
```javascript
|
26
|
-
angular.module('myApp', ['
|
20
|
+
angular.module('myApp', [require('angular-route')]);
|
27
21
|
```
|
28
22
|
|
29
|
-
Note that this package is not in CommonJS format, so doing `require('angular-route')` will
|
30
|
-
return `undefined`.
|
31
|
-
|
32
23
|
### bower
|
33
24
|
|
34
25
|
```shell
|
@@ -56,7 +47,7 @@ Documentation is available on the
|
|
56
47
|
|
57
48
|
The MIT License
|
58
49
|
|
59
|
-
Copyright (c) 2010-
|
50
|
+
Copyright (c) 2010-2015 Google, Inc. http://angularjs.org
|
60
51
|
|
61
52
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
62
53
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -20,10 +20,7 @@ Then add a `<script>` to your `index.html`:
|
|
20
20
|
<script src="/node_modules/angular/angular.js"></script>
|
21
21
|
```
|
22
22
|
|
23
|
-
|
24
|
-
If you're using [Browserify](https://github.com/substack/node-browserify), you can use
|
25
|
-
[exposify](https://github.com/thlorenz/exposify) to have `require('angular')` return the `angular`
|
26
|
-
global.
|
23
|
+
Or `require('angular')` from your code.
|
27
24
|
|
28
25
|
### bower
|
29
26
|
|
@@ -46,7 +43,7 @@ Documentation is available on the
|
|
46
43
|
|
47
44
|
The MIT License
|
48
45
|
|
49
|
-
Copyright (c) 2010-
|
46
|
+
Copyright (c) 2010-2015 Google, Inc. http://angularjs.org
|
50
47
|
|
51
48
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
52
49
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license AngularJS v1.3.
|
2
|
+
* @license AngularJS v1.3.14
|
3
3
|
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
4
4
|
* License: MIT
|
5
5
|
*/
|
@@ -54,7 +54,7 @@ function minErr(module, ErrorConstructor) {
|
|
54
54
|
return match;
|
55
55
|
});
|
56
56
|
|
57
|
-
message = message + '\nhttp://errors.angularjs.org/1.3.
|
57
|
+
message = message + '\nhttp://errors.angularjs.org/1.3.14/' +
|
58
58
|
(module ? module + '/' : '') + code;
|
59
59
|
for (i = 2; i < arguments.length; i++) {
|
60
60
|
message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' +
|
@@ -2121,11 +2121,11 @@ function toDebugString(obj) {
|
|
2121
2121
|
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
|
2122
2122
|
*/
|
2123
2123
|
var version = {
|
2124
|
-
full: '1.3.
|
2124
|
+
full: '1.3.14', // all of these placeholder strings will be replaced by grunt's
|
2125
2125
|
major: 1, // package task
|
2126
2126
|
minor: 3,
|
2127
|
-
dot:
|
2128
|
-
codeName: '
|
2127
|
+
dot: 14,
|
2128
|
+
codeName: 'instantaneous-browserification'
|
2129
2129
|
};
|
2130
2130
|
|
2131
2131
|
|
@@ -17854,20 +17854,23 @@ var htmlAnchorDirective = valueFn({
|
|
17854
17854
|
*
|
17855
17855
|
* @description
|
17856
17856
|
*
|
17857
|
-
*
|
17857
|
+
* This directive sets the `disabled` attribute on the element if the
|
17858
|
+
* {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
|
17859
|
+
*
|
17860
|
+
* A special directive is necessary because we cannot use interpolation inside the `disabled`
|
17861
|
+
* attribute. The following example would make the button enabled on Chrome/Firefox
|
17862
|
+
* but not on older IEs:
|
17863
|
+
*
|
17858
17864
|
* ```html
|
17859
|
-
* <div ng-init="
|
17860
|
-
* <button disabled="{{
|
17865
|
+
* <div ng-init="isDisabled = false">
|
17866
|
+
* <button disabled="{{isDisabled}}">Disabled</button>
|
17861
17867
|
* </div>
|
17862
17868
|
* ```
|
17863
17869
|
*
|
17864
|
-
*
|
17865
|
-
* such as disabled
|
17870
|
+
* This is because the HTML specification does not require browsers to preserve the values of
|
17871
|
+
* boolean attributes such as `disabled` (Their presence means true and their absence means false.)
|
17866
17872
|
* If we put an Angular interpolation expression into such an attribute then the
|
17867
17873
|
* binding information would be lost when the browser removes the attribute.
|
17868
|
-
* The `ngDisabled` directive solves this problem for the `disabled` attribute.
|
17869
|
-
* This complementary directive is not removed by the browser and so provides
|
17870
|
-
* a permanent reliable place to store the binding information.
|
17871
17874
|
*
|
17872
17875
|
* @example
|
17873
17876
|
<example>
|
@@ -17886,7 +17889,7 @@ var htmlAnchorDirective = valueFn({
|
|
17886
17889
|
*
|
17887
17890
|
* @element INPUT
|
17888
17891
|
* @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
|
17889
|
-
* then
|
17892
|
+
* then the `disabled` attribute will be set on the element
|
17890
17893
|
*/
|
17891
17894
|
|
17892
17895
|
|
@@ -19888,7 +19891,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|
19888
19891
|
return value;
|
19889
19892
|
});
|
19890
19893
|
|
19891
|
-
if (attr.min || attr.ngMin) {
|
19894
|
+
if (isDefined(attr.min) || attr.ngMin) {
|
19892
19895
|
var minVal;
|
19893
19896
|
ctrl.$validators.min = function(value) {
|
19894
19897
|
return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
|
@@ -19904,7 +19907,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|
19904
19907
|
});
|
19905
19908
|
}
|
19906
19909
|
|
19907
|
-
if (attr.max || attr.ngMax) {
|
19910
|
+
if (isDefined(attr.max) || attr.ngMax) {
|
19908
19911
|
var maxVal;
|
19909
19912
|
ctrl.$validators.max = function(value) {
|
19910
19913
|
return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
|
@@ -22710,6 +22713,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
|
22710
22713
|
ngModelGet = parsedNgModel,
|
22711
22714
|
ngModelSet = parsedNgModelAssign,
|
22712
22715
|
pendingDebounce = null,
|
22716
|
+
parserValid,
|
22713
22717
|
ctrl = this;
|
22714
22718
|
|
22715
22719
|
this.$$setOptions = function(options) {
|
@@ -22982,16 +22986,12 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
|
22982
22986
|
// the model although neither viewValue nor the model on the scope changed
|
22983
22987
|
var modelValue = ctrl.$$rawModelValue;
|
22984
22988
|
|
22985
|
-
// Check if the there's a parse error, so we don't unset it accidentially
|
22986
|
-
var parserName = ctrl.$$parserName || 'parse';
|
22987
|
-
var parserValid = ctrl.$error[parserName] ? false : undefined;
|
22988
|
-
|
22989
22989
|
var prevValid = ctrl.$valid;
|
22990
22990
|
var prevModelValue = ctrl.$modelValue;
|
22991
22991
|
|
22992
22992
|
var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
|
22993
22993
|
|
22994
|
-
ctrl.$$runValidators(
|
22994
|
+
ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
|
22995
22995
|
// If there was no change in validity, don't update the model
|
22996
22996
|
// This prevents changing an invalid modelValue to undefined
|
22997
22997
|
if (!allowInvalid && prevValid !== allValid) {
|
@@ -23009,12 +23009,12 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
|
23009
23009
|
|
23010
23010
|
};
|
23011
23011
|
|
23012
|
-
this.$$runValidators = function(
|
23012
|
+
this.$$runValidators = function(modelValue, viewValue, doneCallback) {
|
23013
23013
|
currentValidationRunId++;
|
23014
23014
|
var localValidationRunId = currentValidationRunId;
|
23015
23015
|
|
23016
23016
|
// check parser error
|
23017
|
-
if (!processParseErrors(
|
23017
|
+
if (!processParseErrors()) {
|
23018
23018
|
validationDone(false);
|
23019
23019
|
return;
|
23020
23020
|
}
|
@@ -23024,21 +23024,22 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
|
23024
23024
|
}
|
23025
23025
|
processAsyncValidators();
|
23026
23026
|
|
23027
|
-
function processParseErrors(
|
23027
|
+
function processParseErrors() {
|
23028
23028
|
var errorKey = ctrl.$$parserName || 'parse';
|
23029
|
-
if (
|
23029
|
+
if (parserValid === undefined) {
|
23030
23030
|
setValidity(errorKey, null);
|
23031
23031
|
} else {
|
23032
|
-
|
23033
|
-
if (!parseValid) {
|
23032
|
+
if (!parserValid) {
|
23034
23033
|
forEach(ctrl.$validators, function(v, name) {
|
23035
23034
|
setValidity(name, null);
|
23036
23035
|
});
|
23037
23036
|
forEach(ctrl.$asyncValidators, function(v, name) {
|
23038
23037
|
setValidity(name, null);
|
23039
23038
|
});
|
23040
|
-
return false;
|
23041
23039
|
}
|
23040
|
+
// Set the parse error last, to prevent unsetting it, should a $validators key == parserName
|
23041
|
+
setValidity(errorKey, parserValid);
|
23042
|
+
return parserValid;
|
23042
23043
|
}
|
23043
23044
|
return true;
|
23044
23045
|
}
|
@@ -23133,7 +23134,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
|
23133
23134
|
this.$$parseAndValidate = function() {
|
23134
23135
|
var viewValue = ctrl.$$lastCommittedViewValue;
|
23135
23136
|
var modelValue = viewValue;
|
23136
|
-
|
23137
|
+
parserValid = isUndefined(modelValue) ? undefined : true;
|
23137
23138
|
|
23138
23139
|
if (parserValid) {
|
23139
23140
|
for (var i = 0; i < ctrl.$parsers.length; i++) {
|
@@ -23159,7 +23160,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
|
23159
23160
|
|
23160
23161
|
// Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
|
23161
23162
|
// This can happen if e.g. $setViewValue is called from inside a parser
|
23162
|
-
ctrl.$$runValidators(
|
23163
|
+
ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
|
23163
23164
|
if (!allowInvalid) {
|
23164
23165
|
// Note: Don't check ctrl.$valid here, as we could have
|
23165
23166
|
// external validators (e.g. calculated on the server),
|
@@ -23280,6 +23281,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
|
23280
23281
|
// TODO(perf): why not move this to the action fn?
|
23281
23282
|
if (modelValue !== ctrl.$modelValue) {
|
23282
23283
|
ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
|
23284
|
+
parserValid = undefined;
|
23283
23285
|
|
23284
23286
|
var formatters = ctrl.$formatters,
|
23285
23287
|
idx = formatters.length;
|
@@ -23292,7 +23294,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
|
23292
23294
|
ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
|
23293
23295
|
ctrl.$render();
|
23294
23296
|
|
23295
|
-
ctrl.$$runValidators(
|
23297
|
+
ctrl.$$runValidators(modelValue, viewValue, noop);
|
23296
23298
|
}
|
23297
23299
|
}
|
23298
23300
|
|
@@ -24108,6 +24110,55 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
|
|
24108
24110
|
* when keys are deleted and reinstated.
|
24109
24111
|
*
|
24110
24112
|
*
|
24113
|
+
* # Tracking and Duplicates
|
24114
|
+
*
|
24115
|
+
* When the contents of the collection change, `ngRepeat` makes the corresponding changes to the DOM:
|
24116
|
+
*
|
24117
|
+
* * When an item is added, a new instance of the template is added to the DOM.
|
24118
|
+
* * When an item is removed, its template instance is removed from the DOM.
|
24119
|
+
* * When items are reordered, their respective templates are reordered in the DOM.
|
24120
|
+
*
|
24121
|
+
* By default, `ngRepeat` does not allow duplicate items in arrays. This is because when
|
24122
|
+
* there are duplicates, it is not possible to maintain a one-to-one mapping between collection
|
24123
|
+
* items and DOM elements.
|
24124
|
+
*
|
24125
|
+
* If you do need to repeat duplicate items, you can substitute the default tracking behavior
|
24126
|
+
* with your own using the `track by` expression.
|
24127
|
+
*
|
24128
|
+
* For example, you may track items by the index of each item in the collection, using the
|
24129
|
+
* special scope property `$index`:
|
24130
|
+
* ```html
|
24131
|
+
* <div ng-repeat="n in [42, 42, 43, 43] track by $index">
|
24132
|
+
* {{n}}
|
24133
|
+
* </div>
|
24134
|
+
* ```
|
24135
|
+
*
|
24136
|
+
* You may use arbitrary expressions in `track by`, including references to custom functions
|
24137
|
+
* on the scope:
|
24138
|
+
* ```html
|
24139
|
+
* <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
|
24140
|
+
* {{n}}
|
24141
|
+
* </div>
|
24142
|
+
* ```
|
24143
|
+
*
|
24144
|
+
* If you are working with objects that have an identifier property, you can track
|
24145
|
+
* by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
|
24146
|
+
* will not have to rebuild the DOM elements for items it has already rendered, even if the
|
24147
|
+
* JavaScript objects in the collection have been substituted for new ones:
|
24148
|
+
* ```html
|
24149
|
+
* <div ng-repeat="model in collection track by model.id">
|
24150
|
+
* {{model.name}}
|
24151
|
+
* </div>
|
24152
|
+
* ```
|
24153
|
+
*
|
24154
|
+
* When no `track by` expression is provided, it is equivalent to tracking by the built-in
|
24155
|
+
* `$id` function, which tracks items by their identity:
|
24156
|
+
* ```html
|
24157
|
+
* <div ng-repeat="obj in collection track by $id(obj)">
|
24158
|
+
* {{obj.prop}}
|
24159
|
+
* </div>
|
24160
|
+
* ```
|
24161
|
+
*
|
24111
24162
|
* # Special repeat start and end points
|
24112
24163
|
* To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
|
24113
24164
|
* the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
|
@@ -24175,12 +24226,12 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
|
|
24175
24226
|
*
|
24176
24227
|
* For example: `(name, age) in {'adam':10, 'amalie':12}`.
|
24177
24228
|
*
|
24178
|
-
* * `variable in expression track by tracking_expression` – You can also provide an optional tracking
|
24179
|
-
* which can be used to associate the objects in the collection with the DOM elements. If no tracking
|
24180
|
-
* is specified
|
24181
|
-
* more than one tracking
|
24182
|
-
* mapped to the same DOM element, which is not possible.)
|
24183
|
-
* before
|
24229
|
+
* * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
|
24230
|
+
* which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
|
24231
|
+
* is specified, ng-repeat associates elements by identity. It is an error to have
|
24232
|
+
* more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
|
24233
|
+
* mapped to the same DOM element, which is not possible.) If filters are used in the expression, they should be
|
24234
|
+
* applied before the tracking expression.
|
24184
24235
|
*
|
24185
24236
|
* For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
|
24186
24237
|
* will be associated by item identity in the array.
|