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
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.
bin/console make:controller
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);
}
}
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.
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)
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.
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.