DEV Community

Mert Simsek
Mert Simsek

Posted on

Vue.js Running On Symfony4 and Creating Reusable Components

As i tell in my last 2 posts, i am developing a vocabulary web application. We have seen running Symfony4 on Docker Compose. In this post, we are going to add Vue.js into Symfony4. For this, we are going to install Webpack Encore and dependencies of Javascript. I have shared my informations of sources by links in the past.

Firstly, in order run these commands to install Webpack Encore. Yarn as well as Composer should be installed your device.

composer require webpack-encore
yarn install
Enter fullscreen mode Exit fullscreen mode

It will create webpack.config.js file and i have edited like this.

var Encore = require('@symfony/webpack-encore');

Encore
    // directory where compiled assets will be stored
    .setOutputPath('public/build/')

    // the public path used by the web server to access the previous directory
    .setPublicPath('/build')
    // only needed for CDN's or sub-directory deploy
    //.setManifestKeyPrefix('build/')

    /*
     * ENTRY CONFIG
     *
     * Add 1 entry for each "page" of your app
     * (including one that's included on every page - e.g. "app")
     *
     * Each entry will result in one JavaScript file (e.g. app.js)
     * and one CSS file (e.g. app.css) if you JavaScript imports CSS.
     */
    // will create public/build/app.js and public/build/app.css
    .addEntry('dashboard', './assets/js/dashboard.js')

    // allow legacy applications to use $/jQuery as a global variable
    .autoProvidejQuery()

    // enable source maps during development
    .enableSourceMaps(!Encore.isProduction())

    // empty the outputPath dir before each build
    .cleanupOutputBeforeBuild()

    // show OS notifications when builds finish/fail
    .enableBuildNotifications()

    .enableVueLoader()

    .enableSassLoader()
    .enableLessLoader()


module.exports = Encore.getWebpackConfig();
Enter fullscreen mode Exit fullscreen mode

Then this configuration, we should install dependencies of Javascript. I am showing my package.json.

{
  "devDependencies": {
    "@symfony/webpack-encore": "^0.19.0",
    "less": "^3.8.1",
    "less-loader": "^4.1.0",
    "node-sass": "^4.9.3",
    "sass-loader": "^7.1.0",
    "vue": "^2.5.17",
    "vue-loader": "^14",
    "vue-template-compiler": "^2.5.17",
    "webpack-notifier": "^1.6.0"
  },
  "license": "UNLICENSED",
  "private": true,
  "scripts": {
    "dev-server": "encore dev-server",
    "dev": "encore dev",
    "watch": "encore dev --watch",
    "build": "encore production"
  },
  "dependencies": {
    "axios": "^0.18.0",
    "bootstrap": "^4.1.3",
    "font-awesome": "^4.7.0",
    "graceful-fs": "^4.1.11",
    "izitoast": "^1.4.0",
    "jquery": "2.2.4",
    "jquery-easing": "^0.0.1",
    "popper.js": "^1.14.4",
    "select2": "^4.0.6-rc.1",
    "vue-router": "^3.0.1"
  }
}
Enter fullscreen mode Exit fullscreen mode

Thus, you just run this command.

yarn install
Enter fullscreen mode Exit fullscreen mode

Right now, we had Bootstrap 4, Vue.js 2, Vue-cli and a lot of packages that what we need.

From now on, we can start to create structure of Vue.js. I am creating assets/js/layout.js file. These lines mean that file.

'use strict';

const $ = require('jquery');
import 'bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap/dist/js/bootstrap.min.js';
import 'bootstrap/dist/js/bootstrap.bundle.min.js'
import 'jquery-easing';
import './sb-admin.min.js';
import '../css/sb-admin.min.css'
Enter fullscreen mode Exit fullscreen mode

So, these lines are my needing libraries everywhere. Then, i will create assets/js/dashboard.js file. Like this.

'use strict';

require('./layout');

import Vue from 'vue';

import Setting from '../components/user/Setting';
import Select2 from '../components/form_items/Select2';

new Vue({
    el: '#app-dashboard',
    components: {Select2,Setting}
}).$mount('#app-dashboard');
Enter fullscreen mode Exit fullscreen mode

This file will initialize our Vue.js project. Now, we will create our components. I need a Select2 component that i can use it everywhere. I had written a Select component into assets/components/form_items/Select2.vue.

<template>
    <select class="select2 col-3">
        <option v-for="(value, key) in option">{{ value + " (" + key + ")" }}</option>
    </select>
</template>

<script>

    import 'select2';
    import 'select2/dist/css/select2.css';

    export default {
        name: "select2",
        props: {
            option: Object
        },
        data() {
            return {}
        },
        mounted: function () {
            $('.select2').select2();
        },
        methods: {},
        created: function () {
        }
    }
</script>

<style scoped>

</style>
Enter fullscreen mode Exit fullscreen mode

With this way, we can pass option value from out. In Setting.vue file will send a XHR request with Axios library. Then, it is sending these values to Select2 component. First, i am showing Setting component.

<template>

    <form>
        <div class="form-group">
            <label>Target language: </label>
            <select2 :option="optns"></select2>
        </div>
        <button type="submit" class="btn btn-primary">Submit</button>
    </form>
       
</template>

<script>
    import Select2 from '../form_items/Select2';
    import axios from 'axios';

    export default {
        components: {Select2},
        name: "setting",
        data() {
            return {
                optns: {}
            }
        },
        created() {
            axios.get('/google/translate/api')
                .then(response => {
                    // JSON responses are automatically parsed.
                    this.optns = response.data.languages
                })
                .catch(e => {
                    this.errors.push(e)
                });
        },
        methods: {}
    }
