0

I want to organize the use of socket and Presence in a template in my own way. As a newbie, it seems to me that putting all the client code that deals with channels in sockets.js can convert it into a very large piece. Especially if there are many pages that use sockets / Presence. I do not know which is the best solution (or if this is a problem for other people), but my misunderstood functioning of js, leads me to organize the code well.

In socket.js

    import {Socket, Presence} from "phoenix"
    // Other stuff
    export {
      Presence,
      socket
    }

In app.js, instead of import socket from "./socket" at teh end, I write

import {Presence, socket} from "./socket"

window.getPhoenixSocket = function () {
    return socket;
};
window.getPhoenixPresence = function () {
    return Presence;
};

And in the template eex (I know it's not good practice to mix html and scripts) (except for the React guys!) I write the following script:

<script>
  window.onload = function () {
    let channel = getPhoenixSocket (). channel ("cute: channel", {})
    let Presence = getPhoenixPresence ();
....
// Here I can use channel and Presence for my obscure purposes
....
}
</script>

Thus the code of each page that uses socket remains in the own page and not in socket.js

As I am not entirely satisfied with this solution, I ask you, is this correct? Is not it? What would be the best way to do it? Is there someone who has this problem?

===== EDIT ======

SOLUTION1:

A clear, intelligent, kind and complete response has been exposed by peerreynders at elixirforum

SOLUTION2:

Another solution, which Deini points to in Elixir-Lang's Slack Channel, using Webpack again, and involves much more Phoenix-Elixir style, has been published by hoang_nguyen on Medium.

SOLUTION3:

Using Brunch and using Webpack in Diacode and the second page here

Santi
  • 491
  • 6
  • 14
  • "What would be the best way to do it?" -- This question is going to lead to opinionated answers. I don't think there is one "best" way to do it. You are going ot have to figure out what best suites your needs. With that said, there is not reason you cannot just keep that javascript in your js files and avoid writing to the window. If you are using something like brunch or webpack, you can create more js files and only call the ones that are necessary for the page that you are on. There are a lot of tutorials around on how to properly use your asset builder (brunch, webpack, etc). – Justin Wood Jun 12 '18 at 12:45
  • Thank you for your help, your reputation precedes you, I think it was said that I was not in agreement with the solution that I have given, and that is why I was looking for 'opinion' and not simply that they redirected me to the documentation. Thanks to peerreynders, a clear answer, much more intelligent, friendly and complete has been exposed in elixirforum (I have linked it in the question itself), instead of discrediting a question that could help other people to be better programmers and understand the complexities of this environment. – Santi Jun 13 '18 at 08:18

1 Answers1

0

The solution chosen, has several parts and as indicated in diacode with few changes can be applied with the future Webpack in Phoenix> = 1.4.

The first thing is to indicate which js file we want to load, and for convenience we name it the same as the module-template pair.

The name is created in a function in layout_view.ex, and is placed as an attribute in the Body tag of app.html.eex like this:

<body data-js-view-name="<%= js_view_name(@view_module, @view_template) %>">

Then, app.js has the responsibility to load the specific js for each template, which we have placed in assests/js/views, and it does calling a loading function.

import loadView from './views/view_loader';

function handleDOMContentLoaded() {
  //// Get the current view name
  const viewName = document.getElementsByTagName('body')[0].dataset.jsViewName;
  // Load view class and mount it
  const ViewClass = loadView(viewName);

  window.currentView = new ViewClass();
  window.currentView.mount();
}

function handleDocumentUnload() {
  window.currentView.unmount();
}

window.addEventListener('DOMContentLoaded', handleDOMContentLoaded, false);
window.addEventListener('unload', handleDocumentUnload, false);

The loader of the view is :

    import MainView    from './main';
    import RoomShowView from './room_show';
    import UserShowView from './user_show';

    // Collection of specific view modules
    const views = {
      "room_show": RoomShowView,
      "user_show": UserShowView
    };

    export default function loadView(viewName) {
      return views[viewName] || MainView;
    }

for now, you have to import the views (this will change with Webpack)

The structure of each js file is a pair of functions mount and unmount, (that invoke a view parent, by default) where we place our code.

import MainView from './main';
import socket from "../socket";
import {Presence} from "phoenix";

export default class View extends MainView {
  mount() {
    super.mount();
    // the code
    console.log('room-show mounted');
    console.log('eex room.id: '+eex["@room.id"])
  }

  unmount() {
    super.unmount();

    // Specific logic here
    console.log('room-show unmounted');
  }
}

In the template we have placed the variables that are embedded from the server in a dict that we access from the dynamically loaded code.

<script>
var eex = {
  "@room.id": <%= @room.id %>,
  "@current_user.name": "<%= @current_user.name %>",
  "@current_user.id": <%= @current_user.id %>
}
</script>

The idea was to use a single channel only in one template, and maintain another channel for general purposes of all the templates. The channel is created in app.js, and you can push, in any other template, I have put it in main.js

I omit the code, since it's in the link and I'm sure you've seen it anywhere else.

There is still much to be done, it is an unfinished code. But there is on GitHub

Santi
  • 491
  • 6
  • 14