Elixir Phoenix React - 2018

Pobranie niezbędnych narzędzi

Spróbujemy skonfigurować React z frameworkiem Phoenix. Ktoś się pewnie będzie zastanawiał dlaczego miałby używać tego zestawu technologii zamiast przykładowo Node.js z Reactem. Elixir ma szansę stać się jednym z bardziej popularnych języków do pracy po stronie serwera w przeciągu "kilku" najbliższych lat, więc już jest to jakiś powód do jego nauki. Dodatkowo Elixir jest jednym z najpopularniejszych jezyków funkcyjnych, więc warto go troszkę poznać, by mieć obycie z językami tego typu. Poniżej znajduje się przykładowy kod na ciąg Fibonacciego w języku Elixir.


defmodule Fib do 
  def fib(0) do 0 end
  def fib(1) do 1 end
  def fib(n) do fib(n-1) + fib(n-2) end
end

IO.puts Fib.fib(10)

Możemy przetestować tę funkcję, a także pobawić się trochę z tym językiem pod następującym adresem http://elixirplayground.com?gist=12b3b0339f14fbe1de410d429aeb6219.

Przejdziemy teraz do tworzenia naszej aplikacji.

Potrzebujemy do tego:

  • Zainstalować język Elixir
  • Zainstalować framework Phoenix
  • Zainstalować manager / pakietów HEX dla języków Erlang / Elixir
  • Zainstalować manager / pakietów NPM

Linki, z których możemy pobrać wymione wyżej potrzebne nam rzeczy:

Tworzymy szkielet aplikacji

Po zainstalowaniu tych wszystkich rzeczy, przechodzimy do utworzenia początkowego szkieletu naszej aplikacji. Otwieramy konsolę w naszym katalogu roboczym i wpisujemy następującą komendę. mix phx.new myapp --no-ecto. Końcówka --no-ecto oznacza, że dostaniemy szkielet aplikacji bez wstępnej konfiguracji bazy danych, w tym tutorialu nie będziemy z żadnej korzystac. Dostaniemy pytanie czy mają zostać zainstalowane wszystkie zależności, wpisujemy Y i klikamy enter. Następnie przechodzimy do nowo utworzonego folderu myapp.

nowy folder
szkielet-folderu

Po przejściu do katalogu myapp odpalamy komendę mix phx.server a następnie przechodzimy pod adres http://localhost:4000/. Naszym oczom powinna ukazać się strona widoczna poniżej.

phoenix powitanie

Po przejściu do katalogu myapp odpalamy komendę mix phx.server a następnie przechodzimy pod adres http://localhost:4000/. Naszym oczom powinna ukazać się strona widoczna poniżej.

Spróbujmy teraz przejść pod adres http://localhost:4000/data. Powinna nam się wyświetlić informacja w stylu Cannot GET /article/elixir-phoenix-react/data. Zaraz to zmienimy. Warto także wspomnieć, że Phoenix framework jest oparty o model MVC.

Przechodzimy do folderu lib/myapp_web a następnie dokonujemy zmian w pliku router.ex


defmodule MyappWeb.Router do
  use MyappWeb, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", MyappWeb do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index
  end

 # tutaj informujemy pod jakim adresem url i na jakie żądanie ma być wywołany konkretny "kontroller"
   scope "/data", MyappWeb do
    pipe_through :browser
    get "/", DataController, :index
  end



  # Other scopes may use custom stacks.
  # scope "/api", MyappWeb do
  #   pipe_through :api
  # end
end


Następnie przechodzimy do folderu lib/myapp_web/controllers, tworzymy plik o nazwie data_controller.ex Umieszczamy w nim następujący kod.


defmodule MyappWeb.DataController do
  use MyappWeb, :controller

  def index(conn, _params) do
    render conn, "data.html"
  end
end

Po stworzeniu naszego "kontrolera" idziemy do folderu lib/myapp_web/views, gdzie stworzymy plik o nazwie data_view.ex Umieszczamy w nim następujący kod.


defmodule MyappWeb.DataView do
  use MyappWeb, :view
end


Tworzymy folder data w folderze lib/myapp_web/templates a następnie w folderze data dodajemy plik o nazwie data.html.eex z poniższym kodem.


<h1>Hello World</h1>

Przechodzimy pod adres http://localhost:4000/data, powinniśmy zobaczyć następujący widok

phoenix hello world

Dodajemy React do naszego projektu

Jeśli udało nam się dotrzeć do tego etapu to już jest coś. Nie ma może jeszcze dodanego Reacta ale przynajmniej widzimy efekty naszej pracy! By skonfigurować React z Phoenix skorzystamy z biblioteki https://github.com/geolessel/react-phoenix. Przechodzimy do pliku mix.exs i dodajemy naszą zależność react-phoenix. Następnię używamy komendy mix deps.get

.

