Elixir Phoenix serve Javascript from /priv/static - javascript

I am currently fighting esbuild in my phoenix project. I have a heex template on which I want to use Trumbowyg as a text editor. First I tried to import the javascript file via vendoring it and doing import trumbowyg from '../vendor/trumbowyg.min.js in app.js.
I thought this would work because it did for AlpineJs. But it didn't. It complained about missing jQuery. So I vendored jQuery the same way: import {jQuery, $} from '../vendor/jquery.min.js. But no success. Same error message Uncaught ReferenceError: $ is not defined.
Then I had the idea to fix it via importing the scripts in the template withing <script> tags. SO i just threw the two js files into the /priv/static/assets/ folder. This worked in development with the following tags:
<script type="text/javascript" src={Routes.static_path(#conn, "/assets/jquery.min.js")}></script>
<script type="text/javascript" src={Routes.static_path(#conn, "/assets/trumbowyg.min.js")}></script>
But this stopped working in production (I use the docker deploy method). Then I tried using some kind of Plug.Static implementation but I did not get it to work.
So my question now is: How can I make those scripts load in a production environment? Even better would be to know how to configure esbuild to use deploy the script files correctly. I don't know what to change, because my AlpineJs import is working fine.
import "phoenix_html"
// Establish Phoenix Socket and LiveView configuration.
import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
import topbar from "../vendor/topbar"
import Alpine from "../vendor/alpine.min"
window.Alpine = Alpine;
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {
params: {_csrf_token: csrfToken},
dom: {
onBeforeElUpdated(from, to) {
if (from._x_dataStack) {
window.Alpine.clone(from, to)
}
}
}
})
// Show progress bar on live navigation and form submits
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
window.addEventListener("phx:page-loading-start", info => topbar.show())
window.addEventListener("phx:page-loading-stop", info => topbar.hide())
// connect if there are any LiveViews on the page
liveSocket.connect()
// expose liveSocket on window for web console debug logs and latency simulation:
// >> liveSocket.enableDebug()
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
// >> liveSocket.disableLatencySim()
window.liveSocket = liveSocket
That's the content of my app.js file. But like I said, adding: import trumbowyg from '../vendor/trumbowyg.min.js or import {jQuery, $} from '../vendor/jquery.min.js gets me errors like Uncaught ReferenceError: $ is not defined and Uncaught ReferenceError: jQuery is not defined.
Every help is appreciated. Thanks in advance.

Related

Elixir Phoenix - How to create and import javascript files into specific templates

I'm currently experimenting with the Elixir Phoenix framework together with Liveview. For my project, I would like to write some Javascript code that is only imported on certain pages (templates). Although this seems like something very trivial, I am struggling to get it working.
At this moment I created a seperate Javascript file as such assets/js/custom.js. After doing this, I added the following line to my root.html.heex as a first test to see if this already works. For this line, I simply looked at how app.js is imported.
<script defer phx-track-static type="text/javascript" src={Routes.static_path(#conn, "/assets/custom.js")}></script>
The next step would then be to figure out how to import it in a seperate template instead of the root. However, this first test already failed resulting in the following error:
[debug] ** (Phoenix.Router.NoRouteError) no route found for GET /assets/custom.js (MyAppWeb.Router)
(my_app 0.1.0) lib/phoenix/router.ex:405: MyAppWeb.Router.call/2
(my_app 0.1.0) lib/my_app_web/endpoint.ex:1: MyAppWeb.Endpoint.plug_builder_call/2
(my_app 0.1.0) lib/plug/debugger.ex:136: MyAppWeb.Endpoint."call (overridable 3)"/2
(my_app 0.1.0) lib/my_app_web/endpoint.ex:1: MyAppWeb.Endpoint.call/2
(phoenix 1.6.15) lib/phoenix/endpoint/cowboy2_handler.ex:54: Phoenix.Endpoint.Cowboy2Handler.init/4
(cowboy 2.9.0) c:/Users/arnod/Desktop/phoenixtut/my_app/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
(cowboy 2.9.0) c:/Users/arnod/Desktop/phoenixtut/my_app/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
(cowboy 2.9.0) c:/Users/arnod/Desktop/phoenixtut/my_app/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3
(stdlib 4.0.1) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Could somebody help me figure this one out? How does one add seperate Javascript files and only import them in specific templates?
You can import all your custom javascript once in app.js, assign them as hooks which you can then use in your (live) views, wherever needed, for example;
custom.js
export const SomeFunction = {
mounted() {
alert("some function ran!");
}
}
app.js snippet
...
import {SomeFunction} from "./custom.js"
let Hooks = {}
Hooks.SomeFunction = SomeFunction
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}, hooks: Hooks})
...
Then in your live view render function (or template) add the hook
...
def render(assigns) do
~H"""
...
<div id="my-hook" phx-hook="SomeFunction"></div>
...
end
...
More about javascript interoperability can be found on the Phoenix hex page here. You can tie them to all sorts of phoenix events.
nb. Also note that #conn isn't available in live views, only #socket is.

How to import octokit in browser

tried to follow an example from this. To import octokit I first pasted the following lines into my html document as stated by adding the following lines:
<script type="module">
import { Octokit } from "https://cdn.skypack.dev/#octokit/core";
</script>
This just resulted in an Uncaught (in promise) ReferenceError: Octokit is not defined. Moving the line in script tags directly into the script.js file above the code just made one of the main functions appear as undefined (it handled a button click that authenticated a google account and then retrieved data from google sheets via the sheets API).
I then tried all this with new html and js files, adding that code directly to the html had the same result but this time adding it to the start of the js file gives a import declarations may only appear at top level of a module script.js:1
Don't really know what to do from here so any help would be appreciated. Did find this though but it just says the issue is fixed?
I think the best solution is to write your script logic in a separate javascript file, and then import that file in your html at the end of body element.
Here is an example of getting your repositories from GitHub:
repos.js
import { Octokit } from "https://cdn.skypack.dev/octokit";
import { config } from "./config.js";
const GIT_KEY = config.API_KEY;
export const octokit = new Octokit({
auth: GIT_KEY,
});
const repo = await octokit.request(
"GET /users/{username}/repos{?type,sort,direction,per_page,page}",
{
username: "your_username",
}
);
console.log(repo);
repos.html:
<body>
...
<script type="module" src="repos.js"></script>
</body>

Problem using sweetalert2 and filepond in Laravel 9.2

Hell guys ,
I am using Laravel 9 and I want to use sweetalert2 and filepond in my project.
I installed the mentioned plugins through npm.
This is my app.js file
import './bootstrap';
import Alpine from 'alpinejs';
import * as FilePond from 'filepond';
import Swal from 'sweetalert2';
import 'filepond/dist/filepond.min.css';
window.Alpine = Alpine;
window.FilePond = FilePond;
window.Swal = Swal;
I also included this in head tag
#vite(['resources/css/app.css', 'resources/js/app.js'])
I also run this command in terminal without any error.
npm run dev
This is my script tag for sweetalert2.
#if(Session::has('status'))
// This works but I don't want it -> {{-- <script src="//cdn.jsdelivr.net/npm/sweetalert2#11"></script> --}}
// This one is not working
<script>
Swal.fire({
toast: true,
position: 'top-end',
icon: 'success',
title: "{{Session::get('status')}}",
showConfirmButton: false,
timer: 1500
})
</script>
#endif
This is my script tag for filepond.
<script>
// Get a reference to the file input element
const inputElement = document.querySelector('input[type="file"]');
// Create a FilePond instance
const pond = FilePond.create(inputElement);
</script>
I also get this error after using sweetalert2 or filepond
Uncaught ReferenceError: Swal is not defined
Uncaught ReferenceError: FilePond is not defined
Is it because of the vite?
How can I fix this?
I used sweetalert2 in my previous project in Laravel 8.x and that was working and the app.js file looked like this.
require('./bootstrap');
window.Swal = require('sweetalert2')
import Alpine from 'alpinejs';
window.Alpine = Alpine;
Alpine.start();
This require function is not working in Laravel 9.2 and I get the same error require is not defined.
The problem was my I was trying to access Swal and FilePond before they have been loaded into the window. A fix for that is to make sure you only execute some JavaScript after the window and its properties have loaded.
window.addEventListener('load', () => {
// window has loaded...
});
Or you can listen for when the document has finished loading.
document.addEventListener('DOMContentLoaded', () => {
// document has loaded...
});

Import package in javascript file which is script for html

I am doing electron project. In it every tutorial i have watched is adding js in <script> tag i want to separate the js code from html. So i
...
<script type="text/javascript" src="./../scripts/upload.js" ></script>
...
<input onchange="upload()" id='train-button' type="file">File</input>
....
and in js file i
function upload(){
const electron = require('electron');
const {ipcRenderer} = electron;
if (x.files.length == 0) {
alert("Select one or more files.");
}else{
ipcRenderer.send("saveFile",targetFile,destinationPath)
}
}
Now here i am getting error that
Uncaught ReferenceError: require is not defined
but if i define require outside the upload function then it is not running that line and saying
electron is undefined
Try setting nodeIntegration:true in your BrowserWindow config. Doing so will enable require in your js file.
However, this is very poor security practice. I recommend using a security-focused template.
This post might also help explain to you more about best-practices in electron apps regarding using IPC between renderer/main processes.

How do I manually include "#material/drawer" into my component?

I am trying to manually include the #material/drawer npm package into my Ember app. I tried following this guide but I'm running into some weird errors in my Chrome dev console:
Uncaught SyntaxError: Unexpected token *
Uncaught ReferenceError: define is not defined
The first is from the imported node_modules/#material/drawer/index.js file and the second is from my generated shim.
My component code:
import Component from '#ember/component';
import { MDCTemporaryDrawer, MDCTemporaryDrawerFoundation, util } from '#material/drawer';
export default Component.extend({
init() {
this._super(...arguments);
const drawer = new MDCTemporaryDrawer(document.querySelector('.mdc-drawer--temporary'));
document.querySelector('.menu').addEventListener('click', () => drawer.open = true);
}
});
In my ember-cli-build.js:
app.import('node_modules/#material/drawer/index.js');
app.import('vendor/shims/#material/drawer.js');
My generated shim:
(function() {
function vendorModule() {
'use strict';
return {
'default': self['#material/drawer'],
__esModule: true,
};
}
define('#material/drawer', [], vendorModule);
})();
What exactly am I doing wrong? It almost seems as though raw ES6 code got imported rather than compiled into my JS build output.
I also read this SO post but there are too many answers and I'm not sure which to do. It seems this specific answer is what I'm trying to do but not verbatim enough.
Creating a shim only ensures that ember-cli gets an AMD module, which you then can import in your app files.
If the npm package needs a build or transpiling step beforhand, this won't work.
You need a way to get the package build within the ember-cli build pipeline.
Luckily there are addons which can take care of this for you: ember-auto-import and ember-cli-cjs-transform.
You may have also heard of ember-browserify, which does the same thing, but it's deprectaed in favor of ember-auto-import.
I'd suggest you try ember-auto-import:
ember install ember-auto-import
You then should be able to import as you tried:
import { MDCTemporaryDrawer, MDCTemporaryDrawerFoundation, util } from '#material/drawer';
No shim or app.import needed, as ember-auto-import will take care of this for you.

Categories

Resources