Include a component as part of a plugin - aurelia

Initial Question
How, if at all, can we expose a view/view-model component from a plugin? For instance, we have the following:
// ./open-id/foo-bar.html
<template>
<p>FooBar</p>
</template>
// ./open-id/foo-bar.ts
export class FooBar { }
In the consuming application, we would like to do this:
// ./app.html
<require from="open-id/foo-bar"></require>
<foo-bar></foo-bar>
Edits
Simpler Name
Based on Robinson Collado's answer, we using a simpler name (foo not foo-bar) to reduce complexity.
// ./open-id/foo.html
<template>
<p>Foo</p>
</template>
// ./open-id/foo.ts
export class Foo { }
// ./app.html
<require from="open-id/foo"></require>
<foo></foo>
That approach threw this error:
Unhandled rejection Error: Load timeout for modules: template-registry-entry!open-id/foo.html,text!open-id/foo.html
Global Resource
Based on the Installing Plugins documentations, we tried adding the component as a global resource.
// ./open-id/open-id.ts
function configure(config: FrameworkConfiguration) {
config.globalResources('./foo');
}
That approach threw this error:
GET http://localhost:9000/src/open-id/open-id/foo.js 404 (Not Found)
That means Aurelia is looking for the component in open-id/open-id/, which is one directory too deep.
Loading as a Plugin
During development of the plugin, we're loading the plugin like this, which may be why Aurelia is looking one directory too deep. How can we load the plugin differently during developent?
// ./main.ts
aurelia.use.plugin("./open-id/open-id");
Loading as a Feature
aurelia.use.feature("./aurelia-open-id-connect");
The error now is this for each constructor that receiving an injection from our feature.
Message: key/value cannot be null or undefined. Are you trying to inject/register something that doesn't exist with DI?

Try changing the name of the custom tag <foo-bar></foo-bar> to <foobar></foobar>. The name of the tag should match the name of it's view-model class. Unless, if you use the #customElement decorator to explicitly declare the tag name for the custom element.

Related

Nuxt not automatically importing components from nested directory

In my nuxt app, components in nested directories are not automatically importing as expected. For some of my components i have something like the following:
vue 2.6.12, nuxt 2.15.0
components\ Directory structure
TopArea\
--SomeComponent.vue
<template>
<header class="header">
<div>Hello</div>
<SomeComponent />
</header>
</template>
No other component in the application has the name SomeComponent. In the example above i get the error: Unknown custom element: <SomeComponent> - did you register the component correctly? For recursive components, make sure to provide the "name" option.. I can get around the issue by specifying the directory name before the component filename (TopAreaSomeComponent), use the prefix option in nuxt.config, or by manually importing the component. This is confusing because the docs state:
Nested Directories
If you have components in nested directories such as:
components/baseButton.vue
The component name will be based on its own filename. Therefore, the component will be:
<button />
It goes on to say "We recommend you use the directory name in the filename for clarity". But that seems like a rule than a recommendation. If i don't use the directory name as part of the filename, dynamic imports are not working for components in nested directories.
Is this an error in the docs or am I reading it wrong?
Since Nuxt 2.15.0, components changed in the way they work as stated in this github issue.
Depending of you structure and how you want to handle your organization, you may edit your configuration accordingly to the migration guide available here: https://github.com/nuxt/components#v1-to-v2
Or you could also simply set the pathPrefix option to have all your components available without any prefix at all.
nuxt.config.js/ts
components: [
{
path: '~/components', // will get any components nested in let's say /components/test too
pathPrefix: false,
},
]
PS: this solution also works with Nuxt3.
This documentation actually do need an update, indeed: https://nuxtjs.org/docs/2.x/directory-structure/components#components-discovery
This is how it works: for a given page
<template>
<div>
<yolo-swag /> <!-- no need for <nested-yolo-swag /> here -->
</div>
</template>
And with the following file tree
Update for Nuxt3
Looks like this is the new official syntax
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
components: {
global: true,
dirs: ['~/components']
},
})
This may answered already. But to illustrate the solution to comers here here's the way according to docs:
<TopAreaSomeComponent />
if your components is nested deeply:
components / TopArea / SomeComponent.vue
https://nuxtjs.org/docs/directory-structure/components/#nested-directories

Load a different page in nuxt at runtime based on the parameters in the route

