Using serverside HTML-templates with ReactJS - javascript

I'm struggling with Reactjs and component rendering.
Basically I've regular html-templates at the server and I'm trying to use them as a JSX-components with the React. Otherwise it works just fine, but I'm not able to fire the events for example: this.handleSubmit.
How to render the loaded template as a React-element?
//Template /index.html
<form onSubmit={this.handleSubmit}>
<input type="text">
<input type="submit"
</form>
//Template loader
var App = React.createClass({
componentDidMount: function() {
this.updateContent();
},
updateContent: function(){
/**
* Loads the template from the server and sets
* a new state
*/
var url = this.props.source.slice(1);
$.get(url, function(result) {
var html = result;
if (this.isMounted()) {
this.setState({
content: html
});
}
}.bind(this));
},
handleSubmit: function(){
console.log('Submit fired');
}
render: function() {
var converter = new HTMLtoJSX({createClass: false});
var jsx = '/** #jsx React.DOM */ ' + converter.convert(this.state.content);
return (
<div>
{JSXTransformer.exec(jsx)}
</div>
);
});
React.render(
<App source="#/index.html" />,
mountPoint
);

JSX isn't templates for a markup language, it's a syntax extension to the JavaScript programming language. The distinction is important here.
You need to convert JSX to JS (usually done when building your project). If we modify your code to be valid JSX it looks like this:
<form onSubmit={this.handleSubmit}>
<input type="text" />
<input type="submit" />
</form>
And when run through the jsx tools the output is the following JavaScript expression.
React.createElement("form", {onSubmit: this.handleSubmit},
React.createElement("input", {type: "text"}),
React.createElement("input", {type: "submit"})
)
You need to execute this code in render, with the correct this context. You could do this by wrapping the above in a function before serving it to the client:
function renderThingy(){
React.createElement("form", {onSubmit: this.handleSubmit},
React.createElement("input", {type: "text"}),
React.createElement("input", {type: "submit"})
)
}
And calling that in render:
render: function() {
return (
<div>
{renderThingy.call(this)}
</div>
);
}
This is of course confusing, and it's not apparent if handleSubmit is used by anything. Then of course, there's the issue of loading code asynchronously... I'd rather not delve into that here.
This also severely limits what you can do in your 'template', and various other problems will pop up.
tl;dr don't do this
If you want to use JSX on the server: cool, you just need a JS runtime, and a component. React.renderToString or React.renderToStaticMarkup will take care of actually giving you valid html.

The template should be precompiled using React.renderToString(), then the html returned from the function has all the extra DOM attributes such as data-react-id needed to make a reconciliation with the client.
This is because the onSubmit={this.handleSubmit} in your template doesn't have the react data associated, react doesnt care/know about it.
React can render clean html too without the bloat using React.renderToStaticMarkup() function;
Take a look here http://facebook.github.io/react/docs/top-level-api.html#react.rendertostring

Related

Can you use functions from an imported JavaScript library such as Change Case directly in a Vue component's template?

I understand how to import and use Change Case within the <script></script> element of a Vue component, which is just the standard Javascript import covered in the Change Case Github page. However, I would like to use the Change Case functions directly in the template if possible.
Currently, it is my understanding that for dynamic content in the template, in this case generated by v-for running through an array, I must render the return value of a intermediary method from the component's methods section which applies the Change Case function. A method is required for each case type (e.g. camelCase, snakeCase, etc.) I want to render, in this instance one (capitalCase). For example:
// ...
<div
v-for="location in locations"
:key="location.name"
>
<input
type="checkbox"
:id="`select-${location.name}`"
:value="capitalCaseLocationName(location.name)"
v-model="locationsInput"
/>
<label :for="`select-${location.name}`">
{{ capitalCaseLocationName(location.name) }}
</label>
</div>
// ...
methods: {
capitalCaseLocationName(name) {
return capitalCase(name)
}
},
// ...
It would be preferable to somehow import Change Case into the template logic so I could write it like this (no intermediary methods needed):
// ...
<div
v-for="location in locations"
:key="location.name"
>
<input
type="checkbox"
:id="`select-${location.name}`"
:value="capitalCase(location.name)"
v-model="locationsInput"
/>
<label :for="`select-${location.name}`">
{{ capitalCase(location.name) }}
</label>
</div>
// ...
Any chance of that being possible?
As long as you register the imported function as a method you should be able to use it directly in the template.
According to the code, you use Options API, so something like this should do the trick:
import {capitalCase} from "change-case";
...
methods: {
capitalCase,
myOtherMethod () => {...}
}
...
And in the <template>:
<input
type="checkbox"
:id="`select-${location.name}`"
:value="capitalCase(location.name)"
v-model="locationsInput"
/>
The functions need to be defined and passed to the template, that is why even console.log won't work from a template.
You already have an answer with an example, but here's another thing you could do that might make things easier.
You can create a helper like this:
template-helpers.js
export function capitalCase(str) {
return str.split(" ").map(wrd => wrd[0].toUpperCase() + wrd.slice(1)).join(" ")
}
export default {
capitalCase
}
this would make it so that you could use it in a composition/setup like this
import templateHelpers from "../utils/template-helpers.js";
setup(){
return{
...templateHelpers
}
}
in an options API component you could just include it like this
import templateHelpers from "../utils/template-helpers.js";
// ...
methods: {
...templateHelpers,
// other methods
}
// ...
Example
by exporting functions in export default you can destructure them by using methods: { ...templateHelpers
the downside is that it would all the methods every time, but it would make for a more convenient solution. Alternatively, you can pick and chose, since the functions are also exported
import {capitalCase} from "../utils/template-helpers.js";
// ...
methods: {
capitalCase,
// other methods
}
// ...
Vue does have a way to add global definitions, but it's discouraged. This would be done by assigning it to config.globalProperties
https://vuejs.org/api/application.html#app-config-globalproperties
app.config.globalProperties.capitalCase = (str) => {
return str.split(" ").map(wrd => wrd[0].toUpperCase() + wrd.slice(1)).join(" ")

Template Literals - Changing the Inner HTML of an element (onInput event)

So I´ve never used Template Literals before, but I need to work with a template now that seemingly includes a form with Template Literals
This is one of my inputs:
<input
type="number"
className="mf-input "
min="1900"
max="2099"
step="1"
id="curyear"
name="currentyear"
placeholder="${ parent.decodeEntities(`Current Year`) } "
onInput=${parent.handleChange}
aria-invalid=${validation.errors['currentyear'] ? 'true' : 'false'}
ref=${el => parent.activateValidation({"message":"This field is required.","minLength":1900,"maxLength":2099,"type":"by_character_length","required":false,"expression":"null"}, el)}
/>
<${validation.ErrorMessage}
errors=${validation.errors}
name="currentyear"
as=${html`<span className="mf-error-message"></span>`}
/>
What I am trying to do is, in the onInput method, instead of handling the validation, I also want to change the innerHTML of an element:
<h2 class="elementor-heading-title elementor-size-default" id="curyeartext">Current Year</h2>
I´ve been trying to do it for hours, but I can't get it to work.
EDIT: turns out, they are template literals and not reactJS
Avoid setting innerHTML inside React, use state instead. This is because React will overwrite your modified DOM when it re-renders, if the html is in a node that React is controlling.
export default function MyReactComponent() {
var [ currentInput, setCurrentInput ] = React.useState();
return <>
<input
type="number"
className="mf-input"
min="1900"
max="2099"
step="1"
name="currentyear"
onInput={(e) => setCurrentInput(e.target.value)}
value={currentInput}
/>
<h2 class="elementor-heading-title elementor-size-default" id="curyeartext">
{currentInput}
</h2>
</>;
}
However, if you have a situation where it is unavoidable, you can tell React to ignore a node by only specifying a ref on it - i.e. no other props or child JSX:
export default function MyReactComponent() {
return <div ref={divRef => {
divRef.innerHTML = "Hello <b>world!</b>";
}} />
}
This technique is typically used when integrating non-React specific JS libraries into React, as you can do whatever you want with the contents of that DOM node.
I managed to do this by adding a function above the return:
function myfunction() {
document.querySelector('#curyeartext').innerHTML = document.querySelector('#curyear').value;
document.querySelector('#lastyear').innerHTML = document.querySelector('#curyear').value-1;
document.querySelector('#yearbefore').innerHTML = document.querySelector('#curyear').value-2;
}
And call it in the onInput event handler
onInput=${myfunction}

can i use pug (ex-jade) with react framework?

i have read some of pug documentation. its said that i have to install pug first and i'm already done that. then i have to require pug in my js file.
but i don't know where to write the compile for pug file in my react files? what is the right steps to use pug in react framework?
thanks! i really appreciated any help.
here is one of my component in react that i would like to render it with pug.
import React from 'react';
import Sidebar from './Sidebar';
import Header from './header/Header';
import {tokenverify} from '../../utils/helpers';
import pug from 'pug';
class Home extends React.Component {
componentDidMount() {
const token = localStorage.getItem('token')
tokenverify(token)
.catch((res) => {
this.props.history.push('/')
})
}
render() {
return(
<div className="main-container">
<div className="col-md-1">
<Sidebar history={this.props.history} username={this.props.params.username}/>
</div>
<div className="col-md-11">
<div className="row">
<Header history={this.props.history} username={this.props.params.username} />
</div>
<div className="row">
{this.props.children}
</div>
</div>
</div>
)
}
}
export default Home
I found this project in very early phase of its development : https://github.com/bluewings/pug-as-jsx-loader.
I like it because it lets me write my dumb (presentational) react components as pug templates.
The only JSX functionality it currently supports are iterating and conditional if. Which seems good enough for writing most of the dumb components.
Here are the steps to use it
1. Install pug-as-jsx-loader
npm install pug-as-jsx-loader --save-dev
For next step you will have to eject if you are using create-react-app
2. Tell webpack how to handle pug templates.
In your webpack.config.dev.js,
{ test: /\.pug$/, use: [require.resolve('babel-loader'), require.resolve('pug-as-jsx-loader')] },
3. Import pug template in your component
import myTemplate from './mycomponent.pug'
4. Return compiled template from render function
const MyComponent = ({someProperty, someOtherProperty})=> {
return myTemplate.call({}, {
someProperty,
someOtherProperty
});
};
5. Define a pug to render component
#my-element
ul.my-list
li(key='{something.id}', #repeat='something as someProperty')
div(className='planet') {something.name}
div(className='vehicle') {something.type}
div(className='overview') {something.cost}
div(className='cancel', onClick='{()=> someOtherProperty(something)}')
div(className='no-mobile fa fa-remove')
A read about my experience : https://medium.com/p/7610967954a
With Pug, you have two options: render template to HTML string, passing the data object right away or render template to an efficient javascript function that outputs html when passed a data object.
When using pug(alone) with dynamic data, the choice is obviously to compile to function, so that data can be applied on the client.
However, React does not actually consume, or send to the client, html.
If you read an explanation of JSX, you will see that it is just HTML-lookalike syntactic sugar that gets compiled to a javascript function that programmatically creates DOM nodes (essential for the way React handles diffing and updating the page). Pug at the moment, even on the client, outputs an HTML string. Hence, the only way we will be able to use it is
dangerouslySetInnerHTML as following:
//from https://runkit.io/qm3ster/58a9039e0ef2940014a4425b/branches/master?name=test&pug=div%20Wow%3A%20%23%7Ba%7D%23%7Bb%7D
function pug_escape(e){var a=""+e,t=pug_match_html.exec(a);if(!t)return e;var r,c,n,s="";for(r=t.index,c=0;r<a.length;r++){switch(a.charCodeAt(r)){case 34:n=""";break;case 38:n="&";break;case 60:n="<";break;case 62:n=">";break;default:continue}c!==r&&(s+=a.substring(c,r)),c=r+1,s+=n}return c!==r?s+a.substring(c,r):s}
var pug_match_html=/["&<>]/;
function pug_rethrow(n,e,r,t){if(!(n instanceof Error))throw n;if(!("undefined"==typeof window&&e||t))throw n.message+=" on line "+r,n;try{t=t||require("fs").readFileSync(e,"utf8")}catch(e){pug_rethrow(n,null,r)}var i=3,a=t.split("\n"),o=Math.max(r-i,0),h=Math.min(a.length,r+i),i=a.slice(o,h).map(function(n,e){var t=e+o+1;return(t==r?" > ":" ")+t+"| "+n}).join("\n");throw n.path=e,n.message=(e||"Pug")+":"+r+"\n"+i+"\n\n"+n.message,n}function test(locals) {var pug_html = "", pug_mixins = {}, pug_interp;var pug_debug_filename, pug_debug_line;try {;var locals_for_with = (locals || {});(function (a, b) {;pug_debug_line = 1;
pug_html = pug_html + "\u003Cdiv\u003E";
;pug_debug_line = 1;
pug_html = pug_html + "Wow: ";
;pug_debug_line = 1;
pug_html = pug_html + (pug_escape(null == (pug_interp = a) ? "" : pug_interp));
;pug_debug_line = 1;
pug_html = pug_html + (pug_escape(null == (pug_interp = b) ? "" : pug_interp)) + "\u003C\u002Fdiv\u003E";}.call(this,"a" in locals_for_with?locals_for_with.a:typeof a!=="undefined"?a:undefined,"b" in locals_for_with?locals_for_with.b:typeof b!=="undefined"?b:undefined));} catch (err) {pug_rethrow(err, pug_debug_filename, pug_debug_line);};return pug_html;}
// pug source: "div Wow: #{a}#{b}"
// this would obviously be much shorter if you include pug-runtime globally in your application
function createMarkup(a,b) {
return {__html: test({a:a,b:b})};
}
function MyComponent(props) {
return <div dangerouslySetInnerHTML={createMarkup(props.a, props.b)}/>;
}
ReactDOM.render(
<MyComponent a="banana" b="&patata"/>,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id=root />
Alternatively, there are attempts to translate jade or pug syntax into react directly, such as pug-react-compiler and babel-plugin-transform-pug-to-react. It seems they solved including further react components inside the pug template, which might be a desirable tradeoff for them possibly having quirks.

React components as instances like in OO programming

I created React component using React.creatClass ()
module.exports = React.createClass({ // input-field-units.jsx is the file name
displayName: 'input-field-units',
render: function () {
return (
<div >
<form className="form-inline" role="form">
<div className="implement-width-select">
<input id={inputid} type="number" className="form-control" onChange={this.onChangeTest}></input>
<div className="form-group">
<select id="implement-width-unit" className="form-control" defaultValue="m" onChange={this.onChangeTest} >
<option value="m" >m</option>
<option value="mm">mm</option>
<option value="ft">ft</option>
</select>
</div>
</div>
</form>
</div>
);
},
componentWillMount: function(){
inputid = this.props.inputid;
console.log("component: " + inputid);
},
onChangeTest: function(){
$(document).ready(function () {
var _unit = document.getElementById("implement-width-unit").value;
var _widthValue = document.getElementById(inputid).value;
//processing of code here..
});
I intend to call this component like objects in C# where values of properties are not shared if this is called several times. here inputid is set from this.props.inputid in componentWillMount()
Im using this component on several places in my application (distributed code is in a single file). In my .jsx file I am doing this
var InputFieldUnitsComponent = require('../Components/input-field-units.jsx');
var ImplementWidthID = "Implement-Width-ID", againWidthID = "again-width-id";
module.exports = React.createClass({
displayName: 'PathPlannerSidebarHeader',
render: function () {
return (
<div>
<h2 className="sidebar-header-subtitle">Implement Width</h2>
<InputFieldUnitsComponent
inputid= {ImplementWidthID} // 1st call
/>
<h2 className="sidebar-header-subtitle">again Width</h2>
<InputFieldUnitsComponent
inputid= {againWidthID}
/>
</div>
);
//....
})
so that everytime I have a new this.props.inputid to set id of
but the problem is this.props.inputid maintains same value change and hold the last value. eg in this case inputid will have "again-width-id" even when I want to use for 1st time I called the component.
In short I like OO behavior where the properties of objects are not shared with each other.
Please ask if this doesn't make sense I will explain
You essentially made inputid a global variable by not declaring it with var (or const or let).
You could say this.inputid in componentDidMount, but that doesn't make a lot of sense: why have the same value as both this.inputid and this.props.inputid
It's simpler to just use this.props.inputid consistently. If you want to simplify render(), define it as a local variable in there.
I suggest to install eslint and enable it in your editor to find this kind of errors.
You also need to update the function onChangeTest. It's not correct to try something like:
onChangeTest: function() {
$(document).ready(function () {
var _widthValue = document.getElementById(this.inputid).value;
});
}
onChangeTest is a method of your react class, but the anonymous function that you're passing to ready() isn't, and it can't refer to your react component via this... unless you bind it!
onChangeTest: function() {
$(document).ready(function () {
var _widthValue = document.getElementById(this.inputid).value;
}.bind(this));
}
or with the ES6 syntax:
onChangeTest: function() {
$(document).ready(() => {
var _widthValue = document.getElementById(this.inputid).value;
});
}
Obligatory reading: How does the "this" keyword work?

My Meteor app isn't returning data from the server via Pub Sub

I've completed the basic leaderboard app and read further documentation and finally decided to make my own app using this tutorial as a guide: http://meteorcapture.com/publishing-data-from-an-external-api/
my current code seems to work up until the point of passing data back to the client. I can't seem to get data from the server. Even though I have my subscribe and publish all set up.
I've cut down and simplified my code but to reduce points of error:
MyMp = new Mongo.Collection('mymp');
if (Meteor.isClient) {
Session.setDefault('searching', false);
Tracker.autorun(function(){
if(Session.get('postcode')){
var twfyHandle = Meteor.subscribe('twfySearch', Session.get('postcode'));
Session.set('searching', ! twfyHandle.ready());
}
});
Template.searchForm.events({
'submit form': function(event, template) {
event.preventDefault();
var postcode = template.$('input[type=text]').val();
if (postcode) {
Session.set('postcode', postcode);
}
}
});
Template.body.helpers({
mymp: function() {
return MyMp.find();
},
searching: function() {
return Session.get('searching');
}
});
}
if (Meteor.isServer) {
Meteor.publish('twfySearch', function(postcode){
console.log(postcode); // this received ok
var self = this;
var mp = {first_name: 'Test Name', party: 'Labour'}
self.added('mymp', Random.id(), mp);
self.ready();
});
}
Templates in my HTML file:
<body>
<h1>Get Details on your MP and Constituency</h1>
<h2>Enter your post code below</h2>
{{> searchForm }}
{{#if searching }}
<p>Searching...</p>
{{else}}
<div class="">
{{> twfyResults }}
</div>
{{/if}}
</body>
<template name="twfyResults">
{{ mp.first_name }}
</template>
<template name="searchForm">
<form>
<input type="text" name="postcode" id="postcode" />
<input type="submit" value="Search" />
</form>
</template>
I'm passing a postcode to the server and the server populates a basic JSON object 'mp' under a publish method and makes it ready().
This is where it fails. Although my console.log() calls show that the server is getting the postcode fine and creating the mp object. The client is not getting anything back!
UPDATE:
I have managed to manually run in the browser console MyMp.findOne() and it returns the object the server created. However, this object seems inaccesible to my template. Also the 'mp' object itself doesnt exist.
I've realised THREE errors in my code.
I assumed the template object used to access the data sent back had the same name on the frontend as it did in the server (mp). Instead I should have been trying to access the helper name "mymp".
This was fixed by changing the twfyResults template to reference the helper method:
<template name="twfyResults">
{{ mymp.first_name }}
</template>
My helper for the twfyResults was in the wrong context. So I rewrote my helpers like so:
Template.body.helpers({
searching: function() {
console.log(this);
return Session.get('searching');
}
});
Template.twfyResults.helpers({
mymp: function() {
return MyMp.findOne();
}
});
But the above alone wasn't enough. I also had to change the "mymp" helper to return just one result as in this case only one result would ever be returned. That way I could access my objects variables in the above way. So my helper was changed to findOne() instead of just find as seen above.

Categories