Jelajahi Sumber

accomodate async rendering/clearing for a view

Adam Shaw 10 tahun lalu
induk
melakukan
e30bf21197
2 mengubah file dengan 47 tambahan dan 29 penghapusan
  1. 2 2
      src/Calendar.js
  2. 45 27
      src/common/View.js

+ 2 - 2
src/Calendar.js

@@ -565,14 +565,14 @@ function Calendar_constructor(element, overrides) {
 
 			// render or rerender the view
 			if (
-				!currentView.isDisplayed ||
+				!currentView.displaying ||
 				!date.isWithin(currentView.intervalStart, currentView.intervalEnd) // implicit date window change
 			) {
 				if (elementVisible()) {
 
 					freezeContentHeight();
 					currentView.display(date);
-					unfreezeContentHeight();
+					unfreezeContentHeight(); // immediately unfreeze regardless of whether display is async
 
 					// need to do this after View::render, so dates are calculated
 					updateHeaderTitle();

+ 45 - 27
src/common/View.js

@@ -13,7 +13,7 @@ var View = fc.View = Class.extend({
 	coordMap: null, // a CoordMap object for converting pixel regions to dates
 	el: null, // the view's containing element. set by Calendar
 
-	isDisplayed: false,
+	displaying: null, // a promise representing the state of rendering. null if no render requested
 	isSkeletonRendered: false,
 	isEventsRendered: false,
 
@@ -263,56 +263,74 @@ var View = fc.View = Class.extend({
 
 	// Does everything necessary to display the view centered around the given date.
 	// Does every type of rendering EXCEPT rendering events.
+	// Is asychronous and returns a promise.
 	display: function(date) {
+		var _this = this;
 		var scrollState = null;
 
-		if (this.isDisplayed) {
+		if (this.displaying) {
 			scrollState = this.queryScroll();
 		}
 
-		this.clear(); // clear the old content
-		this.setDate(date);
-		this.render();
-		this.updateSize();
-		this.renderBusinessHours(); // might need coordinates, so should go after updateSize()
-		this.isDisplayed = true;
-
-		scrollState = this.computeInitialScroll(scrollState);
-		this.forceScroll(scrollState);
-
-		this.triggerRender();
+		return this.clear().then(function() { // clear the content first (async)
+			return _this.displaying =
+				$.when(_this.displayView(date)) // displayView might return a promise
+					.then(function() {
+						_this.forceScroll(_this.computeInitialScroll(scrollState));
+						_this.triggerRender();
+					});
+		});
 	},
 
 
 	// Does everything necessary to clear the content of the view.
 	// Clears dates and events. Does not clear the skeleton.
-	clear: function() { // clears the view of *content* but not the skeleton
-		if (this.isDisplayed) {
-			this.unselect();
-			this.clearEvents();
-			this.triggerDestroy();
-			this.destroyBusinessHours();
-			this.destroy();
-			this.isDisplayed = false;
+	// Is asychronous and returns a promise.
+	clear: function() {
+		var _this = this;
+		var displaying = this.displaying;
+
+		if (displaying) { // previously displayed, or in the process of being displayed?
+			return displaying.then(function() { // wait for the display to finish
+				_this.displaying = null;
+				_this.clearEvents();
+				return _this.clearView(); // might return a promise. chain it
+			});
+		}
+		else {
+			return $.when(); // an immediately-resolved promise
 		}
 	},
 
 
-	// Renders the view's date-related content, rendering the view's non-content skeleton if necessary
-	render: function() {
+	// Displays the view's non-event content, such as date-related content or anything required by events.
+	// Renders the view's non-content skeleton if necessary.
+	// Can be asynchronous and return a promise.
+	displayView: function(date) {
 		if (!this.isSkeletonRendered) {
 			this.renderSkeleton();
 			this.isSkeletonRendered = true;
 		}
+		this.setDate(date);
+		if (this.render) {
+			this.render(); // TODO: deprecate
+		}
 		this.renderDates();
+		this.updateSize();
+		this.renderBusinessHours(); // might need coordinates, so should go after updateSize()
 	},
 
 
-	// Unrenders the view's date-related content.
-	// Call this instead of destroyDates directly in case the View subclass wants to use a render/destroy pattern
-	// where both the skeleton and the content always get rendered/unrendered together.
-	destroy: function() {
+	// Unrenders the view content that was rendered in displayView.
+	// Can be asynchronous and return a promise.
+	clearView: function() {
+		this.unselect();
+		this.triggerDestroy();
+		this.destroyBusinessHours();
 		this.destroyDates();
+		if (this.destroy) {
+			this.destroy(); // TODO: deprecate
+		}
 	},