Adding JavaScript

webpack[27] is a build tool written in Node.js. We’ll use webpack to build, transform, and minify[28] JavaScript and CSS code. Processing assets in this way makes your page load much more efficiently. webpack not only takes care of JavaScript but also CSS and all of our application assets, such as images.

The asset structure is laid out in the assets directory:

 assets/
 ├── css/
 ├── js/
 ├── static/
 ├── vendor/
 ├── package.json
 └── webpack.config.js

We put everything in assets/static that doesn’t need to be transformed by webpack. The build tool will simply copy those static assets just as they are to priv/static, where they’ll be served by Plug.Static in our endpoint.

We keep CSS and JavaScript files in their respective directories. The vendor directory is used to keep any third-party tools you need, such as jQuery. This structure helps us organize code, but we’re also being practical. Let’s see why.

Open up assets/js/app.js and take a look as its contents:

 // We need to import the CSS so that webpack will load it
 import​ css ​from​ ​"../css/app.css"
 
 // webpack automatically bundles all modules in your
 // entry points. Those entry points can be configured
 // in "webpack.config.js".
 //
 // Import dependencies
 //
 import​ ​"phoenix_html"
 
 // Import local files
 // ...
 // import socket from "./socket"

Phoenix configures webpack to use ECMAScript 6 (ES6)—the latest JavaScript specification we’ll use in this book—to provide the necessary import statements. webpack wraps the contents for each JavaScript file you add to assets/js in a function and collects them into priv/static/js/app.js. That’s the file loaded by browsers at the end of lib/rumbl_web/templates/layout/app.html.eex when we call Routes.static_url(@conn, "/js/app.js").

Since each file is wrapped in a function, it won’t be automatically executed by browsers unless you explicitly import it in your app.js file. In this way, the app.js file is like a manifest. It’s where you import and wire up your JavaScript dependencies. For example, we have imported phoenix_html as it provides some functionality to our HTML forms and buttons.

The assets/vendor directory is the exception to this rule. If you add an external JavaScript file to assets/vendor, it’ll be automatically concatenated to your priv/static/app.js bundle and executed when your page loads. That way, external dependencies are never imported and available on the global JavaScript scope, such as window.

You can configure the webpack tool in the assets/webpack.config.js file. Take a look at it on your own time. The file is short and simple, so you can easily tell what’s happening.

webpack ships with a command-line tool, and using it is straightforward. You need to know only a few commands:

 $ ​​webpack
 $ ​​webpack​​ ​​--watch
 $ ​​webpack​​ ​​--mode​​ ​​production

Since each of these commands builds your assets for a different context, let’s talk about each in turn. webpack just compiles the assets into static files and copies the results to priv/static before exiting. During development as you’re actively working on your JavaScript, you can add the -watch option. After you do so, webpack will monitor the files and automatically recompile them as they change. Use the --mode production flag to do everything you need to generally prepare your JavaScripts and style sheets for production, such as building and minifying them.

In all likelihood, you’ll never type the first couple commands directly, because Phoenix does it for you. If you open up your config/dev.exs, you see this line:

 watchers: [node: ["node_modules/webpack/bin/webpack.js",
  "--mode", "development", "--watch-stdin", "--colors",
  cd: Path.expand("../assets", __DIR__)]]

That code will automatically run webpack --watch-stdin when your Phoenix app starts in development. The --watch-stdin option makes the webpack program abort when Phoenix shuts down.

With the webpack introduction out of the way, it’s time to write some JavaScript. First, we’ll create a Player object to receive the data-player-id and embed the YouTube video. Later, we’ll use the Player object to send and receive information about the video so we’ll know exactly when an annotation is added.

Create a new file called assets/js/player.js with these contents:

 let​ Player = {
  player: ​null​,
 
  init(domId, playerId, onReady){
  window.onYouTubeIframeAPIReady = () => {
 this​.onIframeReady(domId, playerId, onReady)
  }
 let​ youtubeScriptTag = document.createElement(​"script"​)
  youtubeScriptTag.src = ​"//www.youtube.com/iframe_api"
  document.head.appendChild(youtubeScriptTag)
  },
 
  onIframeReady(domId, playerId, onReady){
 this​.player = ​new​ YT.Player(domId, {
  height: ​"360"​,
  width: ​"420"​,
  videoId: playerId,
  events: {
 "onReady"​: (event => onReady(event) ),
 "onStateChange"​: (event => ​this​.onPlayerStateChange(event) )
  }
  })
  },
 
  onPlayerStateChange(event){ },
  getCurrentTime(){ ​return​ Math.floor(​this​.player.getCurrentTime() * 1000) },
  seekTo(millsec){ ​return​ ​this​.player.seekTo(millsec / 1000) }
 }
 export​ ​default​ Player

That’s a fairly long example, so we should break it down piece by piece.

First, we will be creating a Player object that wires up YouTube’s special window.onYouTubeIframeAPIReady callback. We inject a YouTube iframe tag, which will trigger our event when the player is ready.

Next, we implement a onIframeReady function to create the player with the YouTube iframe API. We finish by adding convenience functions like getCurrentTime and seekTo, since we want to bind messages to a point in time for the video playback.

This abstraction is more than a convenient wrapper. It builds an API for video players with the most important features for our application. Our Player API will insulate us from changes in YouTube and also let us add other video players over time. Our onYouTubeReady function needs the HTML container ID to hold the iframe. We’ll pass this in from higher up in our JavaScript stack in a moment.

Chris says:
Chris says:
Why webpack?

Instead of building yet another asset-build tool, the Phoenix team decided to leverage one of the many tools available in the Node.js ecosystem. webpack is the de facto choice in the Node.js community and the Phoenix team loves its adoption, solid documentation, and minimal out-of-the-box configuration.

We know this choice might not resonate with all developers, so Phoenix allows you to use the build tool of your choice. Not a single line of code in Phoenix knows about webpack. All the configuration is in your application. You can even skip webpack altogether when creating a new app by using the --no-webpack option. If you can tell your build tool to compile your static files to priv/static, you’re good to go. You can even change your config/dev.exs file so Phoenix sets up a watcher for your favorite tool.

Our YouTube player is all set, but YouTube’s JavaScript API expects a specific video ID, and all we have is the URL.

Remember, our player.js file won’t be executed unless we import it. Let’s do this in assets/js/app.js by importing the Player and starting it with the video and player ID if one exists:

 import​ Player ​from​ ​"./player"
 let​ video = document.getElementById(​"video"​)
 
 if​(video) {
  Player.init(video.id, video.getAttribute(​"data-player-id"​), () => {
  console.log(​"player ready!"​)
  })
 }

Next, let’s tidy up our annotations box with a sprinkle of CSS. Create an assets/css/video.css file and key this in:

 .annotations {
  border-left: 1px solid #eaeaea;
 }
 
 #msg-container​ {
  min-height: 260px;
 }

Now we can import our new video.css file in assets/css/app.css so our application can use it, like this:

 /* This file is for your main application css. */
 
 @import​ ​"./phoenix.css"​;
 @import​ ​"./video.css"​;

We imported video.css after phoenix.css, a style sheet Phoenix includes for default styling.

Next, we’ll create a new video with a YouTube URL, and you’re now ready to watch it:

images/src/watching_videos/player.png

You can even start sharing the video URL with your friends with a URL that looks like /watch/13—but that’s ugly. URLs for videos should use words, not numbers. Let’s fix that.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.30.236