[API] Change approach to applies-to checking (#1072)

* [API] Allow selection

* [API] Keep in sync using model

* [API] Add selection as EventEmitter

* [API] Use selection from ToDo tutorial

* [API] Add appliesTo-style method

* [API] Remove destroy method, simplify show

* [View] Return a no-op

* [API] Use new applies-to checking

* [API] Rename TodoView to TodoRenderer

* [API] Rewire views

* [API] Wire up so that things work

* [API] Begin adding container

...to attempt to give views something to listen to for destroy-like
events

* [API] Begin using regions...

* [API] Begin working through Region stuff

* [API] Revise Region API

...for similarity with Marionette,
https://github.com/nasa/openmct/pull/1072#issuecomment-230902986

* [API] Begin separating View, ViewDefinition

* [API] Finish separating View/ViewDefinition

* [API] Update MCTView

...to reflect updates to Region/View/ViewDefinition APIs

* [API] Simplify View API

...merging closely-related populate/show methods, and restoring
compatibility with todo tutorial

* [API] Wire in from todo tutorial plugin

* [API] Switch back to region constants

* [API] Update method signature, add JSDoc

* [API] Update variable name

* [API] Remove unnecessary separate regions file

* [API] Relocate Region; not external api

* [API] Revert changes to api.js

...as these ended up becoming entirely superficial
This commit is contained in:
Victor Woeltjen 2016-07-20 13:46:03 -07:00 committed by Pete Richards
parent 1879c122c7
commit 18843cee48
6 changed files with 125 additions and 28 deletions

View File

@ -37,7 +37,13 @@ define([
this.legacyBundle.extensions[category].push(extension); this.legacyBundle.extensions[category].push(extension);
}; };
MCT.prototype.view = function (region, factory) { /**
* Register a new type of view.
*
* @param region the region identifier (see mct.regions)
* @param {ViewDefinition} definition the definition for this view
*/
MCT.prototype.view = function (region, definition) {
var viewKey = region + uuid(); var viewKey = region + uuid();
var adaptedViewKey = "adapted-view-" + region; var adaptedViewKey = "adapted-view-" + region;
@ -61,9 +67,9 @@ define([
this.legacyExtension('policies', { this.legacyExtension('policies', {
category: "view", category: "view",
implementation: function Policy() { implementation: function Policy() {
this.allow = function (view, domainObject) { this.allow = function (v, domainObject) {
if (view.key === adaptedViewKey) { if (v.key === adaptedViewKey) {
return !!factory(domainObject); return definition.canView(domainObject);
} }
return true; return true;
}; };
@ -71,7 +77,7 @@ define([
}); });
this.legacyExtension('newViews', { this.legacyExtension('newViews', {
factory: factory, factory: definition,
region: region, region: region,
key: viewKey key: viewKey
}); });

View File

@ -1,24 +1,26 @@
define(['angular'], function (angular) { define([
'angular',
'./Region'
], function (angular, Region) {
function MCTView(newViews) { function MCTView(newViews) {
var factories = {}; var definitions = {};
newViews.forEach(function (newView) { newViews.forEach(function (newView) {
factories[newView.region] = factories[newView.region] || {}; definitions[newView.region] = definitions[newView.region] || {};
factories[newView.region][newView.key] = newView.factory; definitions[newView.region][newView.key] = newView.factory;
}); });
return { return {
restrict: 'E', restrict: 'E',
link: function (scope, element, attrs) { link: function (scope, element, attrs) {
var key, mctObject, region; var key, mctObject, regionId, region;
function maybeShow() { function maybeShow() {
if (!factories[region] || !factories[region][key] || !mctObject) { if (!definitions[regionId] || !definitions[regionId][key] || !mctObject) {
return; return;
} }
var view = factories[region][key](mctObject); region.show(definitions[regionId][key].view(mctObject));
view.show(element[0]);
} }
function setKey(k) { function setKey(k) {
@ -31,15 +33,16 @@ define(['angular'], function (angular) {
maybeShow(); maybeShow();
} }
function setRegion(r) { function setRegionId(r) {
region = r; regionId = r;
maybeShow(); maybeShow();
} }
scope.$watch('key', setKey); region = new Region(element[0]);
scope.$watch('region', setRegion);
scope.$watch('mctObject', setObject);
scope.$watch('key', setKey);
scope.$watch('region', setRegionId);
scope.$watch('mctObject', setObject);
}, },
scope: { scope: {
key: "=", key: "=",

View File

@ -0,0 +1,23 @@
define([], function () {
function Region(element) {
this.activeView = undefined;
this.element = element;
}
Region.prototype.clear = function () {
if (this.activeView) {
this.activeView.destroy();
this.activeView = undefined;
}
};
Region.prototype.show = function (view) {
this.clear();
this.activeView = view;
if (this.activeView) {
this.activeView.show(this.element);
}
};
return Region;
});

View File

@ -1,23 +1,37 @@
define(function () { define([], function () {
/**
* A View is used to provide displayable content, and to react to
* associated life cycle events.
*
* @interface
*/
function View() { function View() {
} }
/** /**
* Show this view in the specified container. If this view is already * Populate the supplied DOM element with the contents of this view.
* showing elsewhere, it will be removed from that location.
* *
* @param {HTMLElement} container the element to populate * View implementations should use this method to attach any
* listeners or acquire other resources that are necessary to keep
* the contents of this view up-to-date.
*
* @param {HTMLElement} container the DOM element to populate
*/ */
View.prototype.show = function (container) { View.prototype.show = function (container) {
}; };
/** /**
* Release any resources associated with this view. * Release any resources associated with this view.
* *
* Subclasses should override this method to release any resources * View implementations should use this method to detach any
* they obtained during a `show` call. * listeners or release other resources that are no longer necessary
* once a view is no longer used.
*/ */
View.prototype.destroy = function () { View.prototype.destroy = function () {
}; };
return View; return View;

44
src/api/ViewDefinition.js Normal file
View File

@ -0,0 +1,44 @@
define(function () {
/**
* Defines a kind of view.
* @interface
*/
function ViewDefinition() {
}
/**
* Get metadata about this view, as may be used in the user interface
* to present options for this view.
* @param {*} object the object to be shown in this view
* @returns {mct.ViewMetadata} metadata about this view
*/
ViewDefinition.prototype.metadata = function (object) {
};
/**
* Instantiate a new view of this object. Callers of this method are
* responsible for calling `canView` before instantiating views in this
* manner.
*
* @param {*} object the object to be shown in this view
* @returns {mct.View} a view of this object
*/
ViewDefinition.prototype.view = function (object) {
};
/**
* Check if this view is capable of showing this object. Users of
* views should use this method before calling `show`.
*
* Subclasses should override this method to control the applicability
* of this view to other objects.
*
* @param {*} object the object to be shown in this view
* @returns {boolean} true if this view is applicable to this object
*/
ViewDefinition.prototype.canView = function (object) {
};
return ViewDefinition;
});

View File

@ -206,11 +206,18 @@ define([
}; };
mct.type('example.todo', todoType); mct.type('example.todo', todoType);
mct.view(mct.regions.main, function (domainObject) { mct.view(mct.regions.main, {
return todoType.check(domainObject) && new TodoView(domainObject); view: function (domainObject) {
return new TodoView(domainObject);
},
canView: todoType.check.bind(todoType)
}); });
mct.view(mct.regions.toolbar, function (domainObject) {
return todoType.check(domainObject) && new TodoToolbarView(domainObject); mct.view(mct.regions.toolbar, {
view: function (domainObject) {
return new TodoToolbarView(domainObject);
},
canView: todoType.check.bind(todoType)
}); });
return mct; return mct;