Vue.js Running On Symfony4 and Creating Reusable Components
Tags: vue, php, javascript, webdev
Published: 2018-08-30 10:58:40

---
title: Vue.js Running On Symfony4 and Creating Reusable Components
published: true
description: We will learn to run Vue.js with Symfony 4 and we will create reusable components.
tags: #vue #php #javascript #webdev
---

As i tell in my last 2 posts, i am developing a vocabulary web application. We have seen running [Symfony4 on Docker Compose](https://dev.to/_mertsimsek/symfony-4-running-on-docker-compose-2fb3). 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.

```bash
composer require webpack-encore
yarn install
```

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

```javascript
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**.

```javascript
{
  "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.
```bash
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.

```javascript
'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.

```javascript
'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**.

```javascript
<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.

```javascript
<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.

```bash
bin/console make:controller
```

After creating of controller, my controller is like this.

```php
<?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.
```bash
yarn encore dev --watch
```

This command will create 3 kind of files.

<ul>
    <li><b>public/build/dashboard.css</b></li>
    <li><b>public/build/dashboard.js</b></li>
    <li><b>public/build/manifest.json</b></li>
</ul>

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

```php
<?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.

```html
{% 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
<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.

```bash
yarn run encore dev --watch
```

Well, we have obtained this view.

![alt text](https://thepracticaldev.s3.amazonaws.com/i/lj9k515smn9itaga6pew.png "")

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/