Load Vue 3 Component inside mounted div and pass PHP data - symfony

I'm currently working on a new project where I have to integrate Vue 3 in a large Symfony/Drupal project.
The project already contains a lot of PHP code and actually I don't want to refactor too much to begin with.
Well I tried setting up a very small piece of Vue code to see how I could start working on the rest of the code. Actually I just want some PHP code to be transferred from index.html.twig to the sidebar.vue file. I also work with Webpack Encore by the way from Symfony. I read that I could use Vue components to achieve this but my components are not loaded inside my <div id="app"></div>. Or atleast not how I want them to load.
webpack.config.js (Webpack Encore)
var Encore = require('#symfony/webpack-encore');
if (!Encore.isRuntimeEnvironmentConfigured()) {
Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
}
Encore
.setOutputPath('webroot/public/build/')
.setPublicPath('/public/build')
.addEntry('main', './vue/src/main.js')
.splitEntryChunks()
.enableSingleRuntimeChunk()
.cleanupOutputBeforeBuild()
.enableBuildNotifications()
.enableSourceMaps(!Encore.isProduction())
.enableVersioning(Encore.isProduction())
.configureBabelPresetEnv((config) => {
config.useBuiltIns = 'usage';
config.corejs = 3;
})
.enableSassLoader()
.enablePostCssLoader()
// enables Vue
.enableVueLoader(() => {}, {
version: 3,
runtimeCompilerBuild: false,
});
;
module.exports = Encore.getWebpackConfig();
main.js
import { createApp } from 'vue';
import Sidebar from './components/sidebar';
const app = createApp({})
app.component('sidebar', Sidebar);
app.mount("#app");
sidebar.vue
<template>
<h1>Sidebar</h1>
</template>
<script>
export default {
name: 'Sidebar',
};
</script>
<style lang="scss" module>
</style>
index.html.twig
<div id="app"> <!-- The vue #app is loaded -->
<sidebar></sidebar> <!-- This is not loading -->
</div>
<!-- If it's loading I want to setup something like this -->
<div id="app"> <!-- The vue #app is loaded -->
<sidebar :item="{{ $item }}"></sidebar> <!-- This is not loading -->
</div>
{{ encore_entry_script_tags('main') }}
So how can I make <sidebar></sidebar> to load inside the HTML/Twig file? In the next step I would like to pass some PHP data on the <sidebar> component so I can read it inside the sidebar.vue file. Something like: <sidebar :item="{{ $item }}"></sidebar>
I'm not entirely sure if this is possible with my current setup but I would love to see it work like this or in a similar way.

It seems like I'll have to use the runtimeCompilerBuild. That solves the problem. When false Vue can only be used with single file components which performs better but is less suitable for my application at the moment.
.enableVueLoader(() => {}, {
version: 3,
runtimeCompilerBuild: true,
});
Instead of
runtimeCompilerBuild: false;

In your main.js do :
app.mount("sidebar");
instead of
app.mount("#app");

Related

Vue3 dynamic template

I am trying to create a dynamic Vue3 template but template is not rendering.
import App from "./App.vue";
const app = createApp(App)
app.component("DynamicData", {
template: "#dynamic-data",
data: function () {
return {}
}
});
app.mount("#app");
And laravel blade file.
<div id="app"></div>
<script type="vue-template" id="dynamic-data">
<div>My Custom Template</div>
</script>
and this is how i am calling the template.
I am using <component :is=""> because these will be dynamic component.
<component :is="DynamicData"></component>

Vue <script setup>, unable to use defineProps and defineEmits without importing it

As per the official documentation,
defineProps and defineEmits are compiler macros only
usable inside <script setup>. They do not need to be
imported and are compiled away when <script setup> is
processed.
The problem definition
I'm not able to use defineProps and defineEmits in <script setup> without importing it. Please refer to the error screenshot attached below.
The vue code which I'm executing
<!-- HelloWorld.vue -->
<template>
<h1>{{ props.message }}</h1>
</template>
<script setup>
// import { defineProps } from 'vue';
const props = defineProps({
message: {
type: String,
required: true,
}
});
</script>
The environment details for reference:
vue
^3.2.6 (3.2.19)
vue-cli
#vue/cli 5.0.0-beta.4
node:
v14.16.1
npm
6.14.12
We can resolve this issue with one of the below solutions.
Create Vue project with Vite. Follow this link for more information.
yarn create vite <project-name> --template vue
Add below rules in your eslint configuration file. Follow this link for more information.
// .eslintrc.js
module.exports = {
extends: ['plugin:vue/base'],
rules: {
'vue/script-setup-uses-vars': 'error',
}
}

VueJS 2 + ASP.NET MVC 5

