Skip to content

Commit

Permalink
Merge branch 'master' into 2226-deferUpdates-if-binding-2
Browse files Browse the repository at this point in the history
  • Loading branch information
mbest committed Jun 3, 2017
2 parents 554022b + d20958b commit 5dc0ba0
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 19 deletions.
37 changes: 23 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Knockout

**Knockout** is a JavaScript [MVVM](http://en.wikipedia.org/wiki/Model_View_ViewModel) (a modern variant of MVC) library that makes it easier to create rich, desktop-like user interfaces with JavaScript and HTML. It uses *observers* to make your UI automatically stay in sync with an underlying data model, along with a powerful and extensible set of *declarative bindings* to enable productive development.

##Getting started
## Getting started

[![Join the chat at https://gitter.im/knockout/knockout](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/knockout/knockout?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

Expand All @@ -11,40 +13,47 @@ For more details, see
* Documentation on [the project's website](http://knockoutjs.com/documentation/introduction.html)
* Online examples at [http://knockoutjs.com/examples/](http://knockoutjs.com/examples/)

##Downloading Knockout
## Downloading Knockout

You can [download released versions of Knockout](http://knockoutjs.com/downloads/) from the project's website.

For Node.js developers, Knockout is also available from [npm](https://npmjs.org/) - just run `npm install knockout`.

##Building Knockout from sources
## Building Knockout from sources

If you prefer to build the library yourself:

1. **Clone the repo from GitHub**
1. **Clone the repo from GitHub**

git clone https://github.com/knockout/knockout.git
cd knockout
```sh
git clone https://github.com/knockout/knockout.git
cd knockout
```

2. **Acquire build dependencies.** Make sure you have [Node.js](http://nodejs.org/) installed on your workstation. This is only needed to _build_ Knockout from sources. Knockout itself has no dependency on Node.js once it is built (it works with any server technology or none). Now run:
2. **Acquire build dependencies.**

npm install -g grunt-cli
npm install
Make sure you have [Node.js](http://nodejs.org/) installed on your workstation. This is only needed to _build_ Knockout from sources. Knockout itself has no dependency on Node.js once it is built (it works with any server technology or none). Now run:

The first `npm` command sets up the popular [Grunt](http://gruntjs.com/) build tool. You might need to run this command with `sudo` if you're on Linux or Mac OS X, or in an Administrator command prompt on Windows. The second `npm` command fetches the remaining build dependencies.
```sh
npm install -g grunt-cli
npm install
```

3. **Run the build tool**
The first `npm` command sets up the popular [Grunt](http://gruntjs.com/) build tool. You might need to run this command with `sudo` if you're on Linux or Mac OS X, or in an Administrator command prompt on Windows. The second `npm` command fetches the remaining build dependencies.

grunt
3. **Run the build tool**

Now you'll find the built files in `build/output/`.
```sh
grunt
```
Now you'll find the built files in `build/output/`.

## Running the tests

If you have [phantomjs](http://phantomjs.org/download.html) installed, then the `grunt` script will automatically run the specification suite and report its results.

Or, if you want to run the specs in a browser (e.g., for debugging), simply open `spec/runner.html` in your browser.

##License
## License

MIT license - [http://www.opensource.org/licenses/mit-license.php](http://www.opensource.org/licenses/mit-license.php)
28 changes: 28 additions & 0 deletions spec/asyncBehaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -1209,5 +1209,33 @@ describe('Deferred', function() {
expect(threeNotifications).toEqual([true, false]);
}
});

it('Should only notify changes if computed was evaluated', function() {
// See https://github.com/knockout/knockout/issues/2240
// Set up a scenario where a computed will be marked as dirty but won't get marked as
// stale and so won't be re-evaluated
this.restoreAfter(ko.options, 'deferUpdates');
ko.options.deferUpdates = true;

var obs = ko.observable('somevalue'),
isTruthy = ko.pureComputed(function() { return !!obs(); }),
objIfTruthy = ko.pureComputed(function() { return isTruthy(); }).extend({ notify: 'always' }),
notifySpy = jasmine.createSpy('callback'),
subscription = objIfTruthy.subscribe(notifySpy);

obs('someothervalue');
jasmine.Clock.tick(1);
expect(notifySpy).not.toHaveBeenCalled();

obs('');
jasmine.Clock.tick(1);
expect(notifySpy).toHaveBeenCalled();
expect(notifySpy.argsForCall).toEqual([[false]]);
notifySpy.reset();

obs(undefined);
jasmine.Clock.tick(1);
expect(notifySpy).not.toHaveBeenCalled();
});
});
});
5 changes: 4 additions & 1 deletion src/subscribables/dependentObservable.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,9 @@ var computedFn = {
if (!state.isSleeping && notifyChange) {
computedObservable["notifySubscribers"](state.latestValue);
}
if (computedObservable._recordUpdate) {
computedObservable._recordUpdate();
}
}

if (isInitial) {
Expand Down Expand Up @@ -376,7 +379,7 @@ var computedFn = {

// Pass the observable to the "limit" code, which will evaluate it when
// it's time to do the notification.
this._limitChange(this);
this._limitChange(this, !isChange /* isDirty */);
};
},
dispose: function () {
Expand Down
15 changes: 11 additions & 4 deletions src/subscribables/subscribable.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ var ko_subscribable_fn = {

limit: function(limitFunction) {
var self = this, selfIsObservable = ko.isObservable(self),
ignoreBeforeChange, notifyNextChange, previousValue, pendingValue, beforeChange = 'beforeChange';
ignoreBeforeChange, notifyNextChange, previousValue, pendingValue, didUpdate,
beforeChange = 'beforeChange';

if (!self._origNotifySubscribers) {
self._origNotifySubscribers = self["notifySubscribers"];
Expand All @@ -107,16 +108,19 @@ var ko_subscribable_fn = {
if (selfIsObservable && pendingValue === self) {
pendingValue = self._evalIfChanged ? self._evalIfChanged() : self();
}
var shouldNotify = notifyNextChange || self.isDifferent(previousValue, pendingValue);
var shouldNotify = notifyNextChange || (didUpdate && self.isDifferent(previousValue, pendingValue));

notifyNextChange = ignoreBeforeChange = false;
didUpdate = notifyNextChange = ignoreBeforeChange = false;

if (shouldNotify) {
self._origNotifySubscribers(previousValue = pendingValue);
}
});

self._limitChange = function(value) {
self._limitChange = function(value, isDirty) {
if (!isDirty || !self._notificationIsPending) {
didUpdate = !isDirty;
}
self._changeSubscriptions = self._subscriptions[defaultEvent].slice(0);
self._notificationIsPending = ignoreBeforeChange = true;
pendingValue = value;
Expand All @@ -128,6 +132,9 @@ var ko_subscribable_fn = {
self._origNotifySubscribers(value, beforeChange);
}
};
self._recordUpdate = function() {
didUpdate = true;
};
self._notifyNextChangeIfValueIsDifferent = function() {
if (self.isDifferent(previousValue, self.peek(true /*evaluate*/))) {
notifyNextChange = true;
Expand Down

0 comments on commit 5dc0ba0

Please sign in to comment.