Issue
I'm in the middle of migrating an old codebase of AngularJS from hundreds of <script>
tags to es modules (The only reason I mention that, is because it might give some insights into why I'm getting this odd behavior).
I found out that in the old codebase (and in every other place in StackOverflow), given an ng-click with $event, a jQuery.Event
is being injected into the callback:
<button ng-click="doX($event)">Click me</button>
$scope.doX = ($event) => {
console.log($event); // $event: jQuery.Event
};
But, in the new codebase (which I'm using Vite), I'm getting $event: PointerEvent
instead.
What's the explanation for this? does it check a global variable declaration to decide whether to wrap the original event with a jQuery?
See Playground
Solution
After debugging the source code of angular.js, I found these lines that led me to my solution
function bindJQuery() {
// ...
// bind to jQuery if present;
var jqName = jq();
jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present)
!jqName ? undefined : // use jqLite
window[jqName]; // use jQuery specified by `ngJq`
// Use jQuery if it exists with proper functionality, otherwise default to us.
// AngularJS 1.2+ requires jQuery 1.7+ for on()/off() support.
// AngularJS 1.3+ technically requires at least jQuery 2.1+ but it may work with older
// versions. It will not work for sure with jQuery <1.7, though.
if (jQuery && jQuery.fn.on) {
jqLite = jQuery;
extend(jQuery.fn, {
scope: JQLitePrototype.scope,
isolateScope: JQLitePrototype.isolateScope,
controller: /** @type {?} */ (JQLitePrototype).controller,
injector: JQLitePrototype.injector,
inheritedData: JQLitePrototype.inheritedData
});
} else {
jqLite = JQLite;
}
// ...
}
Basically, angular.js is looking for jQuery in the global window
variable. Since we're using ES modules, we can't just put window.$ = jQuery
in the beginning of our code.
A quick solution
Import jQuery globally in your index.html file.
A better solution
Since I'm using Vite, I created a plugin that allows me to modify the source code of angular.js
file. I added the following lines in the top file:
import $ from "jQuery";
window.jQuery = $;
The way I did it while maintaining the source maps, was by using recast.
Steps
- Create a file called
vite-plugin-third-party-modifiers.ts
:
import { PluginOption } from "vite";
import recast from "recast";
export function thirdPartyModifierPlugin(): PluginOption {
return {
name: "third-party-resolvers",
transform(src, id) {
const fileName = id.split(/[/]+/).pop().split("?")[0];
if (fileName === "angular.js") {
const ast = recast.parse(src);
const b = recast.types.builders;
// adds: window.$ = jQuery
ast.program.body.unshift(
b.importDeclaration(
[b.importDefaultSpecifier(b.identifier("jQuery"))],
b.literal("$")
)
);
// adds: import $ from "jquery"
ast.program.body.unshift(
b.expressionStatement(
b.assignmentExpression(
"=",
b.memberExpression(b.identifier("window"), b.identifier("$")),
b.identifier("jquery")
)
)
);
return recast.print(ast);
}
return null;
},
};
}
- import this plugin in
vite.config.js
export default defineConfig({
// ...
plugins: [
thirdPartyModifierPlugin(),
],
// ...
});
Answered By - Eliya Cohen
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.