I'm just learning React, and while I understand many of the basics there is one concept I haven't seen anyone cover: how do I take information loaded via the server-side language (e.g. PHP) and use it when loading up the React view?
I would have expected that I'd just have the RenderDom call in my php view, such as:
// In the pre-compiled JSX file
var Greeting = React.createClass({
render: function() {
<div>
<h1>Hello, { this.props.username }</h1>
</div>
}
});
// In my PHP view
<script type="text/jsx">
ReactDOM.render( <Greeting username="<?php echo $username; ?>"/>, document.body );
</script>
But that doesn't work; nothing is displayed. I'm thinking that's because the text/jsx script area doesn't get executed...but of course if I remove that there's a syntax error.
Soo...I'm just wondering, what's the typical method for taking data loaded up from the DB and passing it into a React component?
Method 1: Define a global variable.
In your main PHP file:
<script>
window.reactInit = {
username: <?php echo $username; ?>
};
</script>
In your component:
class Greeting extends React.Component {
constructor(props) {
super(props);
this.username = reactInit.username;
}
}
If you use Redux, you may find this method particularly useful. Because ANY of your reducers can access this global variable during building of the initial state.
Method 2: Use data-* attributes.
<div id="app" data-username="<?php echo $username; ?>"></div>
All data-* attributes are passed by using {...app.dataset}.
const app = document.getElementById('app');
ReactDOM.render(<Greeting {...app.dataset} />, app);
Now you can access your server data as ordinary props.
class Greeting extends React.Component {
constructor(props) {
super(props);
console.log(this.props.username);
}
}
This method is not so flexible as the previous one, but seems to be more consistent with React philosophy.
The React way would be to load in the data via a RESTful API.
However, you could look into serverside rendering of React components with PHP V8JS. Not sure how stable it is, but if, it would be a very good/better alternative to the AJAX call on the client. It would look somewhat like this:
// the library
$react_source = file_get_contents('/path/to/build/react.js');
// all custom code concatenated
$app_source = file_get_contents('/path/to/custom/components.js');
$rjs = new ReactJS($react_source, $app_source);
$rjs->setComponent('MyComponent', array(
'any' => 1,
'props' => 2
)
);
/// ...
// print rendered markup
echo '<div id="here">' . $rjs->getMarkup() . '</div>';
If you actually want to render this in the browser, you can use plain Javascript instead of JSX:
<?php $username = 'Eric Andre'; ?>
<script type="text/javascript">
ReactDOM.render(React.createElement(Greeting, { username: "<?php echo $username; ?>" }), document.body);
</script>
Another option would be to transform the JSX into plain Javascript with babel-browser and use <script type="text/babel">. Keep in mind that babel-browser is not in active development anymore and also not intended for production use.
<?php $username = 'Eric Andre'; ?>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/6.1.19/browser.min.js"></script>
<script type="text/babel">
ReactDOM.render( <Greeting username="<?php echo $username; ?>"/>, document.body );
</script>
You could also take a look at https://github.com/erikras/react-redux-universal-hot-example for some examples on how to call RESTful APIs from a client.
Specifically it uses superagent to make the AJAX calls:
https://github.com/visionmedia/superagent
Related
So I am using Laravel 5.5. I have a data coming from my Controller and I want to pass it to my root vue instance not the component.
So for example I have the Dashboard Controller which has a data of "users"
class DashboardController extends Controller {
public function index(){
$user = User::find(1);
return view('index', compact('user'));
}
}
I am using Larave mix on my project setup. So my main js file is the app.js. That "$user" data I need to pass on the root Vue instance. Which is located in app.js
const app = new Vue({
el: '#dashboard',
data: {
// I want all the data from my controller in here.
},
});
If you don't want to use an API call to get data (using axios or else), you could simply try this :
JavaScript::put(['user' => $user ]);
This will, by default, bind your JavaScript variables to a "footer" view. You should load your app.js after this footer view (or modify param bind_js_vars_to_this_view).
In app.js :
data: {
user: user
}
Read more : https://github.com/laracasts/PHP-Vars-To-Js-Transformer
I would make a request to fetch the user's data as has been suggested.
Alternatively, you can add a prop to the dashboard component in index.blade.php and set the user like <dashboard :user="{{ $user }}"></dashboard>. You'll probably want to json_encode or ->toArray() the $user variable.
Then within the dashboard component you can set data values based on the prop.
props: ['user'],
data () {
return {
user: this.user
}
}
I just solved this by placing a reference on the window Object in the <head> of my layout file, and then picking that reference up with a mixin that can be injected into any component.
TLDR SOLUTION
.env
GEODATA_URL="https://geo.some-domain.com"
config/geodata.php
<?php
return [
'url' => env('GEODATA_URL')
];
resources/views/layouts/root.blade.php
<head>
<script>
window.geodataUrl = "{{ config('geodata.url') }}";
</script>
</head>
resources/js/components/mixins/geodataUrl.js
const geodataUrl = {
data() {
return {
geodataUrl: window.geodataUrl,
};
},
};
export default geodataUrl;
usage
<template>
<div>
<a :href="geodataUrl">YOLO</a>
</div>
</template>
<script>
import geodataUrl from '../mixins/geodataUrl';
export default {
name: 'v-foo',
mixins: [geodataUrl],
data() {
return {};
},
computed: {},
methods: {},
};
</script>
END TLDR SOLUTION
If you want, you can use a global mixin instead by adding this to your app.js entrypoint:
Vue.mixin({
data() {
return {
geodataUrl: window.geodataUrl,
};
},
});
I would not recommend using this pattern, however, for any sensitive data because it is sitting on the window Object.
I like this solution because it doesn't use any extra libraries, and the chain of code is very clear. It passes the grep test, in that you can search your code for "window.geodataUrl" and see everything you need to understand how and why the code is working.
That consideration is important if the code may live for a long time and another developer may come across it.
However, JavaScript::put([]) is in my opinion, a decent utility that can be worth having, but in the past I have disliked how it can be extremely difficult to debug if a problem happens, because you cannot see where in the codebase the data comes from.
Imagine you have some Vue code that is consuming window.chartData that came from JavaScript::put([ 'chartData' => $user->chartStuff ]). Depending on the number of references to chartData in your code base, it could take you a very long time to discover which PHP file was responsible for making window.chartData work, especially if you didn't write that code and the next person has no idea JavaScript::put() is being used.
In that case, I recommend putting a comment in the code like:
/* data comes from poop.php via JavaScript::put */
Then the person can search the code for "JavaScript::put" and quickly find it. Keep in mind "the person" could be yourself in 6 months after you forget the implementation details.
It is always a good idea to use Vue component prop declarations like this:
props: {
chartData: {
type: Array,
required: true,
},
},
My point is, if you use JavaScript::put(), then Vue cannot detect as easily if the component fails to receive the data. Vue must assume the data is there on the window Object at the moment in time it refers to it. Your best bet may be to instead create a GET endpoint and make a fetch call in your created/mounted lifecycle method.
I think it is important to have an explicit contract between Laravel and Vue when it comes to getting/setting data.
In the interest of helping you as much as possible by giving you options, here is an example of making a fetch call using ES6 syntax sugar:
routes/web.php
Route::get('/charts/{user}/coolchart', 'UserController#getChart')->name('user.chart');
app/Http/Controllers/UserController.php
public function getChart(Request $request, User $user)
{
// do stuff
$data = $user->chart;
return response()->json([
'chartData' => $data,
]);
}
Anywhere in Vue, especially a created lifecycle method:
created() {
this.handleGetChart();
},
methods: {
async handleGetChart() {
try {
this.state = LOADING;
const { data } = await axios.get(`/charts/${this.user.id}/coolchart`);
if (typeof data !== 'object') {
throw new Error(`Unexpected server response. Expected object, got: ${data}`);
}
this.chartData = data.chartData;
this.state = DATA_LOADED;
} catch (err) {
this.state = DATA_FAILED;
throw new Error(`Problem getting chart data: ${err}`);
}
},
},
That example assumes your Vue component is a Mealy finite state machine, whereby the component can only be in one state at any given time, but it can freely switch between states.
I'd recommend using such states as computed props:
computed: {
isLoading() { return (this.state === LOADING); },
isDataLoaded() { return (this.state === DATA_LOADED); },
isDataFailed() { return (this.state === DATA_FAILED); },
},
With markup such as:
<div v-show="isLoading">Loading...</div>
<v-baller-chart v-if="isDataLoaded" :data="chartData"></v-baller-chart>
<button v-show="isDataFailed" type="button" #click="handleGetChart">TRY AGAIN</button>
I am a little confused about how to pass my database data loaded in a controller to another controller.
I load some list items from the server and on each item click I want to open the details about that item on another screen according to its id.
I read some questions about making a service or use $rootScope but $rootScope should be avoid as much as possible.
What is the best way of doing this in my case and could you show me how to do it? Should I load the data inside a service or there is an easiest way in my case?
list item using 1st controller:
<div class="item item-body list-container" id="temporada2016-list-item-container4" ng-model="item_id" ng-repeat="x in items" item="x" href="#/x/{{x.ID}}" ng-click="open_item(x)" ng-show="news_list">
<div id="temporada2016-markdown7" style="margin-top:0px;color:#666666;">
<h2 style="color:#008BBB;">{{ x.TITLE }}</h2>
</div>
</div>
1st controller
.controller('temporada2016Ctrl', ['$scope', '$http', function ($scope, $http) {
$scope.active_news_btn = true;
$scope.search_news = true;
$scope.news_list = true;
$scope.albums_list = false;
$http.get("http://localhost/select-news.php").then(function(response){
console.log(response);
console.log(JSON.stringify(response));
$scope.items = response.data;
});
$scope.open_item = function(x){
//alert("Item id: " + x.ID);
$http.post("http://localhost/select-news-by-id.php", {'item_id': x.ID}).then(function(response){
console.log(response);
console.log(JSON.stringify(response));
$scope.all = response;
$scope.title = response.data[0].TITLE;
$scope.body = response.data[0].BODY;
});
}
}])
second screen (details) using 2nd controller where I want to load the same title and news body
<ion-view title="Detalhes" id="page4" style="background-color:#FFFFFF;">
<ion-content padding="true" class="has-header">
<h3 id="detalhes-heading1" style="color:#008BBB;font-weight:600;font-style:italic;">{{title}}</h3>
<div id="detalhes-markdown3" style="color:#000000;">
<p>{{body}}</p>
</div>
<form id="detalhes-form4" class="list">
<button id="detalhes-button6" style="color:#008BBB;text-align:left;border-radius:9px 9px 9px 9px;" class="button button-calm button-clear icon ion-ios-heart-outline like_btn"></button>
<label class="item item-input" id="detalhes-textarea1">
<span class="input-label"></span><textarea placeholder=""></textarea>
</label>
</form>
<button id="detalhes-button17" style="color:#FFFFFF;" class="button button-positive">Comment</button>
</ion-content>
</ion-view>
2nd controller
.controller('detalhesCtrl', ['$scope', '$stateParams', function ($scope, $stateParams) {
}])
PHP
<?php
include_once('conn.php');
$data = json_decode(file_get_contents("php://input"));
if(property_exists($data, 'item_id')){
$item_id = $data->item_id;
$sql = $mysqli->query("SELECT * FROM news WHERE id = '".$item_id."'");
if($sql->num_rows > 0){
while($row = $sql->fetch_array(MYSQLI_BOTH)){
$registro = array(
"ID" => $row['id'],
"TITLE" => $row['title'],
"BODY" => $row['body']
);
$retorno[] = $registro;
}
}
$mysqli->close();
$retorno = json_encode($retorno);
echo $retorno;
}
?>
In your app-config
$stateProvider
.state('master', {
url: '/master',
templateUrl: 'views/master.html',
controller: 'MasterCtrl',
data: {
someThingToPassToMasterState: false
}
})
.state('details', {
url: '/details',
data : {
somethingToPassToDetailsState: false
},
templateUrl: 'views/details.html',
controller: 'DetailsCtrl'
});
And then in your MasterCtrl
$scope.onClick = function(obj) {
var dataToPass = {};
dataToPass.obj = obj;
dataToPass.somethingElse = 'blah blah';
$state.go('details', {somethingToPassToDetailsState: dataToPass});
}
// Now in the DetailsCtrl
if(!$state.params.somethingToPassToDetailsState) {
// handle this
// maybe do a $state.go('default') and then return to end execution of this controller
}
// Some code
In master.html, using ng-repeat to simulate master-details page redirection
<div ng-repeat="o in objects">
<div ng-click="redirectTo(o)">{{o.name}}</div>
</div>
The idea is to pass day directly from one state to another on state transition. You can either pay in I'd and make api call AFTER transitioning to these new state or get the response from api and then paas required data to the next state
First of all, it is highly recommended you make your http calls in a factory or a service. This will make your code more reusable and it will look something like this:
app.factory("responseFactory", function($http) {
return {
getData: function() {
//Code for making http call goes in here
$http.get("http://localhost/select-news.php").then(function(response){
return(response.data);
});
},
postData: function(x) {
$http.post("http://localhost/select-news-by-id.php", {'item_id': x.ID})
.then(function(response){
return(response.data);
});
}
};
});
You could later use this to call in your controller by injecting this factory in your controller and calling this factory something like this:
app.controller('temporada2016Ctrl', ['$scope', 'responseFactory', function ($scope, responseFactory) {
$scope.items = responseFactory.getData();
$scope.opnItem = function(x){
$scope.all = responseFactory.postData(x);
$scope.title = all.TITLE;
$scope.body = all.BODY;
}
}]);
Now, to make the data available in your second controller, you could do a few things.
Pass it through the $rootScope, which as you already said, should be avoided as much as possible to not clutter the rootScope. It can have many consequences. - NOT RECOMMENDED
Make a service call from the second controller and you will have all the data you need from the api. However, if you edit the data in the first controller and wish to make edited data available in the second controller, that will not be possible using this method. Also, making http calls is costly and it highly recommended to minimize your number of http calls in the app. - NOT RECOMMENDED
Use Angular service/factory - HIGHLY RECOMMENDED
app.factory('commonData', function() {
var data;
return{
setData: setData,
getData: getData
};
//setter
function setData(dataToBeShared) {
data = dataToBeShared;
}
//getter
function getData() {
return data;
}
});
Now you can inject this factory into your controllers and use the setter and getter methods easily. DO not forget to inject the responseFactory which we created earlier!
After injecting it into your first controller, you can call the commonData factory and use the setter method to set the data, something like this:
app.controller('temporada2016Ctrl', ['$scope', 'responseFactory', 'commonData', function ($scope, responseFactory, commonData) {
//All your controller code, including calling the factory like I earlier explained...it all goes here
commonData.setData(passTheDataThatYouWantToShare);
}]);
Now, to get the data in the other controller, all you need to do is access the factory's getter method and you get the data! That will be something like this:
app.controller('detalhesCtrl', ['$scope', '$stateParams', 'commonData', function ($scope, $stateParams, commonData) {
$scope.commonData = commonData.getData();
//Use $scope.commonData to use all the data that has come in from first controller
}]);
Now, the data that is passed from controller 1 is stored in the factory and can be retrieved in the second controller whenever you want. Suppose you would like to display them as panes, next to each other, you might want to add watchers, otherwise this method should work fine for you.
NOTE: This can be achieved without using setter and getter methods, but using them is a good practice and is very useful when an app gets bigger.
Pass data through state params using Angular UI router. It seems to me that you are using Angular UI router which comes bundled with ionic. This can also be used when you are routing. At the time of writing this answer, another answer on this thread (by SLearner) has already explained this method and if it is recommended or not is more or less your choice depending on the level of functionality you want. However, in my opinion, I would not go in with this solution. You can find some more answers on this topic on this thread: AngularJS: Pass an object into a state using ui-router
So, concluding my answer, in my opinion, it is best you go in for an angular factory. IMHO, that is the best solution. Hope your queries are answered.
Cheers!
Share data between two controller is not good practice.
We need to put the data in service which can easily be shared with any number of controllers
Service: MyService
this.dbDataSearch = function(parameters){
// Search record from database
this.resultData = data;
}
In Convtoller 1:
$scope.data = MyService.resultData;
In Convtoller 2:
$scope.data = MyService.resultData;
....
In Convtoller n:
$scope.data = MyService.resultData;
Once service variable will update all these controller variables automatically updated.
In prestashop I am doing a small module. In that module in smarty I have a form. I want to submit those values using ajax. So for that I have made my ajax as $.post like this
jQuery(document).ready(function($) {
$('.module-form').submit(function(e) {
e.preventDefault();
msg = '';
$form = $(this);
$.post(
baseDir + "modules/mymodule/mymodule.php",
{ name: 'myname', action: 'form_subscribe' },
function(data) {
var response = jQuery.parseJSON(data);
console.log(response);
}
);
return false;
});
});
In the module file (mymodule.php) I have my code like this
class MyModule extends Module {
public function __construct() {
-----
----
--
}
function form_subscribe() {
$name = $_POST['name'];
echo json_encode($name);
exit;
}
}
But when I am doing submit the form it is showing the response as null. Can someone tell me how to solve this issue? Any help and suggestions will be really appreciable. Thanks.
You can't access a class or a method directly like you are trying to do.
To perform your action you have to first instantiate the Class and then call the function.
Particularly in a prestashop environment, follow this step:
Create another php page inside your module folder, lets call the page mymodule_ajax.php.
Then inside of it retrieve the configuration, the initialization of the prestashop context and then instantiate the module and perform the call management.
mymodule_ajax.php:
include(dirname(__FILE__). '/../../config/config.inc.php');
include(dirname(__FILE__). '/../../init.php');
/* will include module file */
include(dirname(__FILE__). '/mymodule.php');
/* will instantiate the class MyModule so that you can access the method */
$my_mod = new MyModule;
/* using Prestashop Tools Class we ensure that the call is made from the form with action "form_subscribe */
if(Tools::isSubmit('action') && Tools::getValue('action') == 'form_subscribe')
$my_mod->form_subscribe(); //call the method
Then change your ajax code to point that url so:
$.post(
baseDir + "modules/mymodule/mymodule_ajax.php", //new url
{ name: 'myname', action: 'form_subscribe' }
You should now retrieve the json value in your console.log.
I have question about call to my module action via ajax.
I'd like call to class in my module via ajax. But best solution for me is call to clean class. Not extends Module.
I don't know hot can I make url without add article to database and add module to him.
I use JQuery instead mooTools but js framework is not important. Most important is call to php class by ajax.
I have ajax module. But if I call to ajax.php required is module id from tl_module table. I don't want use this table. (Ajax will be very often calling, I prefer to don't load all contao mechanism. It should be very fast).
Thanks in advance for answers.
I found the answer for Contao >3.x in a GitHub issuse(german)
At first do in your Front-end Template:
<script type="text/javascript">
var data = {};
data["REQUEST_TOKEN"] = "<?php echo REQUEST_TOKEN ?>";
$(document).ready(function(){
$("#trigger").click(function(event){
$.post(
'<?php echo \Contao\Environment::get('requestUri')?>',
data,
function(responseText) {
alert(responseText);
}
).fail(function( jqXhr, textStatus, errorThrown ){ console.log( errorThrown )});
event.preventDefault();
});
});</script>
Important is the
- data["REQUEST_TOKEN"] -> if you do not add it, the POST-request will not reach your module:
public function generate()
{
if ($_SERVER['REQUEST_METHOD']=="POST" && \Environment::get('isAjaxRequest')) {
$this->myGenerateAjax();
exit;
}
return parent::generate();
}
//do in frontend
protected function compile()
{
...
}
public function myGenerateAjax()
{
// Ajax Requests verarbeiten
if(\Environment::get('isAjaxRequest')) {
header('Content-Type: application/json; charset=UTF-8');
echo json_encode(array(1, 2, 3));
exit;
}
}
If you want to do the ajax via GET you do not need the reqest token but the jquery funktion $get();
I would suggest you to use Simple_Ajax extension.
In this case you dont need to use Database and you can do pretty much anything you can do normally with Jquery ajax calls.
It works with Contao 2.11 and you can call your php class with it.
I find it much easier to use than ajax.php .
You can get it from : https://contao.org/de/extension-list/view/simple_ajax.de.html
Copy SimpleAjax.php to Contao's root folder.
Go to [CONTAO ROOT FOLDER]/system/modules and create a php file like following :
class AjaxRequestClass extends System
{
public function AjaxRequestMethod()
{
if ($this->Input->post('type') == 'ajaxsimple' )
{
// DO YOUR STUFF HERE
exit; // YOU SHOULD exit; OTHERWISE YOU GET ERRORS
}
}
}
Create a folder called config with a php file like following ( You can hook you class to TL_HOOKS with class name - class method, simple_ajax will execute you method whenever a ajax call is made ):
$GLOBALS['TL_HOOKS']['simpleAjax'][] = array('AjaxRequestClass','AjaxRequestMethod'); // Klassenname - Methodenname
Now you can easily make ajax calls with simply posting data to SimpleAjax.php:
$.ajax({
type: "POST",
url: "SimpleAjax.php",
data: { type: "ajaxsimple" },
success: function(result)
{
//DO YOUR STUFF HERE
}
I begin with an object-oriented programming in php.
I want to do the logging - using ajax and jquery.
EDIT: Can I call the function ajax from file jquery.php - using jquery AJAX?
//FILE jquery.php
class jquery {
public function ajax() {
echo "result";
}
}
If you make an ajax call similar to this :
http://example.com/ajax.php?firstParam=1
Within your ajax.php file you can do something like this :
switch($_GET['firstParam']){
case "1":
callYourFunction();
break;
case "2":
someOtherFunction();
break;
}
This is a very stripped down example... In a real world case you would have to take security into account and sanitize any information you are getting from outside your server (i.e. from a user).
The names I have given are only place holders - you can change the function/variable names to whatever you feel comfortable with.
I hope this sheds some light on your conundrum.
Not sure if you completely understand what AJAX is, but it's an acronym for Asynchronous JavaScript and XML. With Ajax, web applications can send data to, and retrieve data from, a server asynchronously (in the background) without interfering with the display and behaviour of the existing page. That's it.
This I believe is what you're looking for (though its not OOP, but im not writing an entire OOP login to try answer your question)
File: login.php
<?php
$db = new PDO("mysql:host=localhost;dbname=database", SQL_USER, SQL_PASS );
$stmt = $db->prepare('SELECT user_id, user_activated FROM users WHERE ( username = AND user_password = ? LIMIT 1');
$stmt->execute( array( $_POST['u'], $_POST['p'] ) );
if( $stmt->rowCount() == 1 )
{
// Authentication session storage stuff here
echo 'Logged in';
}
else
{
echo 'bad login';
}
?>
So you could have a HTML page with something like:
<div id="results"></div>
<input type="text" id="txtUsername" /> <br />
<input type="password" id="txtPassword" /><br />
<button id="cmdLogin">Login</button>
<script>
$(document).ready(function(){
$("#cmdLogin").click(function(){
$u = $("#txtUsername").val();
$p = $("#txtPassword").val();
$.ajax({
type: "POST",
url: "http://yoursite.com/login.php",
data: "u="+u+"&p="+p
}).done(function(results) {
$("#results").html(results).hide().fadeIn();
});
});
});
</script>
Also, keep in mind this code is un-tested and written it as replying to this, so don't treat this as a solution to what you're wanting, but rather a resource to help you to implement what it is you're asking for.
You cannot call a class directly - but you can fetch the result of an execution.
Here an example how you can call it nearly direct - but don't use this for critical login code.
<?php
$ftcn = $_GET['getname'];
$bc = new ReallyBadCode();
$bc->$ftcn();
class ReallyBadCode{
function test(){
}
function __call($name, $args){
$this->$name($args);
}
}