I'm very new with VueJS.
I have to build a single page application inside a ASP.NET MVC5.
I follow this tutorial and works very well -> TUTORIAL
But when i create a .vue page to test VueJS2 Routes, the browser does not understand "Import", i read that i have to use a transpiler like Babel, someone know how i solve it?
App.VUE
<template>
<div id="app">
{{msg}}
</div>
</template>
<script>
export default {
name: 'app',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>
App.JS
import Vue from 'vue'
import App from './App.vue'
new Vue({
el: '#app',
router,
render: h => h(App),
data: {
message: 'Hello Vue! in About Page'
}
});
_Layout.cshtml
<div class="container-fluid">
#RenderBody()
<div id="app">
{ { message } }
</div>
</div>
<script src="~/Scripts/essential/jquery-3.1.1.min.js"></script>
<script src="~/Scripts/essential/bootstrap.min.js"></script>
<script src="~/Scripts/essential/inspinia.js"></script>
<script src="~/Scripts/essential/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/2.0.1/vue-router.js"></script>
<script src="~/Scripts/app.js"></script>
<script src="~/Scripts/plugin/metisMenu/jquery.metisMenu.js"></script>
<script src="~/Scripts/plugin/pace/pace.min.js"></script>
<script src="~/Scripts/plugin/slimscroll/jquery.slimscroll.min.js"></script>
Thanks a lot!!
Welcome to Vue.js development! Yes, you are correct, you need something to translate the import statements into JavaScript that the browsers can handle. The most popular tools are webpack and browserify.
You are also using a .vue file, which needs to be converted (with vue-loader) before the browser can pick it up. I am going to lay out how to do this, and set up webpack, which involves a few steps. First, the HTML we're working with looks something like this:
<html>
<head>
</head>
<body>
<div class="container-fluid">
<div id="app">
{ { message } }
</div>
</div>
<script src="./dist.js"></script>
</body>
</html>
Our goal is to use webpack to bundle / compile App.vue and app.js into dist.js. Here is a webpack.config.js file that can help us do that:
module.exports = {
entry: './app.js',
output: {
filename: 'dist.js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
}
}
This configuration says, "start in app.js, replace import statements as we come across them, and bundle it into a dist.js file. When webpack sees a .vue file, use the vue-loader module to add it to dist.js."
Now we need to install the tools / libraries that can make this happen. I recommend using npm, which comes with Node.js. Once you have npm installed, you can put this package.json file in your directory:
{
"name": "getting-started",
"version": "1.0.0",
"scripts": {
"build": "webpack"
},
"dependencies": {
"css-loader": "^0.28.7",
"vue": "^2.4.2",
"vue-loader": "^13.0.4",
"vue-resource": "^1.3.3",
"vue-router": "^2.7.0",
"vue-template-compiler": "^2.4.2",
"webpack": "^3.5.5"
}
}
And do the following:
Run npm install to get all of the packages.
Run npm run-script build to generate your dist.js file via webpack.
Note in the example for this question, router is undefined in app.js, but here is a fixed-up file:
import Vue from 'vue'
import VueRouter from 'vue-router'
import App from './App.vue'
var router = new VueRouter();
new Vue({
el: '#app',
router,
render: h => h(App),
data: {
message: 'Hello Vue! in About Page'
}
});
That should be it! Let me know if you have any questions.

Vue component not showing

I followed the series codetube "youtube clone" and I did everything like the Alex but the Vue component not working. I am not working on localhost but on server. I would be very glad for any suggestions.
My app.js
require('./bootstrap');
Vue.component('videoup', require('./components/VideoUpload.vue'));
const app = new Vue({
el: '#app'
});
My VideoUpload.vue file:
<template>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Upload</div>
<div class="panel-body">
...
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
}
}
</script>
My blade file:
#extends('layouts.app')
#section('content')
<videoup></videoup>
#endsection
My app.blade file:
<!DOCTYPE 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">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Styles -->
<!-- <link href="/css/app.css" rel="stylesheet">-->
<link rel="stylesheet" href="/css/app.css">
<!-- Scripts -->
<script>
window.Laravel = <?php echo json_encode([
'csrfToken' => csrf_token(),
]); ?>
</script>
</head>
<body>
<div id="app">
#include('layouts.partials._navigation')
#yield('content')
</div>
<script src="/js/app.js"></script>
</body>
</html>
My gulfpile.js:
const elixir = require('laravel-elixir');
require('laravel-elixir-vue-2');
require('laravel-elixir-webpack-official');
elixir((mix) => {
mix.sass('app.scss')
.webpack('app.js');
});
My webpack.config.js:
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
// vue-loader options go here
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.common.js'
}
},
devServer: {
historyApiFallback: true,
noInfo: true
},
devtool: '#eval-source-map'
};
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map',
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
};
It's difficult to debug your setup because I have no idea what tutorial you followed, how you are bundling your code (webpack or browserify) or what build tools you are using (gulp, elixir etc), but I think the most important thing is to understand how Vue works, and then you will be able to better understand how to solve this yourself.
Firstly, vue has two builds - a standalone build and a runtime-only build. The difference between these is that the standalone build includes a template compiler and the runtime-only build does not.
Render Functions
Vue compiles templates in to render functions to work (which are just javascript functions), it doesn't use HTML at all, so if you haven't written a render function or you haven't pre-compiled your components (using .vue files and a bundler like browserify or webpack) then you must use the standalone build; and this includes the base component itself, so the important thing to know is:
If you are trying to use a component inside anything other than a .vue file you need to use the standalone build.
because you need the compiler to turn the HTML into a render function.
So, looking at your code, you are trying to use your component inside a .blade.php file, which isn't a single file component so you will need the standalone build in your project.
When using npm, vue imports the runtime-only build by default:
// ES6
import `Vue` from `vue` // this imports the runtime-only build
// ES5
var Vue = require('vue'); // this requires the runtime-only build
But you need to make sure you are using the standalone build, and how you do this depends on whether you use webpack or browserify. If you are using webpack, you need to add the following to your webpack config:
resolve: {
alias: {
'vue$': 'vue/dist/vue.common.js'
}
}
If you are using browserify you will need to add the following to your package.json:
"browser": {
"vue": "vue/dist/vue.common"
},
And also make sure that resources/assets/views/layouts/app.blade.php wraps everything in a div with the id app:
...
<body>
<div id="app">
...
</div>
</body>
...
Update
Based on your webpack config it looks like your issue is here:
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
This says you are compiling main.js in the src folder and outputting it to the dist folder as build.js.
Laravel uses a different structure, so you will need to change this to:
entry: './resources/assets/js/app.js',
output: {
path: path.resolve(__dirname, './public/js'),
publicPath: '/public/',
filename: 'app.js'
},
This now says, compile resources/assets/js/app.js and output the file to public/js/app.js. I don't use webpack myself so that may need a little tweaking, but that should get your project up and running.
I had the same issue. the following solved it for me..
I changed:
require('./components/mycomponent.vue')
to:
import MyComponent from "./components/mycomponent.vue";

