Bootstrap show/hide operations do nothing in Phoenix app

phoenix

#5

P.S. I did my best due diligence searching for solutions. I found a number of partial ones, but none that I could put together into working code.


#6

I posted at same time as you so look above. ^.^

EDIT: Oh, and do note that you will still need what you already added, that is just ‘in addition to’.


#7

The above still doesn’t work. I still get this message:

28 Jul 09:10:35 - error: Resolving deps of web/static/js/app.js failed. Could not load module 'jquery' from '/Users/bem/src/critter4us/eecrit/web/static/js'. Possible solution: run `npm install`. a

Here are the diffs I’ve made to the files originally installed when the project was created:

brunch_config.js

package.json

The one line I’ve added (so far) to web/static/js/app.js:


#8

Hmm… Does the jquery lib have to be manually put in web/static/js? I’d been assuming npm install would do whatever was required.


#9

You should not move any node package from the node_modules to your directories as brunch can link those in. However the "web/static/vendor/js/jquery.min.js" (code tags in future? Pictures are horrible to copy/paste text… :frowning: ) implies that you have another jquery, and same with bootstrap, that is in your ‘order’ section.

Better yet, here is my entire npm package.json file:

{
  "repository": {},
  "license": "MIT",
  "scripts": {
    "deploy": "brunch build --production",
    "watch": "brunch watch --stdin"
  },
  "dependencies": {
    "bootstrap": "^4.0.0-alpha.2",
    "tether": "~1.3.2",
    "bootstrap-select": ">=1.10.0",
    "material-design-lite": ">=1.1.3",
    "material-design-icons": ">=2.2.3",
    "jquery": ">=3.0",
    "moment": ">=2.13.0",
    "highlight.js": ">=9.5.0",
    "phoenix": "file:deps/phoenix",
    "phoenix_html": "file:deps/phoenix_html"
  },
  "devDependencies": {
    "babel-brunch": "~6.0.0",
    "babel-plugin-transform-object-rest-spread": ">=6.8.0",
    "brunch": "~2.8.2",
    "clean-css-brunch": "~2.0.0",
    "elm-brunch": "0.6.0",
    "elmx": "1.0.5",
    "css-brunch": "~2.0.0",
    "javascript-brunch": "~2.0.0",
    "uglify-js-brunch": "~2.0.1"
  }
}

And my brunch-config.js file:

exports.config = {
  // See http://brunch.io/#documentation for docs.
  files: {
    javascripts: {
      joinTo: "js/app.js",
      order: {
        before: [
          "dist/js/jquery.min.js", // I don't think these four are necessary, just old holdovers...
          "dist/js/tether.min.js",
          "dist/js/bootstrap.min.js",
          "dist/js/bootstrap-select.min.js"
        ],
        after: [
          "web/static/js/app.js" // concat app.js last
        ]
      }
    },
    stylesheets: {
      joinTo: "css/app.css",
      order: {
        before: [
          "dist/css/tether.min.css",
          "dist/css/bootstrap.min.css",
          "dist/css/bootstrap-select.min.css",
          "dist/material.min.css",
          "dist/material.blue-light_blue.min.css",
          "iconfont/material-icons.css"
        ],
        after: [
          "web/static/css/app.css" // concat app.css last
        ]
      }
    },
    templates: {
      joinTo: "js/app.js"
    }
  },

  conventions: {
    // This option sets where we should place non-css and non-js assets in.
    // By default, we set this to "/web/static/assets". Files in this directory
    // will be copied to `paths.public`, which is "priv/static" by default.
    assets: /^(web\/static\/assets)/
  },


  // Phoenix paths configuration
  paths: {
    // Dependencies and current project directories to watch
    watched: [
      "web/elm",
      "web/static",
      "test/static"
    ],

    // Where to compile files to
    public: "priv/static"
  },

  // Configure your plugins
  plugins: {
    babel: {
      presets: ['es2015'],
      plugins: ["transform-object-rest-spread"],
      // Do not use ES6 compiler in vendor code
      ignore: [/web\/static\/vendor/],
      compact: false
    },
    uglify: {
      mangle: true
    },
    elmBrunch: {
      elmFolder: '.',
      mainModules: ['web/elm/MessengerApp.elm', 'web/elm/NotificationsApp.elm'],
      // If specified, all mainModules will be compiled to a single file (optional and merged with outputFolder)
      outputFolder: 'web/static/js',
      outputFile: 'elm.js',
      makeParameters: ['--warn']
    }
  },

  modules: {
    autoRequire: {
      "js/app.js": ["web/static/js/app"]
    }
  },

  npm: {
    enabled: true,
    whitelist: [
      "jquery",
      "tether",
      "bootstrap",
      "bootstrap-select",
      "material-design-lite",
      "highlight.js",
      "phoenix",
      "phoenix_html"],
    styles: {
      bootstrap: ["dist/css/bootstrap.min.css"],
      tether: ["dist/css/tether.min.css"],
      "material-design-lite": [
        "dist/material.min.css",
        "dist/material.blue-light_blue.min.css"
      ],
      "material-design-icons": [
        "iconfont/material-icons.css"
      ]
    }
  }
};

