I am working on a ReactJS app for a client. I want the client to be able to customize some of the configurations and templates in the app. So I have created a config.js file
window.APP_CONFIG = {
url: 'example.com',
template: {
item: '
<div>
<h3>A new item title</h3>
{ item.description }
</div>
'
}
};
How can I use this variable in my react app? I have tried the following but it gives me errors.
var app = React.createClass({
render(){
return (
<div>
{APP_CONFIG.template.item}
</div>
)
}
})
Anyone has done something similar?
You are not alone. React Templates allows you to use react without having to put up with inline jsx.
The react-templates syntax is very very similar to jsx but allows you to have your component html code in a separate .rt file - much cleaner. RT files have better support for looping and branching. Added bonus, you do not have to dance around reserved attribute names like "class" and "for" and there are other advantages.
From the docs:
Why not use JSX?
Some love JSX, some don't. We don't. More specifically, it seems to us
that JSX is only a good fit for components with very little HTML
inside. And this can be accomplished by creating DOM elements in code.
Also, we like to separate code and HTML because it just feels right.
The main gotcha I've seen so far is that attribute values like onMouseDown={someJsExpr} are magically quoted in jsx but in react-templates you have to explicitly quote them a la onMouseDown="{someJsExpr}", no magic. I found that it was not hard to find cases of this and convert them over.
Related
I have an API which returns a whole block of HTML. In old project (JS, jQuery... not React) I had a bunch of jQuery events declared after I got this HTML so all the clicks were working fine.
The API returns some classes and ids like this:
<div id="content">
<span class="span">Span 1</span><span class="span">Span 2</span>
</div>
And simple events:
$('.span').click(function (e) {
$(this).text('Span clicked');
});
Obviously it's not that simple and it fills an entire page with different DOM events. Convert it to States is not that simple unless I modify completely the response and this is something that cannot be modified from backend (and from frontend it's too much).
What's the best way in React of doing this without copying the whole jQuery events?
(I don't find it clean)
There are more questions to be asked here but main response will be always to not bring jQuery to React.
As mentioned, there are some situations where we are sitting on the fence and we cannot wait here for a backend refactor.
Since I have React running on a old platform using JS + jQuery but I have done is move my jQuery logic to something global like this:
// NOTE: temporary function until API is refactored returning a json
window.runOldJquery = function () {
$('.span').click(function (e) {
$(this).text('Span clicked');
});
}
So in React, I call my API and when it successes I call window.runOldJquery.
This is the best solution I could find for this specific scenario without bringing nothing to React.
I'm working on a Laravel app that I'm transitioning to React using a microfrontend(-like?) approach. I defined a helper function in Laravel that receives a component name and array of props and outputs the server-side rendered React HTML. I then call this function inside my views where needed.
Each page in my application defines a few variables that might affect the rendering both of these React components and also the Blade templates. So I define the variables in the view and send them to JavaScript land through a global window variable. But I also need these variables in the SSR helper.
Right now I have two ideas on how to do this:
Pass the variables as props in each call to my helper function. I want to avoid this as the variables will never change throughout the request lifecycle and all the #includes and #extends
Set the values using config helper before rendering the view. This seems kind of unidiomatic, as I think config should be used with more "static" values (that apply for the whole application and not particular pages), but I'm not very well versed in Laravel-world so this might actually be acceptable.
So right now I'm more inclined to 2, but I was wondering if there was a better option?
Some code:
my-template.blade.php
//these are the variables that I want to access in my helper
#extends('master.index', [
"_PAGE_CONFIG" => [
"page" => "blog",
"header" => ["search" => true]
]
]);
#section('content')
#include('some-template.that-also-has.ssr-components-in-it')
{!! ssr('blog/Blog', ["posts" => $posts]) !!}
#endsection
master/index.blade.php
<body>
#if($_PAGE_CONFIG["header"])
<header>{!! ssr('header/Header') !!}</header>
#endif
#yield('content')
<script>
//here I pass my variables to (client) JS land
window._PAGE_CONFIG = #json($_PAGE_CONFIG);
</script>
</body>
my-SSR-helper.php
function ssr($component, $props = []) {
/*
here I call a node server or script that handles the rendering for me,
but I want to pass it $_PAGE_CONFIG, which will be different in each page.
I could pass it in each ssr call in the template but this is what I want to avoid
as this function might be called several times down the #include/#extend chain
and $_PAGE_CONFIG never changes in any one page
(but might be different for different pages).
*/
}
If each page always gets the same values, then you should set an array somewhere into your config where the keys are the pages and the values are the props received. By the way, you would get:
function ssr($component, $page, $props = []) {
$pageProps = config('somewhere.'.$page);
}
I'm glad to see people using micro-frontends to use React to server-side rendering in a non-javascript framework like Laravel.
I recommend you take a look at this article Universal Rendering in Laravel using Vue.js and Ara Framework. Ara Framework supports also React, I think the server-side include approach is the best option to overcome the limitations of Blade.
This article can give you context about how this approach works.
https://itnext.io/strangling-a-monolith-to-micro-frontends-decoupling-presentation-layer-18a33ddf591b
I've gotten used to using slim and jade and I recently moved to writing applications on the front end with React and find that now I'm writing bloated HTML again inside my components. I'm currently using Ruby on Rails and .jsx files with babel, etc using:
gem 'react-rails', '~> 1.4.0'
gem 'react-router-rails', '~>0.13.3.2'
But I'm also using React with node and express using the react-starterify boilerplate and it's the same story with Node.
Is there anything that can allow me to start writing my html in React using a syntax like slim or Jade?
One thing to keep in mind is that JSX isn't HTML—it just looks like it. This is important because the JSX transpiler (usually Babel these days) takes the JSX syntax and modifies it from this:
<div className="container">
<p>Testing!</p>
</div>
to something like this:
React.createElement("div", { className: "container" },
React.createElement("p", null, "Testing!")
)
By abstracting over React.createElement calls, you can end up with a project like r-dom, which does the same thing but with a nicer syntax:
r.div({className: 'container'}, [
r.p('Testing!'),
])
or react-hyperscript, which allows an alternative syntax for some properties:
h('div.container', [
h('p', 'Testing!')
])
However, since JSX turns into plain JS calls, any language or syntax that can be converted into React.createElement calls will work great with React, as long as you set up your Rails asset pipeline so that it correctly does the transpilation as part of serving assets.
There is a project that does this with Jade's syntax called react-jade; there are a few differences from regular Jade and some unsupported features, but it may do what you want it to do. In a Rails project, you'd need to find or create a preprocessor that turns the appropriate Jade code into React-specific JS.
There's one other thing I wanted to mention based on a comment in your question:
now I'm writing bloated HTML again inside my components
If you're just talking about HTML's syntax then no problem, but if you're finding that the render methods of your React components are getting large and hard to manage, then it's probably a good sign that you need to break your component into smaller components (see "Tip 4: Embrace composition!" at this link).
I need to implement a browser-like banner warning system so that the user can only access certain features after they've acknowledged certain warnings. The warning messages will depend on data sent from the server and I have to implement different styles and links.
Edit: There could be any number of warnings displayed at the same time. One for each feature. The user must individually acknowledge each warning before the corresponding feature is enabled. Some of the warning texts are static, some are dynamic. Some can have different acknowledge links instead of the standard "okay".
Traditionally, I would package each kind of warning into a class (in the OO sense) and push them to screen through a centralized method, e.g.
displayWarning(new InfoBanner("You must ... before you can modify " + data.name, function onclick() { ... }));
Here the InfoBanner class will have a method that creates the banner elements and attach the event handler.
The Angular way of doing this, on the other hand, seems to be you write the banner entirely in HTML with ng-if and ng-click, etc. E.g.:
<p style="info banner" ng-if="...">You must ... before you can modify {{...}}. <a href ng-click="...">Okay</a></p>
However, this seems quite unfocused and messy because there will now be a large blob of banner code dwarfing the functional part of the page. (There are hundreds of error types defined!)
Is there any way to resolve this without reverting to the fully imperative code?
(Note: a custom directive is probably not the answer as <p style="info banner" is almost like a directive and there's little sharable code among these warnings beyond this.)
(Edit: One can see this question in another way: in the imperative world, the warning-adding logic are scattered in the code but close to the feature they're protecting, so they're easy to understand and maintain. In the declarative world, they must be centralized to the place where they're displayed. I would like a solution where they're declared close to the component they're protecting but displayed centrally.)
What I understand from your question your problem is that since you are in an Angular application you need / should include your banners as markup in your HTML view, since however whether each banner is displayed or not depends on the data you are getting from the server you only know if a specific banner should be displayed in your controller (hence the ng-if you have included in your banner HTML example).
What I would propose in this case would be to create a BannerService which would hold a list of all the banners that should be displayed at any given time. In your controller you can use the functions exposed by the service to add banners to the list when the data you got from the server indicates that you should do so. Each banner in the list would be an object containing all the information that might be different between different banners (ex. banner text, type, etc.) meaning that your HTML view doesn't really need to "know" anything about specific banner details and can just display all the banners available in the BannerService using an ng-repeat.
You can see below a quick-and-dirty example to better understand how this would work.
var app = angular.module('TestApp', [])
app.service('BannerService', [function(){
var banners = [];
this.getBanners = function() {
return banners;
}
this.addBanner = function(banner) {
banners.push(banner);
}
}])
app.controller('TestCtrl', ['BannerService', function(BannerService) {
// Add banners to the banner service depending on your data etc.
BannerService.addBanner({text: "This is a banner", type: "info"});
}])
app.controller('BannerCtrl', ['$scope', 'BannerService', function($scope, BannerService) {
$scope.banners = []
$scope.$watch('BannerService.getBanners', function (newVal, oldVal, scope) {
$scope.banners = BannerService.getBanners();
});
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="TestApp">
<div ng-controller="BannerCtrl">
<div ng-repeat="banner in banners"><p class="banner {{banner.type}}">{{banner.text}}</p></div>
</div>
<div ng-controller="TestCtrl">Page content</div>
</div>
In my opinion what you are looking to implement fits perfectly with Aspect Oriented Programming. If you've never used AOP before be prepared for some light reading. The concept is simple and works very well with Angular's patterns. There is an AngularAOP project, but before you dive into it I suggest running through this article first:
http://www.bennadel.com/blog/2425-decorating-scope-methods-in-the-angularjs-prototype-chain.htm
Basically i would like to render the title element into the head element like this
<head>
<HeadFragment />
</head>
this is only possible at the moment by actually rendering as the component, but this will cause all kinds of issues if you use external scripts that inject into the head.
i basically want
var HeadFragment = React.createClass({
render: function () {
return (<fragment>
<title>{this.props.title}</title>
... meta ...
... styles ...
... scripts ...
</fragment>)
}
});
React.render(<HeadFragment />, document.querySelector('head'));
but the fragment node should not be an actual DOM node, instead it would be a document fragment.
without this support it makes full page rendering pretty much impossible, and forces us to do a bunch of other stuff in order to modify things like this without breaking the HTML spec.
Modifying document fragments like the whole html container, head, and such are just completely undependable. There's been a lot of chatter about this in the past like here: https://groups.google.com/forum/#!topic/reactjs/4jI5xe7TXzQ and on GH and such.
There are projects like https://github.com/matthewwithanm/react-frozenhead which try to do the correct updates based on what you provide, like changing the title using document.setTitle and such, but yeah.