I want to intercept the module loading of Aurelia to redirect some calls.
To do such things the aurelia-loader has a addPlugin() interface. You add a suffix like !myplugin to a resource to mark that it should be loaded using that plugin.
Now when I do this with my own component, it loads the JS file but the name for the HTML template is messed up. Like from the resource name my-comp!myplugin it will load my-comp.js but tries to find my-comp!myplugin.html which does not match the plugin name anymore.
I have provided a gist with that issue here: https://gist.run/?id=c7ed477bc652540ed8b0702d843f1832
The loader plugin code in main.js is basically:
loader.addPlugin('gate', {
fetch(address) {
console.info('Intercepted:', address);
var tmpParts = address.split('.');
var extension = tmpParts[tmpParts.length - 1].toLowerCase();
if (extension == 'css') {
console.debug('Loading as styles', address);
return loader.loadText(address);
} else if(extension == 'html') {
console.debug('Loading as template:', address);
return loader.loadTemplate(address);
} else {
console.debug('Loading as module:', address);
return loader.loadModule(address);
}
}
});
Using it like this in a template (marked with Issue 1 in the gist):
<require from="./comp2!gate"></require>
After that is should be possible to load the component like this:
<comp2></comp2>
Or even like this:
<compose view-model="./comp2!gate"></compose>
Instead the name for the template is messed up, the browser console says:
Failed to load resource: the server responded with a status of 404 ()
https://gist.host/run/1485182959149/comp2!gate.html
The expected name of the template would be https://gist.host/run/1485182959149/comp2.html!gate (including the plugin)
How can I fix the loader plugin to work correctly?
The loader is aurelia-loader-default 1.0.0, JSPM is 0.16.39, Node is 6.5.0, NPM 3.10.5.
I have added a second gist.run: https://gist.run/?id=1ddd4233e3afc40d89eb64b751e1dd8f
It is a bit shorter. When I specify the view using #useView decorator in comp2.js (marked with issue 4), it works - but I cannot specify a loader plugin with #useView. I would expect it to load the view with the same loader plugin or be able to specify a loader plugin with #useView.
Okay, I found a solution which looks like it can work with less code and patching and it works between two gists.
This gist contains the external component comp3:
https://gist.run/?id=dc837978a514011e13c872dbad92ae3f
This gist is the basic Aurelia app with a plugin and a small patch to the applyPluginToUrl method of the loader:
https://gist.run/?id=39e6fdacefc9e5c69b42a5e8c9049384
If the gist URLs are fixed, it should work for everyone. The Aurelia app loads comp3 from the first gist and displays it (you see the purple border define in the comp3-view).
There is one caveat at the moment: No support for CSS as SystemJS adds the extension .js to them, looks like I have to take care of that.
I do not like the path to loader.applyPluginToUrl but SystemJS does not really support plugin chaining and so the order must be correct. Also this solution requires all external components to set #useView including the loader plugin.
Any better approaches?
Related
When I make changes to any function .js file under ./src/fn hot reload is not working and triggers a page reload.
I have been trying to make configure vue.config.js to include the directory to HMR correctly. Also I tried using my custom helpers as a Vue.use(myPlugin).
/* functions.js (just a part of it) */
export const Functions = {
game: {
helper: helpers,
turn: turn,
ui: ui,
validate: validate,
card: cards
},
}
/* main.js */
import Functions from './functions.js';
Vue.prototype.$myFn = Functions;
Expected HMR to work but instead get a full page reload when making changes to functions.js (or any underlying js file imported in functions.js)
Adding your own objects and functions to the Vue prototype is a bit of an anti-pattern. In this case, Webpack can't determine the extent of the changes made so it falls back to a page reload. This is because ES modules can be statically analysed whereas global objects cannot be.
Here's some articles about static analysis:
Static Program Analysis
ES Modules in Depth
Remove the functions from the Vue protoype and always use ES modules to structure your functions. Then you can import them into other modules or components without using the legacy style global hack approach of yesteryear.
In particular, can the typescript source of the ag-grid-vue component be compiled then included in a regular html file?
I found a way to do this without modifying the source. Near the top of ag-grid-vue.umd.js, you can see that the module does this:
root["ag-grid-vue"] = factory(root["Vue"], root["agGrid"]);
Here, "root" is the window, and the result of the factory call is what you'll want. But due to the dashes, you can't access it directly. But you can use the dictionary syntax (or whatever it's called):
let agVueObj = window["ag-grid-vue"];
//The component is a field on this object:
let AgGridVue = agVueObj.AgGridVue;
//Then register it as a component in your Vue instance:
//components: { AgGridVue }
And you should be able to use <ag-grid-vue> tags.
I figured it out:
Download the ag-grid source code, go into the packages/ag-grid-vue directory and do npm install and npm run build. That will put compiled javascript modules in the dist directory that can be used without a build system.
I did have to modify the built javascript slightly to get the AgGridVue object into the global namespace, since I'm not using a module loader.
EDIT:
To get the AgGridVue into the global namespace, add window.AgGridVue = AgGridVue; to the end of the function that returns AgGridVue in ag-grid-vue.umd.js
I am trying a new way of writing my ui and I am using straight ESM loading with Vue. As such I am trying to load my HTML files like I would with say Webpack. I have a simple example of what I am talking about. I basically want to take...
export default {
template: "<div>Here is the component. I want this template to be an html file without webpack</div>"
// I want this to be from a url say mysite.net/viewport.html
}
I tried the simple things like
import Template from "/viewport.html"
But of course that didn't work
I think there might be something I can do with dynamic components. Has anyone tried this an come up with a good solution?
We're happy users of Vue and its server-side rendering module, Vue SSR. One of the requirements of my project is that we be able to dynamically adjust Webpack's publicPath at runtime, so that we can obtain assets from our different CDNs (we have two, one for test and one for prod).
We are able to accomplish this easily on the client-side using the __webpack_public_path__ free variable, and you can also override the publicPath within the SSR client manifest for asset URLs injected into the <head>.
Yet we continue to have issues with asset URLs that are housed directly within our templates and are rendered by SSR. For example, imagine if we had the following image within our tag:
<img src="~#/test.png" />
Our goal is that, on both the server and the client, we could adjust that URL to be prefixed how we please via publicPath. There doesn't seem to be a way to dynamically update the publicPath once the vue-ssr-server-manifest.json has been generated, the resulting URL ends up being something relative like /static/test.png or whatever we original cited in our Webpack config.
Per our project constraints, it's not possible to rebuild our SSR bundle, so we need to do this at runtime. We've tried adding placeholder values as our publicPath, e.g. __CUSTOM_WEBPACK_PUBLIC_PATH__, and replacing them in the server bundle at runtime, but that ends up being ineffective since publicPath is also embedded in other Webpack generated files.
Wondering if there is a cleaner way to achieve what we need directly via Vue SSR, or if this is something we just can't configure at runtime. Thanks for reading!
Late follow-up here, but we eventually solved this issue.
We found that setting __webpack_public_path__ globally in our Node.js process did result in the correct public path being applied to our assets in our server bundle.
Once that global is present both on the window (e.g. client-side), and globally in the node process (e.g. server-side), things started working as we wanted.
We faced similar type of problems in our webapp as well. BTW, we implemented a CDN plugin for vue.
export const CDNPlugin = {
install(Vue, { CDN, assetsManifest }) {
Vue.prototype.$cdn = {
...CDN,
asset(name) {
return `${CDN.baseUrl}${assetsManifest[name]}`;
},
resource(filepath) {
return `${CDN.baseUrl}/resources/${filepath}`;
}
};
}
};
Install this plugin both of your ssr and csr file.
Vue.use(CDNPlugin, {
CDN: { baseUrl: 'https://my.static.cdn.com' },
assetsManifest: yourAssetManifestObject,
});
And the usage of this CDN plugin inside vue template is as below
<img :src="$cdn.asset('relative/path/to/asset/style.css')">
If you think it is helping a bit, then I can share more regarding our implementation.
I spent an entire day trying to figure this out. In the end I used this:
https://github.com/agoldis/webpack-require-from
Worked like a charm, client side and server. Be aware you need to set a global.MY_BASE_URL in your node/server somewhere AND you need to inject a window.MY_BASE_URL somewhere in your HTML. Then just configure webpack.
plugins.push(new WebpackRequireFrom({variableName: 'MY_BASE_URL'}));
Similar problem occurred in my project, and finally I worked it out.
Ryan's answer really helps, but there is one thing I want to clear up. __webpack_public_path__ is a LOCAL variable in webpack bundled code, which means __webpack_public_path__ and global.__webpack_public_path__ is not the same. You need to do something like
__webpack_public_path__ = process.env.ASSET_PATH;
to specify public path (https://webpack.js.org/guides/public-path/#on-the-fly FYI).
Last, please make sure your process.env.ASSET_PATH is not undefined, maybe you have to set it manually to global in your server code.
global.process.env.ASSET_PATH = process.env.ASSET_PATH;
I am getting an error message as element.dispatchEvent is not a function. I am using jQuery with prototype in rails 3 application. In my layout file, I have added the js files as below
javascript_include_tag 'jquery','jquery_ujs','prototype','shadowbox/shadowbox.js'
<script type="text/javascript">jQuery.noConflict();</script>
I have also added jQuery.noConflict as above and used jQuery instead of $ in jQuery functions. Any idea how to resolve this.?
In my another controller page action I have also mentioned the same thing as there are some js files which needs to be reloaded only for that particular page.
I am a newbie in js as well as rails also.
you should use jQuery.noConflict right after src to the jQuery library
Using jQuery.noConflict(); should be enough. Please check the code of the page in your browser so you can see when prototype is actually added.
You should have jQuery, then the .noConflict call, then prototype.
Besides adding the 'no conflict' method, I do this instead (though both would probably be best):
I'll 'preset' my custom script page. Let's say my prediction is that I will use maybe 5 blocks of code in a page - this is how I preset my page:
jQuery(document).ready(function($) {
// use $ in here like normal!
});
jQuery(document).ready(function($) {
});
jQuery(document).ready(function($) {
});
jQuery(document).ready(function($) {
});
etc. etc.
Notice this uses the jQuery object itself to pass as the callback function to the .ready method so you can once again use the $ identifier within the functions. I can rename it so their will never be a conflict, and I can use the $ identifier within the function like I normally would. Hope that helps.