And my entire app.js (give-or-take a touch):

import "phoenix_html"

import $ from "jquery"
import "jquery"
import moment from "moment"
import "bootstrap-select"


// Ugh...
global.jQuery = require("jquery")  // Needed for tether or bootstrap...
global.Tether = require("tether")
global.bootstrap = require("bootstrap")
global.moment = moment


import socket from "./socket"

import setup_help from './components/help'
import setup_elm_messenger_app from './elm_apps/messenger'
import setup_elm_notifications_app from './elm_apps/notifications'

$(document).ready(() => {
  $('.dropdown-toggle').dropdown()
  $('[data-toggle="tooltip"]').tooltip()

  setInterval(() => {
    $.each($('.rel-time-display'), (index, val) => {
      let elem = $(val)
      let old_time = elem.text()
      let new_time = moment(elem.attr('data-time')).fromNow()
      if(old_time != new_time) {
        elem.text(new_time)
      }
    })
  },5000);

  if( /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) ) {
    $('.selectpicker').selectpicker('mobile');
  }
  else {
    $('.selectpicker').selectpicker()
  }
})


let socket_setup_once = false
socket.onOpen(() => {
  $(document).ready(function(){
    if(socket_setup_once) return
    socket_setup_once = true

    setup_help(socket)

    setup_elm_notifications_app(document.querySelector('#notifications-container'), socket)

    if(setup_elm_messenger_app(undefined, socket)) {
      $("#messenger-popup-dropdown").remove()
      $("#messenger-popup-button").removeClass('dropdown-toggle')
      $("#messenger-popup-button").addClass('disabled')
    }
    else {
      setup_elm_messenger_app(document.querySelector('#messenger-popup-container'), socket)
    }

  })
})

socket.connect()

And I know that all works. So perhaps use it as an example? :slight_smile:


#10

Works. I’ll write something up for other newbies, see if I can make a pull request.


#11

Cool. :slight_smile:


#12

Actually, I was wrong - I hadn’t changed the app.js. When I use yours, I still get the error:

28 Jul 10:49:29 - error: Resolving deps of web/static/js/app.js failed. Could not load module 'bootstrap' from '/Users/bem/src/critter4us/eecrit/web/static/js'. Possible solution: run `npm install`. a

Here’s the prefix of your file I used to get the above line:

import "phoenix_html"

import $ from "jquery"
import "jquery"
import moment from "moment"
import "bootstrap-select"


// Ugh...
global.jQuery = require("jquery")  // Needed for tether or bootstrap...
global.Tether = require("tether")
global.bootstrap = require("bootstrap")

Removing the last line lets it load without error.

Maybe I should have been a carpenter, like my dad wanted.


#13

Ah, it was this in yours:

"bootstrap": "^4.0.0-alpha.2",

My npm must not want to download alpha versions, so it just silently did nothing.


#14

o.O

Have I mentioned much how much I dislike the whole javascript ecosystem? >.>

You should be able to use the latest v3 of bootstrap though, I’m just using v4 because of certain changes in it that I like, however I am slowly fazing bootstrap out and replacing with material.


#15

Here are the diffs, stripped down to just bootstrap and bootstrap-select. https://github.com/marick/eecrit/commit/699c0e83993e9e940144b3d4a60d0b0afe04fa18


#16

Yeah this part:

+// Original version had this note about next line:
+// “Needed for tether or bootstrap…”
+// Because I don’t know how/when the problem would
+// show up if it’s required for bootstrap, I’m leaving
+// it in.

Bootstrap inside its ‘require-style’ interface looks for a global jquery for whatever-the-frick-reason (might just be broken like that in v4, I’m unsure). It is utterly retarded though. Just one of many reasons I am slowly converting the bootstrap site into material (material handles resizing so much more gracefully than bootstrap as well).


#17

Oh hey, I just noticed your project description at https://github.com/marick/eecrit of:

Elixir/Elm version of Critter4Us

I’ve no clue what Critter4Us is but I use Elm, if you need any help with that integration that was also included in my above code (and I can supply some more of the Elm stuff if you need). :slight_smile:


#18

Hmm, going back to the initial non-working case, it should be enough to just do npm install --save jquery in order to have jQuery or anything else that’s compilant with CommonJS (anything non ancient) available for importing. Sometimes it’s good to issue a clean brunch build too, but that’s it.

npm.whitelist doesn’t appear by default in newest Phoenix boilerplate and order.before is redundant if things are imported directly from NPM. I guess your initial version didn’t work due to pinning down an old version of jquery. It’s either that or some weird Brunch bug related to module wrapping.


#19

I added those order.before's just to follow the pattern when the project was first set up when I did not know how brunch worked yet and just never ended up removing them. And as I recall from the docs the npm.whitelist if missing will import all node modules that you have, which makes my output huge, so I like to whittle it down, thus I guess it does not need to be there either, but I also like controlling exactly what goes in the output files. :slight_smile:


