Replace angular route with custom router

This commit is contained in:
Pete Richards 2018-08-17 13:46:38 -07:00
parent 63f22c3f21
commit 4203dbf8e1
6 changed files with 260 additions and 125 deletions

View File

@ -43,6 +43,7 @@
"karma-html-reporter": "^0.2.7",
"karma-jasmine": "^1.1.2",
"karma-webpack": "^3.0.0",
"location-bar": "^3.0.1",
"lodash": "^3.10.1",
"markdown-toc": "^0.11.7",
"marked": "^0.3.5",

View File

@ -73,15 +73,6 @@ define([
legacyRegistry.register("platform/commonUI/browse", {
"extensions": {
"routes": [
{
"when": "/browse/:ids*?",
"template": browseTemplate,
"reloadOnSearch": false
},
{
"when": "",
"redirectTo": "/browse/"
}
],
"constants": [
{

View File

@ -55,16 +55,16 @@ define(
navigatedObject = this.navigationService.getNavigation(),
actionMetadata = action.getMetadata ? action.getMetadata() : {};
if (navigatedObject.hasCapability("editor") && navigatedObject.getCapability("editor").isEditContextRoot()) {
// if (navigatedObject.hasCapability("editor") && navigatedObject.getCapability("editor").isEditContextRoot()) {
if (selectedObject.hasCapability("editor") && selectedObject.getCapability("editor").inEditContext()) {
return this.editModeBlacklist.indexOf(actionMetadata.key) === -1;
} else {
//Target is in the context menu
return this.nonEditContextBlacklist.indexOf(actionMetadata.key) === -1;
}
} else {
return true;
}
// } else {
// return true;
// }
};
return EditContextualActionPolicy;

View File

@ -35,6 +35,7 @@ define([
'./ui/registries/ViewRegistry',
'./ui/registries/InspectorViewRegistry',
'./ui/registries/ToolbarRegistry',
'./ui/router/ApplicationRouter',
'../platform/framework/src/Main',
'./styles-new/core.scss',
'./ui/components/layout/Layout.vue',
@ -54,6 +55,7 @@ define([
ViewRegistry,
InspectorViewRegistry,
ToolbarRegistry,
ApplicationRouter,
Main,
coreStyles,
Layout,
@ -296,6 +298,12 @@ define([
this.install(LegacyIndicatorsPlugin());
this.router = new ApplicationRouter();
this.router.route(/^\/$/, () => {
this.router.setPath('/browse/mine');
});
/**
* Fired by [MCT]{@link module:openmct.MCT} when the application
* is started.
@ -312,11 +320,11 @@ define([
console.log('Attaching adapter');
installVueAdapter(appLayout, this);
this.router.start();
this.emit('start');
}.bind(this));
};
/**
* Install a plugin in MCT.
*

View File

@ -14,6 +14,27 @@ define([
}
}
// recursively locate and return an object inside of a container
// via a path. If at any point in the recursion it fails to find
// the next object, it will return the parent.
function findViaComposition(containerObject, path) {
var nextId = path.shift();
if (!nextId) {
return containerObject;
}
return containerObject.useCapability('composition')
.then(function (composees) {
var nextObject = findObject(composees, nextId);
if (!nextObject) {
return containerObject;
}
if (!nextObject.hasCapability('composition')) {
return nextObject;
}
return findViaComposition(nextObject, path);
});
}
function getLastChildIfRoot(object) {
if (object.getId() !== 'ROOT') {
return object;
@ -24,6 +45,15 @@ define([
});
}
function pathForObject(domainObject) {
var context = domainObject.getCapability('context'),
objectPath = context ? context.getPath() : [],
ids = objectPath.map(function (domainObj) {
return domainObj.getId();
});
return "/browse/" + ids.slice(1).join("/");
}
class MainAdapter {
constructor(layout, openmct) {
this.openmct = openmct;
@ -36,12 +66,6 @@ define([
this.navigationService = this.$injector.get('navigationService');
this.$timeout = this.$injector.get('$timeout');
// this.urlService = this.$injector.get('urlService');
// this.$route = this.$injector.get('$route');
// this.defaultPath = this.$injector.get('DEFAULT_PATH');
// this.initialPath = (this.$route.current.params.ids || defaultPath).split("/"),
// console.log('Initial path!', initialPath);
this.templateMap = {};
this.$injector.get('templates[]').forEach((t) => {
this.templateMap[t.key] = this.templateMap[t.key] || t;
@ -49,50 +73,34 @@ define([
var $rootScope = this.$injector.get('$rootScope');
this.scope = $rootScope.$new();
this.scope.representation = {};
this.run();
this.templateLinker.link(
this.scope,
angular.element(layout.$refs.mainContainer),
this.templateMap["browseObject"]
);
openmct.router.route(/^\/browse\/(.*)$/, (path, results) => {
let navigatePath = results[1];
if (!navigatePath) {
navigatePath = 'mine';
}
this.navigateToPath(navigatePath);
});
this.navigationService.addListener(o => this.navigateToObject(o));
this.getObject('ROOT')
.then(rootObject => {
this.rootObject = rootObject;
return getLastChildIfRoot(rootObject);
}
navigateToPath(path) {
if (!Array.isArray(path)) {
path = path.split('/');
}
return this.getObject('ROOT')
.then(root => {
return findViaComposition(root, path);
})
.then(o => {
this.navigationService.setNavigation(o, true);
.then(getLastChildIfRoot)
.then(object => {
this.setMainViewObject(object);
});
// this.navigateToRoot();
}
run() {
// TODO: navigate to default path.
// TODO: listen to route service and navigate on route changes?
// TODO: handle change to/from ?
}
// idsForObject(domainObject) {
// return this.urlService
// .urlForLocation("", domainObject)
// .replace('/', '');
// }
navigateToObject(object) {
this.scope.representation = {
selected: {
key: 'items'
}
};
// this.scope.domainObject = this.rootObject
setMainViewObject(object) {
this.scope.domainObject = object;
this.scope.navigatedObject = object;
this.templateLinker.link(
@ -100,84 +108,39 @@ define([
angular.element(this.layout.$refs.mainContainer),
this.templateMap["browseObject"]
);
// this.scope.navigatedObject = object;
this.scheduleDigest();
}
idsForObject(domainObject) {
return this.urlService
.urlForLocation("", domainObject)
.replace('/', '');
}
navigateToObject(object) {
let path = pathForObject(object);
let views = object.useCapability('view');
let params = this.openmct.router.getParams();
let currentViewIsValid = views.some(v => v.key === params['view']);
if (!currentViewIsValid) {
this.scope.representation = {
selected: views[0]
}
this.openmct.router.update(path, {
view: views[0].key
});
} else {
this.openmct.router.setPath(path);
}
}
scheduleDigest() {
this.$timeout(function () {
// digest done!
});
}
//
// navigateToObject(desiredObject) {
// this.ngEl = angular.element(this.layout.$refs.mainContainer);
// this.scope.navigatedObject = desiredObject;
// this.templateLinker.link(
// this.scope,
// this.ngEl,
// this.templateMap["browse-object"]
// );
//
// // $scope.navigatedObject = desiredObject;
// // $scope.treeModel.selectedObject = desiredObject;
// // currentIds = idsForObject(desiredObject);
// // $route.current.pathParams.ids = currentIds;
// // $location.path('/browse/' + currentIds);
// }
//
// navigateDirectlyToModel(domainObject) {
// var newIds = idsForObject(domainObject);
// if (currentIds !== newIds) {
// currentIds = newIds;
// navigateToObject(domainObject);
// }
// }
//
//
//
//
//
//
// // recursively locate and return an object inside of a container
// // via a path. If at any point in the recursion it fails to find
// // the next object, it will return the parent.
// findViaComposition(containerObject, path) {
// var nextId = path.shift();
// if (!nextId) {
// return containerObject;
// }
// return containerObject.useCapability('composition')
// .then(function (composees) {
// var nextObject = findObject(composees, nextId);
// if (!nextObject) {
// return containerObject;
// }
// if (!nextObject.hasCapability('composition')) {
// return nextObject;
// }
// return findViaComposition(nextObject, path);
// });
// }
//
// navigateToRoot() {
// this.getObject('ROOT')
// .then(o => this.scope.domainObject = 0);
// }
//
// navigateToPath(path) {
// return this.getObject('ROOT')
// .then(root => {
// return this.findViaComposition(root, path);
// })
// .then(getLastChildIfRoot)
// .then(object => {
// this.navigationService.setNavigation(object);
// });
// }
//
getObject(id) {
return this.objectService.getObjects([id])
.then(function (results) {

View File

@ -0,0 +1,172 @@
/**
Application router -- must
*/
const LocationBar = require('location-bar');
const EventEmitter = require('EventEmitter');
function paramsToObject(searchParams) {
let params = {};
for ([key, value] of searchParams.entries()) {
if (params[key]) {
if (!Array.isArray(params[key])) {
params[key] = [params[key]];
}
params[key].push(value);
} else {
params[key] = value;
}
}
return params;
}
class ApplicationRouter extends EventEmitter {
/**
* events
* change:params -> notify listeners w/ new, old, and changed.
* change:path -> notify listeners w/ new, old paths.
*
* methods:
* update(path, params) -> updates path and params at the same time. Only
* updates specified params, other params are not modified.
* updateParams(newParams) -> update only specified params, leaving rest
* intact. Does not modify path.
* updatePath(path);
* route(path, handler);
* start(); Start routing.
*/
constructor() {
super()
this.routes = [];
this.started = false;
}
/**
* start application routing, should be done after handlers are registered.
*/
start() {
if (this.started) {
throw new Error('Router already started!');
}
this.started = true;
let locationBar = new LocationBar();
locationBar.onChange(p => this.handleLocationChange(p));
locationBar.start({
root: location.pathname
});
}
handleLocationChange(pathString) {
let url = new URL(
pathString,
`${location.protocol}//${location.host}${location.pathname}`
)
let oldLocation = this.currentLocation;
let newLocation = {
url: url,
path: url.pathname,
queryString: url.search.replace(/^\?/, ''),
params: paramsToObject(url.searchParams)
};
this.currentLocation = newLocation;
if (!oldLocation) {
this.doPathChange(newLocation.path, null, newLocation);
this.doParamsChange(newLocation.params, {}, newLocation);
return;
}
if (oldLocation.path !== newLocation.path) {
this.doPathChange(
newLocation.path,
oldLocation.path,
this
);
}
if (!_.isEqual(oldLocation.params, newLocation.params)) {
this.doParamsChange(
newLocation.params,
oldLocation.params,
newLocation
);
}
}
doPathChange(newPath, oldPath, newLocation) {
let route = this.routes.filter(r => r.matcher.test(newPath))[0];
if (route) {
route.callback(newPath, route.matcher.exec(newPath));
}
this.emit('change:path', newPath, oldPath);
}
doParamsChange(newParams, oldParams, newLocation) {
let changedParams = {};
for (let [key, value] of Object.entries(newParams)) {
if (value !== oldParams[key]) {
changedParams[key] = value;
}
}
for (let [key, value] of Object.entries(oldParams)) {
if (!newParams.hasOwnProperty(key)) {
changedParams[key] = undefined;
}
}
this.emit('change:params', newParams, oldParams, changedParams);
}
/**
* Update route params. Takes an object of updates. New parameters
*/
updateParams(updateParams) {
let searchParams = this.currentLocation.url.searchParams;
for (let [key, value] of Object.entries(updateParams)) {
if (typeof value === 'undefined') {
searchParams.delete(key);
} else {
searchParams.set(key, value);
}
}
this.setQueryString(searchParams.toString());
}
getParams() {
return this.currentLocation.params;
}
update(path, params) {
let searchParams = this.currentLocation.url.searchParams;
for (let [key, value] of Object.entries(params)) {
if (typeof value === 'undefined') {
searchParams.delete(key);
} else {
searchParams.set(key, value);
}
}
this.set(path, searchParams.toString());
}
set(path, queryString) {
location.hash = `${path}?${queryString}`;
}
setQueryString(queryString) {
this.set(this.currentLocation.path, queryString);
}
setPath(path) {
this.set(path, this.currentLocation.queryString);
}
route(matcher, callback) {
this.routes.push({matcher, callback});
}
}
module.exports = ApplicationRouter;