Render React JS server side using .net - javascript

I did the comments tutorial on http://reactjs.net/getting-started/tutorial.html and got it to render server side using .net mvc.
I have an existing mvc app where I rewrote a page in react. I'm trying to render it server side using .net but I get an error when it tries to render server side.
Exception Details: React.Exceptions.ReactServerRenderingException: Error while rendering "TopicAnswers" to "react1": TypeError: undefined is not a function
at React.createClass.render (Script Document [8]:80:39) -> var answerNodes = this.props.data.map(function(answer){
at ReactCompositeComponentMixin._renderValidatedComponentWithoutOwnerOrContext (Script Document [2]:7395:34)
Here's the code:
In my MVC view:
#Html.React("TopicAnswers", new
{
initialAnswers = Model,
url = Url.Action("TopicAnswers", new { id = ViewBag.TopicID }),
})
My TopicAnswers.jsx file:
var TopicAnswers = React.createClass({
getInitialState: function(){
alert('inside getInitialState: ' + this.props.initialAnswers);
return {answers: this.props.initialAnswers};
}
My ReactConfig.cs file:
ReactSiteConfiguration.Configuration
.AddScript("~/Scripts/internal/eusVote/TopicAnswers.jsx");
QUESTION: Why is it having a problem rendering the react file server side?

I got this error message trying to render React jsx file server side using .net:
"React.Exceptions.ReactServerRenderingException: Error while rendering "TopicAnswers" to "react1": TypeError: undefined is not a function"
My problem was that in the JSX file, I still had ComponentWillMount which called the loadAnswersFromServer. This is what you need if you are rendering client side. But once you adjust your code to render server side, you need to comment out/remove the ComponentWillMount so that it doesn't try to run the loadAnswersFromServer function on the server side.
With MVC, the data should be passed from the controller to the view and referenced in the #Html.Render("Comment", new { initialData = Model }) for the initial load.
Also, don't forget to comment out/remove the React.render( ... ) line from the JSX file.

alert is available only in browser window context. When rendering it on a server you can not call any functions that are browser related. If you are calling them, then you need to shim them so they are not failing on a server.

Related

How do you run scripts off of handlebar data

I'm trying to parse data from a node.js/ express server which I wish to use on the client-side as a variable (although I understand this could most likely all be done server-side, I'm trying to learn about how handlebars handles data). At this stage I wish to have it as an object I can print so I can use it at a later stage. My current code is as follows:
Handler for request
exports.getList = (req,res) => {
console.log("Request for list sent");
var MyDataObject = {
days: 75,
people: 12
};
return res.status(200).render("home", {MyDataObject});
};
Client-Side
<script>
handleServerData = (ServerDataObject) => {
console.log(ServerDataObject);
};
</script>
{{#if MyDataObject}}
<script>handleServerData({{MyDataObject}})</script>
{{/if}}
When the page is requested through the shown handler this simply gives the error Uncaught SyntaxError: Unexpected identifier Which I assume is from it setting {{MyDataObject}} to [object Object] inside the sources view however I don't know how to fix this although I assume it does it because of it running the script before the handlebars parse the data.
Any help fixing this is greatly appreciated.

What is optimal data flow between PHP backend and Vue frontend

I am wondering, what would be the best setup of data flow from PHP (database) to fronted Javascript rendering, lets say VueJS.
My first idea is, that will provide data attribute with JSON data to HTML element - div, which Vue instance will be mounted on.
I dont want to create REST API and load data via AJAX HTTP requests from Vue.
# PHP part
echo '<div id="myId" data-my-data="{h1:\"My heading\"}"></div>';
// Javascript part
import Vue from 'vue/dist/vue.js';
import App from './MyApp.vue';
const el = document.querySelector('#myId');
new Vue({
el,
render: h => h(App, {
props: {
myData: JSON.parse(el.dataset.myData),
},
}),
});
What do you think, is it ok or is there any other option except REST?
Update:
Main purpose is that my App will handle complex - multi-step Forms with some complex fields, which I would like to create as reusable components.
App will validate forms via AJAX (to prevent re-rendering page), on success redirect to success page.
My infrastructure would be, that PHP will prepare data, render main layout with assets (JS + CSS) and DIV with data atributes for Vue App.

Can't Render EJS Template on Client

I'm coding an application on express, and I'm using ejs as a view/template engine.
At path /artists, I'm rendering the view artists.ejs which has artists covers. When clicking on a cover, I want an AJAX call to retrieve the corresponding data, place it in my template/view for artist artist.ejs and display this template in my HTML under the cover.
I've seen this related question but it has not solved my use case.
Everything seems clear, but I can't render the data with the template. I would like to compile the template server-side, send it to the client ready to use, and then fill it in when needed with the data received from the AJAX call.
What I've done:
When calling /artists, compile on server-side using ejs.compile(str, opt):
router.get('/artists', function(req, res) {
// Compile artist template
fs.readFile('views/artist.ejs', "utf-8", function(err, template) { // Convert template file to string
artist_template = ejs.compile(template); // Compile template
res.render('artists.ejs', {template: artist_template}); // render page with compiled template
});
I took care of converting the file into String, as ejs compiler only works with String (compared to Jade .compileFile)
Then on client-side, I grab the function:
<script>
var template = <%= template %>
</script>
Then on another script, I retrieve the data with an AJAX call:
$.get('/artists/'+artist_name, function(data) {
var html = template({artist: data});
$('#artist-page').html(html);
}
But when I make the call, I receive:
Uncaught ReferenceError: fn is not defined
When I call the template, fn, I receive:
Uncaught ReferenceError: opts is not defined.
Is the function fn hard-coded? I've read the EJS and Jade documentation but there was little relevant information in regards to my issue.
Do I perhaps need the template on client-side also?
I eventually found a workaround to my question, and I understood with your answer that you could proceed in 2 different ways:
1) What I did: read and save template as a string, then render it client-side with ejs Runtime script.
// In controller.js
var templates = {};
templates.template1 = fs.readFileSync(filePath1, 'utf-8'); // Read template as a string
templates.template2 = fs.readFileSync(filePath2, 'utf-8');
...
res.render('app.ejs', {templates: templates}); // Send templates in view
// In view app.ejs
<script type="text/javascript">
var templates = <%- JSON.stringify(templates) %>; // Get templates object (object of strings)
</script>
<script type="text/javascript" src="/JS/ejs.min.js"></script> <!-- Load ejs RunTime -->
// In site.js - javascript client/public file
$.get('/artists', function(data) {
var html = ejs.render(templates.template1, data); // Render ejs client side with EJS script (template1 corresponds to the artists template)
$('#artists-wrapper').html(html); // Sets HTML
});
Thus, I send all my templates on first page load, and then I render the requested page on the client side. The interest, according to what I've read, is that you only send JSON object (your data) through AJAX calls, and not the entire page, making your request light. Only the first load is heavy with all your templates.
2) What I would like to do according to #RyanZim answer: compiling templates server side into functions, send them, and then call them on the client side : template(data). If I understood well, there is no need of EJS client library in this case, and my templates are no longer strings but functions:
// In controller.js
var templates = {};
templates.template1 = ejs.compile(fs.readFileSync(filePath1, 'utf-8'), {client: true}); // Get template as a function
templates.template2 = ejs.compile(fs.readFileSync(filePath2, 'utf-8'), {client: true});
...
res.render('app.ejs', {templates: templates}); // Send templates in view
However, I can't get them in my view:
<script type="text/javascript">
var templates = <%- JSON.stringify(templates) %>; // Get templates object (object of functions)
</script>
is not working. they are functions on the server before I send them, but I don't know how to recover them. Do you have an idea ?
I tried a workaround, by changing them into String before sending them:
templates.template1 = templates.template1.toString();
Send them and then client side, transform them back in functions:
var template = new Function(templates.template1);
$.get('/artists', function(data) {
var html = template(data);
$('#artists-wrapper').html(html); // Sets HTML
});
But that won't work either.
Do you have an idea what I'm missing here?
And last, do you agree that compiling them server side before using the functions is better in terms of computation than rendering each template client-side?
Thanks for the help, and hope that will help anybody else!
You need to use the client option on the server side when you are compiling for the client. From the docs:
client When true, compiles a function that can be rendered
in the browser without needing to load the EJS Runtime
https://github.com/mde/ejs#options
Your server-side code snippet should be:
// Compile artist template
fs.readFile('views/artist.ejs', "utf-8", function(err, template) {
artist_template = ejs.compile(template, {client: true}); // Use client option
res.render('artists.ejs', {template: artist_template});
});

Call to MapSignalR causes Protocol error

this is a follow-up to my previous question here..
MVC - trouble linking to another Controller/Action
as you can see, i eventually did get my view from another controller to display in a new tab so it was working. that is until i installed SignalR. the simple version using this tutorial as a guide..
http://www.asp.net/signalr/overview/getting-started/tutorial-getting-started-with-signalr-and-mvc
the tutorial worked fine after following the steps to create a project. the only thing i had to do to make it work was change the version of the jquery signalr javascript file to the latest (it was one i didn't have because the tutorial was written in older VS 2012).
in any case, after following the same steps for my site, i now get an error when i click the link for /SignalR/SRStart (new tab)..
Protocol error: Unknown transport
playing around i found that this only happens after calling app.MapSignalR() in the startup.cs file. can't understand why since the tutorial i followed worked fine unless it has something to do with crossing over into another controller on that link. it's in the SRStart view that i placed all the signalr connection code and callback function but i don't think it's ever reached since the page doesn't even load.
this is my code..
startup.cs
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
app.MapSignalR();
}
}
hub
public class SRHub : Hub
{
public void Send(string message)
{
// Call the addNewMessageToPage method to update clients.
var conn = GlobalHost.ConnectionManager.GetHubContext<SRHub>();
conn.Clients.All.addNewMessageToPage(message);
//Clients.All.addNewMessageToPage(message);
}
}
javascript in SRStart.cshtml
$(function () {
// Reference the auto-generated proxy for the hub.
var conn = $.connection.sRHub;
// Create a function that the hub can call back to display messages.
conn.client.addNewMessageToPage = function (message) {
if (!message.contains('[EOF]')) {
populateStreamDialog(message);
}
};
$.connection.hub.start()
.done(function () {
});
});
any help would be appreciated..
I was able to replicate error. Problem is that /SignalR is route used by SignalR itself.
By using MVC controller named SignalRController there is now conflict between SignalR and MVC causing the error.
Just rename you MVC controller SignalRController (and folder containing its views) to something else...

Attempted to handle event `becameInvalid` while in state 'root.loaded.saved'

I've implemented DS.Errors for my RestAdapter, thanks to Alex Spellers tutorial on server side validation.
However, in this part of my app I want to do a simple client side check to see if the form is complete. (Why not have DS.Errors handle all the errors?)
process: function(upload) {
var form = upload.get('form');
if (!isComplete(form)) {
upload.get('errors').add('field', 'field isempty');
return;
}
// else "Processing..."
The logic here is somewhat simplified, but errors.add() should invalidate, and add an error to the model. However I'm getting the following error:
Uncaught Error: Attempted to handle event `becameInvalid` on <#model:upload:54a1f298ef912a2ace760b0f> while in state root.loaded.saved.
I have read about the state manager, but am unsure as to how, and what state I should transition to before adding an error to my model.
Thanks in advance!
Ember : 1.8.1
Ember Data : 1.0.0-beta.11
Handlebars : 1.3.0
jQuery : 1.11.2
After revisiting this I came across the following post. What solved my problem was not sending willCommit, but sending upload.send('becomeDirty'); before executing the following:
upload.get('errors').add('field', 'field isempty');
Now errors can be added as the upload model is not in the saved state.

Categories