Vue.js Running On Symfony4 and Creating Reusable Components

By | 30 August 2018

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 EncoreYarn as well as Composer should be installed your device.

composer require webpack-encore
yarn install

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();

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"
  }
}

Thus, you just run this command.

yarn install

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'

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');

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>

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>

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

<?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);
    }
}

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

yarn encore dev --watch

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');
    }
}

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 %}

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 %}

Right now, we are running this comand again.

yarn run encore dev --watch

Well, we have obtained this view.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

*