Basic angularjs code not working properly

Have installed the angularjs and Twitter.Bootstrap packages succesfully
This is my index.html:
<!DOCTYPE html>
<html ng-app="TodoApp" xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="Scripts/jquery-1.9.1.js"></script>
<script src="Scripts/bootstrap.js"></script>
<script src="Scripts/angular.js"></script>
<script src="Scripts/angular-resource.js"></script>
<script src="Scripts/app.js"></script>
<link rel="stylesheet" type="text/css" href="Content/bootstrap.css" />
<title>Amazing Todo</title>
</head>
<body>
<div class="container">
<div ng-view></div>
</div>
</body>
</html>
This is my app.js:
var TodoApp = angular.module("TodoApp", []).
config(function ($routeProvider) {
$routeProvider.
when('/', { controller: ListCtrl, templateUrl: 'list.html' }).
otherwise({ redirectTo: '/' });
});
var ListCtrl = function ($scope, $location) {
$scope.test = "testing";
};
And, this is my list.html:
<h1>Test: {{test}}</h1>
This should work fine. However the index.html is not showing the content of list.html. I think the angularjs part is not working properly.
No idea about what am i doing wrong?
Once you have defined a module, you need to define your controllers for that module and not independently.
Thus, your controller should be rewritten as:
TodoApp.controller('ListCtrl', [ '$scope', '$location',
function ($scope, $location) {
$scope.test = "Testing";
}
]);
This should show the view in question.
I would say, that if you check errors in console (in Chrome or IE press F12) you should see:
...Failed to instantiate module TodoApp due to:
Error: [$injector:unpr] Unknown provider: $routeProvider...
The reason for this expectation is that we ask IoC to inject $routeProvider while not correctly listing dependent modules. This is the above code:
var TodoApp = angular
// here we say: we do not need any other module
.module("TodoApp", [])
// here we ask: inject $routeProvider from other module
.config(function ($routeProvider)
So to make it runing we have to include the module 'ngRoute'
var TodoApp = angular
// here we say: we need these modules to make our module working properly
.module("TodoApp", [
'ngRoute'
])
// now we can ask for the provider,
// using minification-safe syntax
.config(
[ '$routeProvider',
function ($routeProvider) {
$routeProvider.
...
}]);
And also do not forget to also reference this module scripts:
<script src="Scripts/angular.js"></script>
<script src="Scripts/angular-resource.js"></script>
<!-- here we have to load this module -->
<script src="Scripts/angular-route.js"></script>
What is your directory structure can you check if list.html is in the same directory as index.html, if not specify a relative path from the application root?
Since no one has posted a full correct answer to this question and it hasn't been closed yet, here is another answer.
This is your function:
var ListCtrl = function ($scope, $location) {
$scope.test = "testing";
};
This is a bare function, which isn't of much use. You need a controller so that Angular knows what to do with {{ test }}:
<div ng-controller="someController">
<h1>{{ test }}</h1>
</div>
If you insist on keeping the function as a separate variable, you could do so and still have a controller:
var ListCtrl = function ($scope, $location) {
$scope.test = "testing";
};
TodoApp.controller('someController', ListCtrl);
This also works.
Despite of this, your UI won't show, as there's an error in it:
var TodoApp = angular.module("TodoApp", [])
You're using $routeProvider and .when(),.otherwise(), for which you need ngRoute as a dependency:
var TodoApp = angular.module("TodoApp", ['ngRoute'])
Your app should work after that.

Resources