</script>

<style scoped>

</style>
Enter fullscreen mode Exit fullscreen mode

Well, from now on, we can create a Symfony controller in order that we will get the languages.

bin/console make:controller
Enter fullscreen mode Exit fullscreen mode

After creating of controller, my controller is like this.

<?php

namespace App\Controller;

use Google\Cloud\Translate\TranslateClient;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;

/**
 * Class GoogleTranslateApiController
 * @package App\Controller
 */
class GoogleTranslateApiController extends AbstractController
{
    /**
     * @Route("/google/translate/api", name="google_translate_api")
     */
    public function index()
    {
        $translate = new TranslateClient();
        $targetLanguage = 'en';
        $result = $translate->localizedLanguages([
            'target' => $targetLanguage,
        ]);

        $languages = array();
        foreach ($result as $lang) {
            $languages[$lang['code']] = $lang['name'];
        }

        return new JsonResponse(
            array(
                'status' => true,
                'languages' => $languages
            ), 200);
    }
}
Enter fullscreen mode Exit fullscreen mode

Further all of these, we are using this command that to build by Webpack.

yarn encore dev --watch
Enter fullscreen mode Exit fullscreen mode

This command will create 3 kind of files.

  • public/build/dashboard.css
  • public/build/dashboard.js
  • public/build/manifest.json

Right, we can load our Twig files. My user controller is like this.

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;

/**
 * Class UserController
 * @package App\Controller
 *
 * @Route("/user")
 */
class UserController extends AbstractController
{
    /**
     * @Route("/setting", name="setting")
     */
    public function index()
    {
        return $this->render('user/setting.html.twig');
    }
}
Enter fullscreen mode Exit fullscreen mode

It just load Twig file and my twig file means that.

{% extends 'base.html.twig' %}

{% block head %}
{% endblock %}

{% block body %}
    <div id="app-dashboard">
        <setting></setting>
    </div>
{% endblock %}

{% block javascripts %}
    <script src="{{ asset('build/dashboard.js') }}" type="text/javascript"></script>
{% endblock %}
Enter fullscreen mode Exit fullscreen mode

As you see, we are adding builded dashbard.js. And my base.html.twig means that.

<html lang="en">

<head>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="">

    <title>SB Admin - Dashboard</title>

    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css"
          integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">
    <!-- Bootstrap core CSS-->
    <link href="{{ asset('build/dashboard.css') }}" rel="stylesheet">

    {% block head %}{% endblock %}

</head>
<body>
<nav class="navbar navbar-expand navbar-dark bg-dark static-top">

    <a class="navbar-brand mr-1" href="index.html">Start Bootstrap</a>

    <button class="btn btn-link btn-sm text-white order-1 order-sm-0" id="sidebarToggle" href="#">
        <i class="fas fa-bars"></i>
    </button>


    <!-- Navbar -->
    <ul class="navbar-nav ml-auto ml-md-0">
        <li class="nav-item dropdown no-arrow">
        </li>
    </ul>

</nav>

<div id="wrapper">

    <!-- Sidebar -->
    <ul class="sidebar navbar-nav">
        <li class="nav-item active">
            <a class="nav-link" href="index.html">
                <i class="fas fa-fw fa-tachometer-alt"></i>
                <span>Dashboard</span>
            </a>
        </li>
        <li class="nav-item dropdown">
            <a class="nav-link dropdown-toggle" href="#" id="pagesDropdown" role="button" data-toggle="dropdown"
               aria-haspopup="true" aria-expanded="false">
                <i class="fas fa-fw fa-folder"></i>
                <span>Pages</span>
            </a>
            <div class="dropdown-menu" aria-labelledby="pagesDropdown">
                <h6 class="dropdown-header">Login Screens:</h6>
                <a class="dropdown-item" href="login.html">Login</a>
                <a class="dropdown-item" href="register.html">Register</a>
            </div>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="charts.html">
                <i class="fas fa-fw fa-chart-area"></i>
                <span>Charts</span></a>
        </li>
    </ul>

    <div id="content-wrapper">

        <div class="container-fluid">
            {% block body %}{% endblock %}
        </div>

        <!-- Sticky Footer -->
        <footer class="sticky-footer">
            <div class="container my-auto">
                <div class="copyright text-center my-auto">
                    <span>Copyright © https://mertblog.net 2018</span>
                </div>
            </div>
        </footer>

    </div>
    <!-- /.content-wrapper -->

</div>
{% block javascripts %}{% endblock %}
Enter fullscreen mode Exit fullscreen mode

Right now, we are running this comand again.

yarn run encore dev --watch
Enter fullscreen mode Exit fullscreen mode

Well, we have obtained this view.

alt text

As you see, from now on, we have had a Select 2 component that we can use it everywhere. We are passing values from root component. Here, our root component is Setting.Vue As well as, we can create that what we need any components with this way.

Sources:

https://symfony.com/doc/current/frontend/encore/simple-example.html
https://knpuniversity.com/screencast/webpack-encore
https://vuejs.org/v2/guide/

Top comments (2)

Collapse
 
azazqadir profile image
Muhammad Azaz Qadir

Symfony webpack is really great for integrating front-end frameworks with your app. I also used it to integrate my Symfony app with React.js.

Collapse
 
yellow1912 profile image
yellow1912

You don't use the Form Component of Symfony in your example. I think things will get much more difficult with the Form Component, because Vue expects you to pre-define your data structure, yet the purpose of the Form Component is to dynamically configure your data structure.