We have a scenario in which a different page is required to be loaded based on whether parts of the route has parameters that are valid and that can be determined at run-time.
Consider the following example:
Request to http://example.com/param1/param2
If param1 is a valid product identifier (can be determined by an API call to another service) the product page loads or its considered a category and Category Page is loaded.
Considering Nuxt uses static routes mostly and the list of products are dynamic, is there a hook where you can execute custom code to load a different page ?
Cant you create _product page
like described in nuxt docs:
https://nuxtjs.org/guide/routing/#dynamic-routes
And in your code make something like:
<template>
<div>
<nuxt-child />
</div>
</template>
<script>
export default {
asyncData({route, params, redirect}) {
//use route
console.log(route.params.slug)
//directly use params
console.log(params.slug)
redirect(`/`);
},
};
</script>
or use mounted() hook if you are creating SPA

How to add custom HTML tag TSX

While trying to render a custom HTML tag <my-element> in JSX an error displayed
Property does not exist on type 'JSX.IntrinsicElements'
I've found some examples of how to do that using
declare global {
interface IntrinsicElements {
"my-element": any
}
}
but this produced another error:
ES2015 module syntax is preferred over custom TypeScript modules and namespaces #typescript-eslint/no-namespace
I've found the useful link to Typescript guide which helped me a lot:
The main idea is to create a new file with extension d.ts (e.g. myModule.d.ts) which should contain the following
export as namespace JSX;
export interface IntrinsicElements {
"my-element": any;
}

Multiple Aurelia Instances - Aurelia Webpack Plugin - aureliaApp option - "module not found"

I am composing my web app as a number of Aurelia "feature" apps - although I'm not using Aurelia features as such. Consequently in my html markup I have two entry points pointing to different apps:
<!-- Top Navigation Bar -->
<div aurelia-app="topnav"></div>
<!-- Main App-->
<div aurelia-app="main"></div>
I am using webpack and everything works perfectly using the single "main" app. Webpack generates a JS file "main.bundle.js" which I include in the src tag.
Things are not so straightforward when I added the "topnav" app. In webpack I tell the plugin to use a different aureliaApp name:
new AureliaPlugin({ aureliaApp: "topnav"}),
and, as you can see my HTML entrypoint also calls "topnav". Webpack generates a JS file "topnav.bundle.js" which I also include. I have a file called "topnav.ts" which contains the aurelia Cionfigure function which ends:
aurelia.start().then(() => aurelia.setRoot(PLATFORM.moduleName("nav")));
And a pair of files "nav.ts", "nav.html" which constitute my viewmodel and view.
When I run the app aurelia loads and the "nav" module code executes. But I then get an error - see below.
The module which it reports that it cannot find is the one entered into the HTML markup.
Should this work? Have I missed something?
I should add, everything seems to work. I can create and update properties in the viewmodel and these are bound to the view. It's just that this error is thrown.
You are doing nothing wrong, just unsupported scenario. Per official doc-wiki: https://github.com/aurelia/webpack-plugin/wiki/AureliaPlugin-options#aureliaapp
You can have only 1 auto entry module with aureliaApp configuration. To solve this, you just need to add PLATFORM.moduleName('topnav') to your main.ts (and put it on root level)
Another way to do is to bootstrap manually:
// in your index.ts
import { bootstrap } from 'aurelia-bootstrapper';
// bootstrap top nav application, with one instance of Aurelia
bootstrap(aurelia => {
// do your configuration
aurelia
.start()
.then(() => aurelia.setRoot(
PLATFORM.moduleName('topnav'),
document.querySelector('#topnav')
);
});
// bootstrap main application, with another instance of Aurelia
bootstrap(aurelia => {
// aurelia.use.standardConfiguration();
// ...
aurelia
.start()
.then(() => aurelia.setRoot(
PLATFORM.moduleName('app'),
document.querySelector('app')
)
});

is this the correct declarative, data-dojo-type syntax to require a package?

I have a package, defined in dojoConfig like this:
packages: [
{ name: 'Widget', location: '/widgets/Widget' }
]
The /widgets/Widget/main.js file defines my main module. With this config, in Javascript i can require the module Widget/main directly by its package name like this:
require(["Widget"], function(Widget){
var widget = new Widget();
// all is well
});
But doing the same using declarative syntax throws a Unable to resolve constructor for: 'Widget' error:
<div data-dojo-type="Widget"></div>
Am I doing something wrong, or is this expected behaviour?
It would be easier to see how widget is created, but the complaint is that you have no constructor.
a constructor is required for a widget. If you extend WidgetBase its done for you.
check the doc:
http://dojotoolkit.org/reference-guide/1.9/quickstart/writingWidgets.html
You need to add the
require(["Widget"], function(Widget){});
part in a script tag in the HTML document you are using
<div data-dojo-type="Widget"></div>
It should look like something:
<script> require(["Widget"], function(Widget){}); </script>
You have to require the module/widget before you can call it in an HTML page the same way you need to do it in a script tag.