Skip to content

Commit 0734394

Browse files
Merge branch 'feature/observeAngularChangeLikeBlaze'
2 parents 551664c + f6372b1 commit 0734394

File tree

6 files changed

+330
-170
lines changed

6 files changed

+330
-170
lines changed

.docs/angular-meteor/client/views/manifest.html

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,27 @@
2222

2323
When they understand they can use angular-meteor, they think they need to choose between Blaze or AngularJS.
2424

25-
We disagree. we created this library so you could use **both Blaze and AngularJS** in the same application and even on the same page, template or directive.
25+
We disagree. We created this library so you could use **both Blaze and AngularJS** in the same application and even on the same page, template or directive.
2626

2727
You can start with AngularJS and migrate your application slowly to Blaze.
2828
You can start using Blaze and move slowly to AngularJS.
2929
You can use both of them together forever.
3030

3131
### A combined power
3232

33-
both frameworks have missing parts, yet we found they could still complete each other into a more complete solution.
33+
Both frameworks have missing parts: we found that the complement each other, creating a more complete solution.
3434

3535
By combining both tools we are **merging both communities**, for greater power over existing solutions.
3636

37-
**Making both frameworks more open and integrable** -
37+
**Making both frameworks more open and integrable**
3838
As we sit in the middle, we see ways both frameworks can make their API’s more open for other frameworks.
3939

4040

4141
### Neutrality
4242

4343
Meteor and AngularJS are two advanced, very popular javascript frameworks with very strong development teams behind them.
4444

45-
Both Frameworks have agendas and marketing teams. they believe in their mission and also they need to make money for the guys who pay their development.
45+
Both Frameworks have agendas and marketing teams. They believe in their mission and also they need to make money for the guys who pay for their development.
4646
Most of the core team developers in each framework haven't used the other framework.
4747

4848
Some developers who use one of those frameworks are naturally more comfortable with the technology they are familiar with.
@@ -60,41 +60,39 @@
6060

6161
In Meteor’s client, everything is global. AngularJS is great with structuring large applications
6262

63-
AngularJS DOM syntax makes it easier to place the view login in the view and the app logic in JS
63+
AngularJS DOM syntax makes it easier to place the view login in the view and the app logic in JS.
6464

65-
If you still want to use global variables, Meteor is still there for you as also Angular’s $rootScope
65+
If you still want to use global variables, Meteor is still there for you - as is Angular’s `$rootScope`.
6666

6767
### Running few applications on the same Meteor server
6868

69-
Angular’s modularity makes it easier to run few separate client applications on the same Meteor server
69+
Angular’s modularity makes it easier to run few separate client applications on the same Meteor server.
7070

71-
### Eco-system and community
71+
### Ecosystem and community
7272

73-
* AngularJS community is huge. there are very mature community and team solutions for almost any requirement you can think of -
74-
Local-storage, forms, UI components, calendars, maps and of course many more
75-
* There are a lot of other frameworks that works great with AngularJS - React, Famo.us, Ionic Framework, ngCordova
76-
* The amount of community’s code inside AngularJS core code is huge, the framework core is very easy to look into and pull request
73+
* AngularJS community is huge and very mature. They have solutions for almost any requirement you can think of -
74+
local storage, forms, UI components, calendars, maps and of course many more.
75+
* There are a lot of other frameworks that works great with AngularJS - e.g. React, Famo.us, Ionic Framework, ngCordova.
76+
* The amount of the community’s code inside AngularJS core code is substantial, and the framework core is very easy to look into and pull request.
7777

7878
### Learning curve
7979

80-
* AngularJS introduces a lot of new concepts for Meteor developers like directives, dependency injection and filters.
81-
Some of those might be hard for a starting developer.
82-
those concepts are important for a lot of use cases, but if you want to keep it simple, you don’t have to learn them.
83-
* It is hard to believe that a very hard framework to learn would grow so fast in 2 years.
80+
* AngularJS introduces a lot of new concepts for Meteor developers like directives, dependency injection and filters. Some of those might be hard for a starting developer. Those concepts are important for a lot of use cases, but if you want to keep it simple, you don’t have to learn them.
81+
* It is hard to believe that a framework with such a steep learning curve would grow so fast in 2 years, but it has.
8482
* Events - for some, handling events on top of HTML makes the code much more readable and easy than handling it in the javascript code like
85-
the JQuery concept
83+
the JQuery concept.
8684

8785
### The “Extending HTML” approach is taken also by browsers, standards and Polymer
8886

8987
### Code sharing
9088

91-
As AngularJS is not intrusive and can work inside other technologies, using it gives you the possibility to share code and knowledge with other Frameworks different than Meteor
89+
As AngularJS is not intrusive and can work inside other technologies, using it gives you the possibility to share code and knowledge with other frameworks besides Meteor.
9290

