I want to mix basic HTML generated by Php and VueJS components without having to use VueJS all the way down to the lowest dom leaf.
The parent layout has the Vue app applied all the headers navs assides etc are Vue components and the main content on most pages are still plain HTML generated by PHP.
I want to upgrade small parts of the main content with Vue components but I cant use them once HTML has been used:
Php script generates the entire dom
<div id="app"> VueJS instance mounts here.
<Cats>This component works perfectly here.
<div id="main Content">More HTML content generated by PHP.
<Cats> Cats does nothing here.
<Cats> works perfectly fine in the upper DOM but after rendering some basic HTML the app will no longer populate components further down.
Php can render JS in a script tag but it's unable to use any imports.
Is there any way to get the Vue instance to treat all <Cats> tags as the Cats component regardless of where it is written on the page?
So far I have tried:
async components.
portals using mounting portals.
creating another Vue instance and mounting it to another ID.
Any ideas?
EDIT: The things I tried previously may be been blocked by the mishmash of multiple UI modernization attempts in this monolith I have inherited.
So I would not rule those out for others facing this situation.
If you are dynamically passing the template which is coming from an API or from any other source into your Vue instance. There is a way to access that via v-html attribute but before using that please have a look at note below.
Note that the contents are inserted as plain HTML - they will not be compiled as Vue templates.
Hence, As you are trying to bind/render the Component itself and it's not a plain HTML. So it will not processed by Vue's template compiler.
Possible solution is that you can get a component name and props from your PHP API instead of whole HTML template.
You can also achieve the same by using Vue compile option. Here is the demo :
Vue.component('Cats', {
props: ['msg'],
template: '<h3>{{ msg }}</h3>'
});
// Template coming from PHP
const template = `<div id="main Content"><Cats msg="Meow... Meow..."></div>`
var app = new Vue({
el: '#app',
data: {
compiled: null
},
mounted() {
setTimeout(() => {
this.compiled = Vue.compile(template);
})
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<Cats msg="Meow...">
</Cats>
<component :is="compiled"></component>
</div>
In the end, I had to manually add a JS file to the build configs containing a global window.ExtraVue with a setup function like so:
import Cats from 'files/Cats.vue';
window.ExtraVue = {
setup: function(data) {
new Vue({
el: '#extra-vue',
data: data,
components: {
Cats
},
template: '<Cats/>'
})
}
};
The setup function is then called at the very end of the PHP script.
<script>window.ExtraVue.setup(<?=json_encode($array)?>)</script>
The setup function can now create a new Vue instance for each component that needs to be out on its own.
Ideally, it would be great if VueJS could just replace matching tags with components.
Related
I develop a PHP web app. The UI is built with Twig templates.
I've started to add some new (and replace some existing) forms with ReactJS components. I like the approach and I am happy with the results.
The application uses Bootstrap 4. A couple of the ReactJS components wrap/use the Bootstrap 4 modal.
I show/hide the modals with JQuery inside the React components. I can't work out any other way of doing it.
$("#myModal").modal('show');
I know you're supposed to avoid JQuery in React, but is there another way of opening/closing the modals without JQuery? A couple of lines of JQuery per component seems like the simplest approach.
I tried to open close the modals by setting state and then applying styles, but it doesn't work because I still need to set styles on the 'parent' body outside the React component.
It's better to avoid jQuery in React Components.
For your bootstrap modals, you can convert them to ReactBootStrap components.
You can achieve the same thing with this one as well.
Moreover you can use ref={refName} on the modal and access the DOM element for the modal and use the .modal() function provided by Bootstrap to show/hide the modal.
Like below:
const modalRef = useRef(null);
const handleShowModal = () => {
$(modalRef.current).modal('show');
};
const handleHideModal = () => {
$(modalRef.current).modal('hide');
};
The Modal Div should be <div ref={modalRef} like this.
This question already has an answer here:
Why my Symfony Page specific script is not working?
(1 answer)
Closed last year.
I'm trying to use page-specific module with inline script but getting an error.
At first I have created a module and stored it in assets/js/tags.js
class TaggedImage {
constructor(image) {
this.image = image
}
...
}
export {TaggedImage}
Then I added this module in webpack.config.js to specific route:
Encore
...
.addEntry('photo_page', './assets/js/tags.js')
Now I'm trying to use this module with inline script in twig template:
<script>
const myImage = new TaggedImage(document.getElementById('photoImage'));
const tags = [
{x:0.5, y:0.5, width:0.15, height:0.22},
{x:0.65, y:0.33, width:0.13, height:0.19},
{x:0.1222, y:0.398, width:0.10, height:0.335}
];
myImage.addTags(tags).render();
</script>
Then I ran npm run dev in the terminal and it finished without errors.
...and after refreshing the page I get an error in the console:
Uncaught ReferenceError: TaggedImage is not defined
I noticed that my code is added to /public/build/photo_page.js but I don't see any mention of this file in page source.
There are two things to your problem.
The first one is that besides defining the entry in webpack.config, you have to explicitly import your javascript in your html.
You can do that with the twig helper function encore_entry_script_tags as described in the page specific scripts section of the manual.
So you would add:
{{ encore_entry_script_tags('photo_page') }}
<script>
// Here goes your script
</script>
Or override your block, if you have one, and call {{ parent() }} to include any additional scripts you require in all pages.
Note that the entry tag will add the defer attribute by default, as configured in config/packages/webpack_encore.yaml, so you'll need to take that into account and wrap your script to fire on the load event.
The second one is that webpack doesn't expose modules to the global namespace. For that you would have to do it explicitly in your module:
// Instead of exporting the module, you export it to the global scope
// (the browser window in this case)
global.TaggedImage = TaggedImage;
The CodeIgniter application was developed much earlier without plans to integrate ReactJS at that time. A later requirement was added to integrate another ReactJS project with this backend and replace the current frontend (views).
The CodeIgniter application is not done as a RESTful API. The .php view files could not be replaced with .js files of the reactjs app as the server is Apache.
Running a nodejs server would not render the CodeIgniter views.
Bootstrap, jquery, and simple javascript can be included within the view of the CodeIgniter application. But is it possible to replace the PHP view files in CodeIgniter with javascript files?
The PHP view files do not need to be replaced with js files. JavaScript can easily be added to PHP files using <script> tags. Below is the Add React in One Minute demo in a CodeIgniter app.
To integrate the React demo into CodeIgniter start with a simple controller - React.php
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class React extends CI_Controller
{
public function index()
{
$this->load->view('react_view');
}
}
The "view" file is straight from the React demo but it's put in a .php file instead of .html.
The only change made to the demo code is in the script tag at the bottom of the page. My assets folder is on the same level as CodeIgniter's /application folder. There are subfolders in assets for css, js, and images.
/public_html
/application
/system
/assets
/js
/css
/img
So I've changed the src for the tag that loads like_button.js to work with my file layout.
The "view" file react_view.php
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Add React in One Minute</title>
</head>
<body>
<h2>Add React in One Minute</h2>
<p>This page demonstrates using React with no build tooling.</p>
<p>React is loaded as a script tag.</p>
<p>
This is the first comment.
<!-- We will put our React component inside this div. -->
<div class="like_button_container" data-commentid="1"></div>
</p>
<p>
This is the second comment.
<!-- We will put our React component inside this div. -->
<div class="like_button_container" data-commentid="2"></div>
</p>
<p>
This is the third comment.
<!-- We will put our React component inside this div. -->
<div class="like_button_container" data-commentid="3"></div>
</p>
<!-- Load React. -->
<!-- Note: when deploying, replace "development.js" with "production.min.js". -->
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
<!-- Load our React component. -->
<script src="assets/js/like_button.js"></script>
</body>
</html>
/assets/js/like_button.js
'use strict';
const e = React.createElement;
class LikeButton extends React.Component {
constructor(props) {
super(props);
this.state = { liked: false };
}
render() {
if (this.state.liked) {
return 'You liked comment number ' + this.props.commentID;
}
return e(
'button',
{ onClick: () => this.setState({ liked: true }) },
'Like'
);
}
}
// Find all DOM containers, and render Like buttons into them.
document.querySelectorAll('.like_button_container')
.forEach(domContainer => {
// Read the comment ID from a data-* attribute.
const commentID = parseInt(domContainer.dataset.commentid, 10);
ReactDOM.render(
e(LikeButton, { commentID: commentID }),
domContainer
);
});
Here's a boilerplate that you can use.
https://github.com/cyruzin/codeigniter-react-boilerplate
React is used for single page applications. Codeigniter is a PHP framework. Nobody forbids you to include react apps inside codeigniter. The react app polls data from API provided by codeigniter. You can use multiple codeigniter controllers or views to define different react states, or you can just use one controller, one view and multiple models and define states with react alone.
React is not in opposition to codeigniter. Codeigniter doesn't care if you use jquery, angular or react. From the view on it's not a codeigniter's business.
I am maintaining Zend application with ExtJS 3 based views. Recently, I am working on migrating to ExtJS 4 MVC based application.
Background
With ExtJS 3 I used to combine ExtJS library + all my JS files and include that in Zend layout (let's call that combine.js).
<script type="text/javascript" src="combine.js"></script>
All I needed to do was to met dependencies in combined file.
Now, trying to accomplish ExtJS 4 MVC approach with Zend generating my views, I am running into multiple problems with the new Ext.Loader. My Zend layout consists of
<script type="text/javascript">
Ext.application({
name: 'demo',
appFolder: 'app',
controllers: [
// controllers list here
],
launch: function()
{
// viewport + page items
}
});
</script>
Moreover, in HEAD section I still maintain the same approach to include one combined file (ExtJS 4 library + all my js files). In theory, I have all js dependencies met since I include the whole library before my code.
Problem
I am running into multiple problems with new class loading mechanism.
Let's assume I have only one controller with one view and one store on ExtJS side:
Ext.define('demo.myModule.controller.MyGrid', function() {
extend: 'Ext.app.Controller',
stores: [ 'demo.myModule.store.MyStore' ],
views: [ 'demo.myModule.view.MyStore' ]
...
});
Rendering such page ends up with Ext.Loader trying to fetch not existing resource :
GET http://myPage/demo/myModule/store/MyStore.js?_dc=1390226857962 404 (Not Found)
Disabling Ext.Loader ends up with :
Uncaught Error: Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. Missing required class: demo.myModule.controller.MyGrid
Since ExtJS 4 is a really popular library, someone must have hit the same stumbling block before. What is the proper way to tackle such problem ?
BTW. I am asking that question having already spent couple of days on ExtJS documentation + official tutorials, tried Sencha CMD but I fail to get what the exact problem is.
EDIT:
I have added a sample project showing the problem: https://github.com/mirfilip/extjs-zend. You can see app.js containing simple class demo.myModule.view.Panel. In application/layouts/scripts/layout.phtml you can see that the whole library + app.js is loaded (in order). When you try to render the project it will show the blank page. However, if you go to layout and uncomment lines 23-33 (and comment out appending of app.js in line 11) and render, it works well. The only difference is where my class is defined in. There should be no possible time race between classes as Ext.application creation is wrapped it Ext.onReady. Any ideas ?
I don't use Zend Studio, so I cannot give a specific answer. I will just show you my code, how I integrate ExtJs into my own homebrewed framework.
This is the html code on my development site, which will use Ext.Loader to load the classes dynamically.
<!-- <x-compile> -->
<!-- <x-bootstrap> -->
<link rel="stylesheet" href="bootstrap.css">
<script src="ext/ext-dev.js"></script>
<script src="bootstrap.js"></script>
<!-- </x-bootstrap> -->
<script src="ext/locale/ext-lang-fr.js"></script>
<script src="app.js"></script>
<!-- </x-compile> -->
This will then compile to :
<link rel="stylesheet" href="resources/Mb-all.css"/>
<script type="text/javascript" src="app.js"></script>
This snipped is found in the build/production/MyApp/index.php folder. I have then a build script that takes this file and copies it where it should be for the php framework to work.
I hope this will give you an idea to solve your specific problem.
Important: app.js is a autogenerated file by sencha cmd. It is not the same thing in the development environment and in the test environment.
In the development it just contains this text:
/*
This file is generated and updated by Sencha Cmd. You can edit this file as
needed for your application, but these edits will have to be merged by
Sencha Cmd when upgrading.
*/
Ext.application({
name: 'Mb',
extend: 'Mb.Application',
autoCreateViewport: true
});
In the production envrionment it contains the full, minified javascript source of the app.
Probably you have to indicate paths to Ext classes before your combined app js file. I'm using paths relative to domain root ('/app' not 'app').
<script>
Ext.Loader.setConfig({
paths : {
'demo' : '/app'
},
enabled: true
});
</script>
<script src="combine.js" />
Currently working with ZF and ZendX_JQuery.
As I understand, script links to Google's CDN JQuery are only included in the <head> section of a view if ZF JQuery is referenced in some way in the MVC.
However, I'm working with 'Cloud Zoom', a JQuery library for image zooming.
The view I wish to include Cloud Zoom in has no reference to JQuery, and therefore the script links are not included in the head. How can I make ZF include the script links in the head section of the page without explicitly including any ZF JQuery references in the MVC?
from http://framework.zend.com/manual/en/zendx.jquery.view.html
To access the javascript we have to
utilize the jQuery() functionality.
Both helpers already activated their
dependencies that is they have called
jQuery()->enable() and
jQuery()->uiEnable()
i have this code in my layout
if ($this->jQuery()->isEnabled()) {
$this->headScript()->appendFile('/scripts/jquery.ui.datepicker-it.js');//localizazione di DatePicker
echo $this->jQuery()->setRenderMode(ZendX_JQuery::RENDER_JAVASCRIPT | ZendX_JQuery::RENDER_JQUERY_ON_LOAD);
}
so i guess you have to do something like $this->jQuery()->enable()->enableui()