defmodule Myapp.Mixfile do
  use Mix.Project

  def project do
    [
      app: :myapp,
      version: "0.0.1",
      elixir: "~> 1.4",
      elixirc_paths: elixirc_paths(Mix.env),
      compilers: [:phoenix, :gettext] ++ Mix.compilers,
      start_permanent: Mix.env == :prod,
      deps: deps()
    ]
  end

  # Configuration for the OTP application.
  #
  # Type `mix help compile.app` for more information.
  def application do
    [
      mod: {Myapp.Application, []},
      extra_applications: [:logger, :runtime_tools]
    ]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_),     do: ["lib"]

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [
      # dodajemy naszą zależność
      {:react_phoenix, " ~> 0.5.0"},
      {:phoenix, "~> 1.3.0"},
      {:phoenix_pubsub, "~> 1.0"},
      {:phoenix_html, "~> 2.10"},
      {:phoenix_live_reload, "~> 1.0", only: :dev},
      {:gettext, "~> 0.11"},
      {:cowboy, "~> 1.0"}
    ]
  end
end


mix deps react-phoenix

Przechodzimy do katalogu assets i szukamy pliku package.json. Dodajemy do niego nasze "javascriptowe" zależności.


{
  "repository": {},
  "license": "MIT",
  "scripts": {
    "deploy": "brunch build --production",
    "watch": "brunch watch --stdin"
  },
  "dependencies": {
    "phoenix": "file:../deps/phoenix",
    "phoenix_html": "file:../deps/phoenix_html",

    "react-phoenix": "file:../deps/react_phoenix"
  },
  "devDependencies": {
    "babel-brunch": "6.1.1",
    "brunch": "2.10.9",
    "clean-css-brunch": "2.10.0",
    "uglify-js-brunch": "2.10.0"
  }
}

Używamy komendy npm install, jeśli wyskoczy jakiś error ponawiamy naszą komendę.

Następnie upewniamy się, że mamy zainstalowany React i Babel, w tym celu używamy komendy npm install react babel-preset-env babel-preset-react --save. Musimy jeszcze tylko aktywować nasze zależności w pliku brunch-config.js


exports.config = {
  // See http://brunch.io/#documentation for docs.
  files: {
    javascripts: {
      joinTo: "js/app.js"

      // To use a separate vendor.js bundle, specify two files path
      // http://brunch.io/docs/config#-files-
      // joinTo: {
      //   "js/app.js": /^js/,
      //   "js/vendor.js": /^(?!js)/
      // }
      //
      // To change the order of concatenation of files, explicitly mention here
      // order: {
      //   before: [
      //     "vendor/js/jquery-2.1.1.js",
      //     "vendor/js/bootstrap.min.js"
      //   ]
      // }
    },
    stylesheets: {
      joinTo: "css/app.css"
    },
    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 "/assets/static". Files in this directory
    // will be copied to `paths.public`, which is "priv/static" by default.
    assets: /^(static)/
  },

  // Phoenix paths configuration
  paths: {
    // Dependencies and current project directories to watch
    watched: ["static", "css", "js", "vendor"],
    // Where to compile files to
    public: "../priv/static"
  },

  // Configure your plugins
  plugins: {
    babel: {
      presets: ["env", "react"],
      // Do not use ES6 compiler in vendor code
      ignore: [/vendor/]
    }
  },

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

  npm: {
    enabled: true
  }
};


Przechodzimy do pliku app.js znajdującego się w katalogu assets/js/ i importujemy naszą zależność


// Brunch automatically concatenates all files in your
// watched paths. Those paths can be configured at
// config.paths.watched in "brunch-config.js".
//
// However, those files will only be executed if
// explicitly imported. The only exception are files
// in vendor, which are never wrapped in imports and
// therefore are always executed.

// Import dependencies
//
// If you no longer want to use a dependency, remember
// to also remove its path from "config.paths.watched".
import "phoenix_html"
import "react-phoenix"


// Import local files
//
// Local files can be imported directly using relative
// paths "./socket" or full ones "web/static/js/socket".

// import socket from "./socket"

W folderze /assets/js/ tworzymy folder o nazwie components a w nim plik o nazwie App.jsx. Umieszczamy w nim poniższy kod.



import React from "react";


export default class App extends React.Component {

	constructor() {
		super();
	}


	render() {

		return (
			
<h1>Hello World App Component Page</h1>
) } }

Wracamy do naszego pliku app.jsx, musimy sprawić by nasz komponent był widoczny dodająć go do "global namespace", ciekawe jak to na polski przetłumaczyć.


// Brunch automatically concatenates all files in your
// watched paths. Those paths can be configured at
// config.paths.watched in "brunch-config.js".
//
// However, those files will only be executed if
// explicitly imported. The only exception are files
// in vendor, which are never wrapped in imports and
// therefore are always executed.

// Import dependencies
//
// If you no longer want to use a dependency, remember
// to also remove its path from "config.paths.watched".
import "phoenix_html"
import "react-phoenix"

import App from "./components/App"


// Import local files
//
// Local files can be imported directly using relative
// paths "./socket" or full ones "web/static/js/socket".

// import socket from "./socket"


window.Components = {
	App
}


By wyświetlić nasz komponent, przechodzimy do pliku data.html.eex będącego w katalogu /lib/myapp_web/templates/data i wywołujemy go. Na ekranie powinniśmy zobaczyć nasz komponent.


<h1>Hello World</h1>
<%= ReactPhoenix.ClientSide.react_component("Components.App") %>

react widok