9391
### Future
9492

9593
Angular 2.0 - Angular’s team chose to rewrite the whole framework for its version 2.0.
96-
as it sounds scary, we appreciate the courage and it gives a lot of room for the framework to become a lot better while still holding the huge experience with the existing code.
97-
there are not a lot of frameworks that did that.
94+
As it sounds scary, we appreciate the courage and it gives a lot of room for the framework to become a lot better while still holding the huge experience with the existing code.
95+
There are not a lot of frameworks that have the commitment and audacity to do that.
9896

9997
----
10098

@@ -107,8 +105,7 @@
107105
Two of Meteor core principles that you can actually see when developing with it:
108106

109107
1. Nothing in Meteor should harm the experience of a new Meteor developer - building an app with Meteor is as fast and easy as it gets.
110-
2. Nothing in Meteor should preclude an expert from doing what they want - Meteor is not similar to Firebase.
111-
Meteor is a full blown server, you can do almost anything in Firebase compared to Meteor
108+
2. Nothing in Meteor should preclude an expert from doing what they want - Meteor is not similar to Firebase. Meteor is a full blown server, you can do much more in Meteor than in Firebase.
112109

113110
### Super easy, Real-time integration with the database
114111

@@ -118,21 +115,20 @@
118115

119116
### Amazing tools for working with Cordova applications
120117

121-
### Eco-system - Like AngularJS, Meteor is also built from combining a lot of separate, individual packages written by the core team and the community -
122-
Accounts and permissions, Search, sorting and pagination from client until the server, underscore, client and server router and much more
118+
### Ecosystem - Like AngularJS, Meteor is also built from combining a lot of separate, individual packages written by the core team and the community:
119+
Accounts and permissions, Search, sorting and pagination from client until the server, underscore, client and server router and much more.
123120

124121
### Easy deployment
125122

126123
----
127124

128125
## Should I use angular-meteor or Asteroid
129126

130-
Asteroid is a very interesting solution on top of meteor.
131-
it separates the client side completely from Meteor so that you can take an existing front-end solution and still use Meteor reactivity and methods.
127+
Asteroid is a very interesting solution on top of Meteor. It separates the client side completely from Meteor so that you can take an existing front-end solution and still use Meteor reactivity and methods.
132128

133129
In Asteroid you are missing Meteor’s client side abilities like using Blaze templates, latency compensation and minimongo and the Cordova support.
134130

135-
With Asteroid you need to choose and can’t use both AngularJS and Blaze
131+
With Asteroid you need to choose and can’t use both AngularJS and Blaze.
136132

137133

138134
{{/markdown}}