#20

Elm tips would be great. You may be the only person in the universe who has more than one entry in the mainModules array. I’m trying to do the same, and keep having problems with There are two Elm modules called DatePicker4Us on this page! Rename one of them.

What I’m trying to do, just to get started is put this on the front page:

main =
    Html.text "Consider this text a promise of future beauty"

… and a use of Bogdanp/elm-datepicker on another, both as embeds. If you had a stripped-down example of one of your elm-embedding .html files (and maybe templates/layout/app.html.eex and web/static/js/app.js), I think that would help a lot.


#21

So first in my ./web/elm/ directory I made a file of ./web/elm/ExampleElmApp.elm, and in that file I have:

module ExampleElmApp exposing (..)

import Html

main : Html.Html msg
main = Html.text "Consider this text a promise of future beauty"

Then in my ./brunch-config.js file I added the new root App by altering the elmBrunch part of the plugins section like:

  // Configure your plugins
  plugins: {
    // snip other plugins that I have
    elmBrunch: { // I included my entire elmBrunch section with two Apps instead of my larger amount for brevity
      elmFolder: '.',
      mainModules: [
        'web/elm/MessengerApp.elm',
        'web/elm/ExampleElmApp.elm' // I added this line to add the new App, with a comma on the previous line of course
      ],
      // If these next two are specified, all mainModules will be compiled to a single file (optional and merged with outputFolder)
      outputFolder: 'web/static/js',
      outputFile: 'elm.js',
      makeParameters: ['--warn']
    }
  },

And upon saving this my server printed:

Elm compile: web/elm/MessengerApp.elm web/elm/ExampleElmApp.elm, in ., to web\static\js\elm.js
29 Jul 15:59:44 - info: compiling
29 Jul 15:59:48 - info: compiled 2 files and 193 cached into app.js in 12.3 sec

It takes so long because I have SO many files now, still blazing fast compared to when I tried webpack…

But now I go to my app.js and add it to my appropriate starting code. I have a section for things that do not require a phoenix active socket (which this one does not), and a section that does (which does not start those Apps until it is active, but that is just because I am too lazy to check if the socket is open before I start using it in Elm). So the relevant bits with the irrelevant omitted of my app.js:


import $ from "jquery"
import Elm from '../elm'

$(document).ready(() => { // Standard jQuery function run when the page finishes loading
  let app = Elm.ExampleElmApp.embed(document.querySelector('#my-elm-example'))
  // I have a lot of Helper libraries that make Elm a *LOT* easier to use that I attach via Elm Ports here with app.
}

And then in, oh let’s say my ./web/templates/layout/app.html.eex' file I add this to the` section:

    <div id="my-elm-example"></div>

And then I run it and I see at the top of every single one of my server pages:

Consider this text a promise of future beauty

Aaaand then I run git reset --hard to complete undo my examples changes so I can keep working. ^.^

EDIT: Do note, there is a bit of a race condition between elm compiling and brunch recognizing it so if that happens you will not see elm changes until the next compile, so I am in the habit of saving, waiting until the compiled appears in 10 seconds or so, then saving again. I should probably report this to elm-brunch…

EDIT2: It also really is best to compile all Elm Apps into a single elm.js file so that you do not duplicate the Elm standard library code inside each package and so Brunch/Babel can properly trim out what is not needed.

EDIT3: I use the elm-brunch plugin for brunch for handling it, which you just add to the devDependencies section of your NPM package.json file:

    "elm-brunch": "~0.6.0",

#22

Thanks. My difficulty is getting different apps to run on different pages. For example, suppose I want ExampleElmApp to run somewhere in the middle of templates/page/index.html.eex and a SecondExampleElmApp to run somewhere in the middle of templates/old_animal/index.html.eex.


#23

I guess I can put everything in web/static/js/app.js, like this:

import Elm from "./elm"

$(document).ready(() => {
    let frontPageForUsPlace = document.querySelector('#FrontPageForUs');
    let frontPageForUsApp;
    if (frontPageForUsPlace) {
        frontPageForUsApp = Elm.FrontPage4Us.embed(frontPageForUsPlace);
    }

    let onePageForUsPlace = document.querySelector('#OnePageForUs');
    let onePageForUsApp;
    if (onePageForUsPlace) {
        onePageForUsApp = Elm.OnePage4Us.embed(onePageForUsPlace);
    }
})

That seems inelegant.


#24

I try to keep Elm apps as something like components. There-for I can ‘tag’ that I want one on a given page in a given place by just putting an appropriately ID’d div. However you could setup brunch to run more then one main module so not only your app.js is run but others can be as well, that way you can make more than one final ‘app.js’. I prefer the singular file case, that way I can add something to a page I may not have originally considered adding.

I have each of my Elm apps wrapped in another javascript file, they get passed a default ID object and do their own testing if it exists else ignores. That lets me build it up with other things too if I wish.