spoonx / aurelia-config Goto Github PK
View Code? Open in Web Editor NEWConfiguration plugin that allows you to expose your configuration.
License: MIT License
Configuration plugin that allows you to expose your configuration.
License: MIT License
Hi!
I'm following your docs to configure the aurelia-forms plugin and i'm getting the above error. I'm using the aurelia-api plugin as well but that is configured in the standard way. I'm not sure how to resolve this or figure out what the problem may be. I've attached the bundled files for your convenience.
Thanks in advance!
import {Aurelia} from 'aurelia-framework'
import environment from './environment';
import * as entities from './config/entities';
import {Config} from 'aurelia-config';
import formConfig from './config/form';
export function configure(aurelia: Aurelia) {
aurelia.use
.standardConfiguration()
.feature('resources')
.plugin('aurelia-config', configure => {
return configure([
'aurelia-form'
// Other plugins
], formConfig);
})
.plugin('aurelia-api', config => {
config.registerEndpoint('github', 'https://api.github.com/');
config.registerEndpoint('bb-api', 'http://localhost:3000/api/', {headers: {'Content-Type': 'application/json; charset=utf-8'}});,
config.setDefaultEndpoint('bb-api');
})
// Register the plugin, and register our entities.
.plugin('aurelia-orm', builder => {
builder.registerEntities(entities);
})
.plugin('aurelia-validation');
if (environment.debug) {
aurelia.use.developmentLogging();
}
if (environment.testing) {
aurelia.use.plugin('aurelia-testing');
}
aurelia.start().then(() => aurelia.setRoot());
}
The formConfig is defined as follows:
export default {
'aurelia-form': {
// The default element used, probably best left unchanged
defaultElement: 'input',
// The default behavior to use for forms
defaultBehavior: 'regular',
// Register your (custom) elements here
elements : {},
// Configured by aurelia-form-validation
validation : {},
// Submit button configuration for <aurelia-form /> and <entity-form />
submitButton: {
enabled: true, // Show the button
options: ['primary'], // Options to pass to the button
label : 'Submit' // Default (fallback) label of the button
},
// Alias these entity types to elements (used by aurelia-orm)
aliases: {
enum : 'radio',
int : 'input',
integer: 'input',
number : 'input',
float : 'input',
string : 'input',
bool : 'checkbox',
boolean: 'checkbox',
text : 'textarea'
}
}
};
Error:
Unhandled rejection TypeError: Cannot read property 'Config' of undefined
at Object.configure (http://localhost:9000/scripts/vendor-bundle.js:30677:46)
at http://localhost:9000/scripts/vendor-bundle.js:12811:36
From previous event:
at _loadPlugin (http://localhost:9000/scripts/vendor-bundle.js:12809:42)
at http://localhost:9000/scripts/vendor-bundle.js:12802:16
From previous event:
at loadPlugin (http://localhost:9000/scripts/vendor-bundle.js:12801:75)
at next (http://localhost:9000/scripts/vendor-bundle.js:13063:20)
From previous event:
at next (http://localhost:9000/scripts/vendor-bundle.js:13063:56)
at http://localhost:9000/scripts/vendor-bundle.js:13070:16
From previous event:
at FrameworkConfiguration.apply (http://localhost:9000/scripts/vendor-bundle.js:13055:44)
at Aurelia.start (http://localhost:9000/scripts/vendor-bundle.js:12671:39)
at Object.configure (http://localhost:9000/scripts/app-bundle.js:74:17)
at http://localhost:9000/scripts/vendor-bundle.js:11572:29
From previous event:
at config (http://localhost:9000/scripts/vendor-bundle.js:11567:56)
at http://localhost:9000/scripts/vendor-bundle.js:11603:14
From previous event:
at bootstrap (http://localhost:9000/scripts/vendor-bundle.js:11602:26)
at http://localhost:9000/scripts/vendor-bundle.js:11589:9
From previous event:
at run (http://localhost:9000/scripts/vendor-bundle.js:11584:61)
at Object.<anonymous> (http://localhost:9000/scripts/vendor-bundle.js:11609:37)
at Object.execCb (http://localhost:9000/scripts/vendor-bundle.js:5480:33)
at Module.check (http://localhost:9000/scripts/vendor-bundle.js:4667:51)
at Module.enable (http://localhost:9000/scripts/vendor-bundle.js:4960:22)
at Object.enable (http://localhost:9000/scripts/vendor-bundle.js:5341:39)
at Module.<anonymous> (http://localhost:9000/scripts/vendor-bundle.js:4945:33)
at http://localhost:9000/scripts/vendor-bundle.js:3918:23
at each (http://localhost:9000/scripts/vendor-bundle.js:3843:31)
at Module.enable (http://localhost:9000/scripts/vendor-bundle.js:4897:17)
at Module.init (http://localhost:9000/scripts/vendor-bundle.js:4572:26)
at http://localhost:9000/scripts/vendor-bundle.js:5244:36
printWarning @ vendor-bundle.js:1395
formatAndLogError @ vendor-bundle.js:1111
fireRejectionEvent @ vendor-bundle.js:1136
Promise._notifyUnhandledRejection @ vendor-bundle.js:582
(anonymous) @ vendor-bundle.js:132
setTimeout (async)
(anonymous) @ vendor-bundle.js:131
Promise.resolve (async)
18../util @ vendor-bundle.js:3155
s @ vendor-bundle.js:30
(anonymous) @ vendor-bundle.js:30
1../queue @ vendor-bundle.js:34
s @ vendor-bundle.js:30
(anonymous) @ vendor-bundle.js:30
module.exports @ vendor-bundle.js:2146
3../promise @ vendor-bundle.js:271
s @ vendor-bundle.js:30
e @ vendor-bundle.js:30
(anonymous) @ vendor-bundle.js:30
(anonymous) @ vendor-bundle.js:30
(anonymous) @ vendor-bundle.js:30
Currently config uses defaults. I think it makes more sense to rename that, and simplify it. I've added a snippet that illustrates what sort of simplicity I'm looking for. It's untested code, so it'll probably not work.
import {Config} from './Config';
export function configure(aurelia, plugins, ...localConfigs) {
let pluginManager = aurelia.container.get(PluginManager);
let config = aurelia.container.get(Config);
config.merge({'aurelia-config': {configure: true}}).merge(localConfigs);
pluginManager.processPlugins(aurelia, plugins);
}
import {Loader} from 'aurelia-loader';
import {inject} from 'aurelia-dependency-injection';
import {Config} from './Config';
@inject(Config, Loader)
export class PluginManager {
constructor(config, loader) {
this.config = config;
this.ownConfig = config['aurelia-config'];
this.loader = loader;
}
processPlugins(aurelia, plugins) {
let processPlugins = [];
this.normalized(plugins, plugin => {
processPlugins.push(this.mergeConfig(plugin).then(() => this.registerPlugin(aurelia, plugin)));
});
return Promise.all(processPlugins);
}
registerPlugin(aurelia, plugin) {
aurelia.plugin(plugin.name, plugin.rootConfig ? this.config : this.config[plugin.name]);
}
mergeConfig(plugin) {
if (!plugin.configure) {
return Promise.resolve();
}
return this.loader.loadModule(plugin.name).then(loadedModule => {
this.config.merge(loadedModule.config);
});
}
normalized(plugins, handler) {
plugins.forEach(pluginDefinition => {
pluginDefinition = pluginDefinition || {};
if (typeof pluginDefinition === 'string') {
pluginDefinition = {name: pluginDefinition};
}
if (typeof pluginDefinition.configure === 'undefined') {
pluginDefinition.configure = this.ownConfig.configure;
}
handler(pluginDefinition);
});
}
}
import {Homefront} from 'homefront';
export class Config {
merge(data) {
Homefront.merge(this, data);
return this;
}
}
Bas @bas080 Sept. 05 10:29
I know the feeling.
I wrote a test for view manager which should pass but is not passing.
It has to do with the configureNamespace method.
Which i notice you have changed as it was nt performing correctly.
Bas @bas080 Sept. 05 10:35
What happens if you run the view manager tests? Does it exit with code 0?
doktordirk @doktordirk Sept. 05 10:38
lemme check
doktordirk @doktordirk Sept. 05 10:48
current master tests run, but did i disable one in master?
Bas @bas080 Sept. 05 10:53
no, They were already disabled. Mine keeps giving exit code: 1
doktordirk @doktordirk Sept. 05 10:53
but which of the tests?
Bas @bas080 Sept. 05 10:53
The real issue being that i am not able to override the namespace value or the defaults.
doktordirk @doktordirk Sept. 05 10:54
i should upload my sample i did for the swan-client. i'll to that asap
Bas @bas080 Sept. 05 10:54
I also find it weird that the defaults are the same as that of the form.
the defaults should just have
defaults = {
location: '{{framework}}/{{view}}.html',
framework: 'bootstrap',
map: {}
};
doktordirk @doktordirk Sept. 05 10:58
i'm lost. where is that?
Bas @bas080 Sept. 05 10:58
But when i look in the defaults object in one of the projects the map is not empty but filled with
Config {defaults: Object, namespaces: Object}
defaults
:
Object
base
:
"./../component"
framepath
:
"{{base}}/framework/{{framework}}"
framework
:
"bootstrap"
location
:
"./{{framework}}/{{view}}.html"
map
:
Object
actions
:
"{{framepath}}/actions"
association
:
"{{framepath}}/association"
button
:
"{{framepath}}/input.html"
checkboxes
:
"{{framepath}}/checkboxes"
collection
:
"{{framepath}}/collection"
color
:
"{{framepath}}/input.html"
conditional
:
"{{framepath}}/conditional"
date
:
"{{framepath}}/input.html"
datetime
:
"{{framepath}}/input.html"
datetime-local
:
"{{framepath}}/input.html"
email
:
"{{framepath}}/input.html"
entity-form
:
"./schema-form.html"
form-field
:
"./form-field.html"
form-fields
:
"./form-fields.html"
month
:
"{{framepath}}/input.html"
number
:
"{{framepath}}/input.html"
password
:
"{{framepath}}/input.html"
radios
:
"{{framepath}}/radios"
range
:
"{{framepath}}/input.html"
schema-form
:
"./schema-form.html"
search
:
"{{framepath}}/input.html"
select
:
"{{framepath}}/select"
string
:
"{{framepath}}/input.html"
tel
:
"{{framepath}}/input.html"
time
:
"{{framepath}}/input.html"
url
:
"{{framepath}}/input.html"
week
:
"{{framepath}}/input.html"
srry for the syntax.
But basicly it set the map object with that of aurelia form.
It should only be set in the namespaces['spoonx/form'].map
doktordirk @doktordirk Sept. 05 11:01
you're using a current version? sounds like a problem i had, but that was fixed
so SpoonX/aurelia-view-manager@3d38d6f was what fixed that for me, but for you that is what causes your problems?
Bas @bas080 Sept. 05 11:19
i'm using 0.0.7
doktordirk @doktordirk Sept. 05 11:20
hmm
i'll try to up a version to the sample. then it might be easier to see/communicate
Bas @bas080 Sept. 05 11:21
So tha last plugin i define is the view manager with the following in as configuration
view.configureNamespace('spoonx/form', {
map: {
actions: 'template/form/buttons.html'
}
});
And it does not set the map.actions to that value.
doktordirk @doktordirk Sept. 05 11:22
hmm
Bas @bas080 Sept. 05 11:23
I also notice that the plugins are not loaded syncronously.
Even though i define the plugins in a certain order.
That is an issue!
doktordirk @doktordirk Sept. 05 11:25
hmm, i thought they would.
(hence i used promises in config, as does i18n)
Bas @bas080 Sept. 05 11:26
This is what causes the config to be re-overwritten with the aurelia-form values.
Bas @bas080 Sept. 05 11:39
We load plugins in the configure function of other plugins.
So when configuring we overwrite stuff configure in the configureCallback.
Maybe we should have a way to check if a plugin is already loaded which prevents being loaded again within the configure functions of other plugins.
That way stuff won't be overwritten.
Or perform configuration when aurelia bootstrapped.
doktordirk @doktordirk Sept. 05 12:10
hm hm nasty hm
doktordirk @doktordirk Sept. 05 13:01
/* @see https://github.com/spoonx/aurelia-datatable */
.plugin('aurelia-datatable')
/* @see https://github.com/spoonx/aurelia-pager */
.plugin('aurelia-pager')
/* @see https://github.com/spoonx/aurelia-form */
.plugin('aurelia-form')
/* @see https://github.com/spoonx/aurelia-datatable */
.plugin('aurelia-view-manager', config => {
config.configure({
'spoonx/datatable': {
location: 'customViews/datatable.html'
},
works for me. i'll try form next, as in the first try i have an error
doktordirk @doktordirk Sept. 05 13:16
works fine for me SpoonX/swan-example-client#32
Bas @bas080 Sept. 05 14:11
But in my case it does not as form's configure function is called in the configure function of other plugins.
Aurelia-filter calls form's configure function.
Which is performed after the aurelia-view-manager's configure function.
doktordirk @doktordirk Sept. 05 14:58
i'm a bit confused what you wanna do there. you want to change the button design everywhere from with aurelia-filter?
Bas @bas080 Sept. 05 14:59
I want to overwrite the buttons for aurelia-form.
A basic use-case.
doktordirk @doktordirk Sept. 05 14:59
but generally or only for within filter?
Bas @bas080 Sept. 05 14:59
I have done it now by doing it when aurelia has started.
Generally.
doktordirk @doktordirk Sept. 05 15:00
ok, so from with view.manager it worked now,
Bas @bas080 Sept. 05 15:00
Filter has it's own buttons defined. Does not use aurelia-form's
doktordirk @doktordirk Sept. 05 15:00
question would be what happens when changing from somewhere else eg a plugin
Bas @bas080 Sept. 05 15:00
It works when configure after all configuration is performed.
Because it does not overwrite the configuration.
doktordirk @doktordirk Sept. 05 15:01
i'm lost
so my sample code above works for you or not?
Bas @bas080 Sept. 05 15:02
Because aurelia-filter instantiates a plguing within it's configure function. It triggers the aurelia-forms configure function after all configure function are called because of the plugins defined in the main.js
That would work for me as none of the plugins load aurelia-form plugin('aurelia-form') in their configure function.
if they do it will overwrite my set aurelia-view-manager settings.
doktordirk @doktordirk Sept. 05 15:03
export function configure(aurelia) {
aurelia.plugin('aurelia-form');
that's needed?
seems oddish as an general approach
Bas @bas080 Sept. 05 15:04
That is defined in aurelia-filter and that is the cause of the overwritting of the settings.
So we should get rid of that approach everywhere.
doktordirk @doktordirk Sept. 05 15:05
calling a plugin configure from plugins surely can'T be good
but also, if calling it twice is a problem can't be good
Bas @bas080 Sept. 05 15:06
I guess so cause it's causing the overwriting of configurations defined by the user in the main.js
doktordirk @doktordirk Sept. 05 15:07
maybe just a check if plugin('aurelia-form') was called before and if not throw 'aurelia-form must be configured first'?
Bas @bas080 Sept. 05 15:08
That sounds better.
Bas @bas080 Sept. 06 14:18
found the reason for SpoonX/aurelia-form#77
aurelia/binding#250
your pull request will not fix it. I tried it already.
doktordirk @doktordirk Sept. 06 14:20
worked for me in ff. but also i did ask to review on purpose ๐
Bas @bas080 Sept. 06 14:24
What did you test it on? I used swan client.
doktordirk @doktordirk Sept. 06 14:25
me to, ff 47.0.1 . i have no old ie to test atm
i'll recheck though
Bas @bas080 Sept. 06 15:14
import 'bootstrap';
import {AuthService, AuthenticateStep} from "aurelia-authentication";
import appConfig from "config/app";
import authConfig from "config/auth";
import entities from "config/entities";
import localConfig from "config/local";
import routes from "config/routes";
import {Router} from "aurelia-router";
import {getLogger} from 'aurelia-logging';
import Backend from "i18next-xhr-backend";
export function configure(aurelia) {
let view;
if (localConfig.app.environment !== 'production') {
aurelia.use.developmentLogging();
}
aurelia.use
.globalResources('component/converter/dateFormatValueConverter')
.globalResources('component/converter/toStringValueConverter')
.globalResources('component/converter/limitValueConverter')
.globalResources('component/core/list-view')
.globalResources('component/core/filter')
.globalResources('component/page/page.html')
.globalResources('component/modal/modal.html')
.standardConfiguration()
.plugin('aurelia-api', builder => {
localConfig.app.endpoints.forEach(endpoint => {
builder.registerEndpoint(endpoint.name, endpoint.endpoint, endpoint.config);
if (endpoint.default) {
builder.setDefaultEndpoint(endpoint.name);
}
});
})
.plugin('aurelia-authentication', baseConfig => {
baseConfig.configure(authConfig);
})
.plugin('aurelia-orm', builder => {
builder.registerEntities(entities);
})
.plugin('aurelia-notification', config => {
config.configure({
translate: true
});
})
.plugin('aurelia-i18n', instance => {
instance.i18next.use(Backend);
instance.setup({
backend: {
loadPath: 'scripts/config/locale/{{lng}}/{{ns}}.json'
},
lng : appConfig.defaultLocale.language,
attributes : ['t'],
fallbackLng: appConfig.defaultLocale.language,
debug : false
});
})
.plugin('aurelia-dialog', config => {
config.useDefaults();
config.settings.lock = false;
config.settings.centerHorizontalOnly = true;
})
.plugin('aurelia-datatable')
.plugin('aurelia-form')
.plugin('aurelia-filter')
.plugin('aurelia-view-manager', v => {
view = v
});
aurelia.start().then(a => {
view.configureNamespace('spoonx/form', {
map: {
actions: 'component/form/buttons.html'
}
});
a.container.get(Router).configure(configureRouter);
if (!a.container.get(AuthService).isAuthenticated()) {
return a.setRoot('login');
}
a.setRoot('app');
});
}
function configureRouter(config) {
config.title = appConfig.title;
config.addPipelineStep('authorize', AuthenticateStep);
config.map(routes);
}
const logger = getLogger('cmcp');
export {
logger
}
see the view let.
This is what i had to do as aurelia-filter reloads the configure function or aurelia-form. This is supposed to fix it. SpoonX/aurelia-form#80
So the configure function is called everytime and the configureOnce function only once.
doktordirk @doktordirk Sept. 06 15:24
hmm, don't like it. configure should only be called once anyways. it's called on startup to get a plugin initialized. if some other plugin needs it to be configured first, then it should check that itself or view-manager checks if it got initialized on certain calls and complains itself (like validation does). else often there shouldn't be the need to call configure, but rather the plugin should export sub-configuration functions, like our api Config or authes fectch-client-config.
Bas @bas080 Sept. 06 15:27
I'm for this approach as it allows other plugins to configure the plugins they depend on. Allowing loose coupling. Epecially in the case of aurelia-form I would like other plugins to extend or overwrite the available form element types.
doktordirk @doktordirk Sept. 06 15:29
they can. just that form needs to be configured first in the app setup
Bas @bas080 Sept. 06 15:29
Valid point.
Inject view manager or form config.
and your done.
So at most calling the configure function of other plugins in a plugin's configure function may provide some convenience and likely confusion.
doktordirk @doktordirk Sept. 06 15:34
yeah, configure function shouldn't be imported, call somewhere else
Configuring A Feature Copy
ES Next
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging()
.feature('feature-name', featureConfiguration);
aurelia.start().then(() => aurelia.setRoot());
}
Installing a Plugin Copy
ES Next
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging()
.plugin('plugin-name', pluginConfiguration);
aurelia.start().then(() => aurelia.setRoot());
}
that's the use for the configure function.
so in this case i'd suggest to add a isConfigured check to viewManagerConfig.configureNamespace and throw if it isn't
Bas @bas080 Sept. 06 15:37
That will encourage best practice for other plugin makers that use form
seemingly aurelia.use.plugin doens't respect the promise in in aurelia-config. that can lead to plugins being called without any data
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.