lib/diff-array.js

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
'use strict';
2+
3+
var module = angular.module('diffArray', []);
4+
5+
var idStringify = LocalCollection._idStringify;
6+
var idParse = LocalCollection._idParse;
7+
8+
// Calculates the differences between `lastSeqArray` and
9+
// `seqArray` and calls appropriate functions from `callbacks`.
10+
// Reuses Minimongo's diff algorithm implementation.
11+
var diffArray = function (lastSeqArray, seqArray, callbacks) {
12+
var diffFn = Package.minimongo.LocalCollection._diffQueryOrderedChanges;
13+
var oldIdObjects = [];
14+
var newIdObjects = [];
15+
var posOld = {}; // maps from idStringify'd ids
16+
var posNew = {}; // ditto
17+
var posCur = {};
18+
var lengthCur = lastSeqArray.length;
19+
20+
_.each(seqArray, function (doc, i) {
21+
newIdObjects.push({_id: doc._id});
22+
posNew[idStringify(doc._id)] = i;
23+
});
24+
_.each(lastSeqArray, function (doc, i) {
25+
oldIdObjects.push({_id: doc._id});
26+
posOld[idStringify(doc._id)] = i;
27+
posCur[idStringify(doc._id)] = i;
28+
});
29+
30+
// Arrays can contain arbitrary objects. We don't diff the
31+
// objects. Instead we always fire 'changedAt' callback on every
32+
// object. The consumer of `observe-sequence` should deal with
33+
// it appropriately.
34+
diffFn(oldIdObjects, newIdObjects, {
35+
addedBefore: function (id, doc, before) {
36+
var position = before ? posCur[idStringify(before)] : lengthCur;
37+
38+
_.each(posCur, function (pos, id) {
39+
if (pos >= position)
40+
posCur[id]++;
41+
});
42+
43+
lengthCur++;
44+
posCur[idStringify(id)] = position;
45+
46+
callbacks.addedAt(
47+
id,
48+
seqArray[posNew[idStringify(id)]],
49+
position,
50+
before);
51+
},
52+
movedBefore: function (id, before) {
53+
var prevPosition = posCur[idStringify(id)];
54+
var position = before ? posCur[idStringify(before)] : lengthCur - 1;
55+
56+
_.each(posCur, function (pos, id) {
57+
if (pos >= prevPosition && pos <= position)
58+
posCur[id]--;
59+
else if (pos <= prevPosition && pos >= position)
60+
posCur[id]++;
61+
});
62+
63+
posCur[idStringify(id)] = position;
64+
65+
callbacks.movedTo(
66+
id,
67+
seqArray[posNew[idStringify(id)]],
68+
prevPosition,
69+
position,
70+
before);
71+
},
72+
removed: function (id) {
73+
var prevPosition = posCur[idStringify(id)];
74+
75+
_.each(posCur, function (pos, id) {
76+
if (pos >= prevPosition)
77+
posCur[id]--;
78+
});
79+
80+
delete posCur[idStringify(id)];
81+
lengthCur--;
82+
83+
callbacks.removedAt(
84+
id,
85+
lastSeqArray[posOld[idStringify(id)]],
86+
prevPosition);
87+
}
88+
});
89+
90+
_.each(posNew, function (pos, idString) {
91+
var id = idParse(idString);
92+
93+
if (_.has(posOld, idString)) {
94+
var newItem = seqArray[pos];
95+
var oldItem = lastSeqArray[posOld[idString]];
96+
var setDiff = diffObjectChanges(oldItem, newItem);
97+
var unsetDiff = diffObjectRemovals(oldItem, newItem);
98+
99+
if (setDiff)
100+
setDiff._id = newItem._id;
101+
102+
if (unsetDiff)
103+
unsetDiff._id = newItem._id;
104+
105+
if (setDiff || unsetDiff)
106+
callbacks.changedAt(id, setDiff, unsetDiff, pos);
107+
}
108+
});
109+
};
110+
111+
// Takes an object and returns a shallow copy, ie. with all keys at
112+
// a one-level depth. Transforms the name of each key using dot notation
113+
var flattenObject = function (object, parentKey) {
114+
var flattened = {};
115+
116+
angular.forEach(object, function (value, key) {
117+
if (isActualObject(value)) {
118+
angular.extend(flattened, flattenObject(value, key));
119+
} else {
120+
var dotNotedKey = (parentKey) ? parentKey + "." + key : key;
121+
flattened[dotNotedKey] = value;
122+
}
123+
});
124+
125+
return flattened;
126+
};
127+
128+
// Can tell whether a value is an object and not an array
129+
var isActualObject = function (value) {
130+
return angular.isObject(value) && !angular.isArray(value);
131+
};
132+
133+
// Diffs two objects and returns the keys that have been added or changed.
134+
// Can be used to construct a Mongo {$set: {}} modifier
135+
var diffObjectChanges = function (oldItem, newItem) {
136+
var result = {};
137+
138+
angular.forEach(newItem, function (value, key) {
139+
if (oldItem && angular.equals(value, oldItem[key]))
140+
return;
141+
142+
if (isActualObject(value)) {
143+
var diff = diffObjectChanges(oldItem[key], value);
144+
if (diff) result[key] = diff;
145+
} else {
146+
result[key] = value;
147+
}
148+
149+
// If a nested object is identical between newItem and oldItem, it
150+
// is initially attached as an empty object. Here we remove it from
151+
// the result if it was not empty from the beginning.
152+
if (isActualObject(result[key]) && _.keys(result[key]).length === 0) {
153+
if (_.keys(value).length !== 0)
154+
delete result[key];
155+
}
156+
});
157+
158+
if (!(_.keys(result).length > 0 && !(_.keys(result).length === 1 && result.$$hashKey)))
159+
return undefined;
160+
else
161+
return flattenObject(result);
162+
};
163+
164+
// Diffs two objects and returns the keys that have been removed.
165+
// Can be used to construct a Mongo {$unset: {}} modifier
166+
var diffObjectRemovals = function (oldItem, newItem) {
167+
if (newItem == null)
168+
return true;
169+
170+
var oldItemKeys = _.keys(oldItem);
171+
var newItemKeys = _.keys(newItem);
172+
var result = {};
173+
174+
angular.forEach(oldItemKeys, function (key) {
175+
if (!_.contains(newItemKeys, key))
176+
result[key] = true;
177+
178+
if (isActualObject(oldItem[key])) {
179+
var diff = diffObjectRemovals(oldItem[key], newItem[key]);
180+
if (diff) result[key] = diff;
181+
}
182+
});
183+
184+
if (_.keys(result).length === 0)
185+
return undefined;
186+
else
187+
return flattenObject(result);
188+
};
189+
190+
module.value('diffArray', diffArray);

0 commit comments

Comments
 (0)