How to make "global" variable available in Laravel helper and view? - reactjs

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

Related

React: DOM manipulation from HTML response

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.

Usercontrol equivalent in html5

I am trying to find the equivalent of .Net usercontrol in the html5 SPA world. Does react component fall under this or angular directives. I want to create self contained sections of page made of multiple rendering components put together (grid,chart etc) that talks to a socket. And I want to reuse those by pointing to a different data source.
An example would be a chart and a grid (with underlying data) together with a button to flip between chart and grid. How will I make this as one component that can be used multiple times in same application and also shared to different applications.
Yes, You can use Reactjs or Angularjs, I would recommend Reactjs in case if the sections' state are changing many times, in reactjs every component have a state, Reactjs knows when to re-render the component because it is able to observe when this data changes. Dirty checking is slow regarding the reactjs.
In other word Reactjs can give you much better performance, but note that Reactjs is only for the UI, no routing or other like in Angularjs.
I hope that helps, good luck
I've done this exact thing with a simple combination of JS objects, jQuery and Handlebars. No need for Angular et al if you don't want them (and nothing wrong with them if you do). I've defined my components as JS Objects:
/**
* A data grid.
*/
function Grid(columns, $container) {
this.columns = $.extend([], columns);
...
this.render($container);
}
/**
* Render the grid into a container.
* #private
*/
Grid.prototype.render = function($container) {
Rendering the view is done with Handlebars:
<div class="grid">
<header>
<div class="label">
{{#each columns}}<div data-id="{{id}}" data-index="{{#index}}" style="width:{{boxWidth}}px">{{name}}</div><nav></nav>{{/each}}
</div>
The Model is obtained by ajax calls:
/**
* Handle a grid filter change.
* #private
*/
MasterCompanyList.prototype.onFilter = function(e, filter) {
App.loading.start();
App.ajax({
url: 'ws/admin/mcl',
data: filter.filter,
context: this,
success: function(json) {
this.grid.setData(json);
App.loading.done();
}
});
};
All at a simpler level than Angular etc (I've used Angular commercially for a few years), but I find this KISS approach gets me where I need to be without having to rely on a heavier framework - and if I want to manipulate something jQuery styles then off I go.
Angular and React can do this, but React is based on a component design, so it would be perfect for what you're trying to achieve.

Angular translate extend existing translations

I am trying to have external modules change my $translateProvider.translation on the main module. see this as a "tranlation plugin" for my app.
it seems like changing translations from the $translate service is not possible.
mymodule.service('MyService', function ($translateProvider) {
var lib = function () {
//EDITED FOR BREVITY
this._registerTranslations = function (ctrl) {
if (!ctrl.i18n) return;
for (var name in ctrl.i18n) {
/////////////////////////////
// THIS IS THE PLACE, OBVIOUSLY PROVIDER IS NOT AVAILABLE!!!!
$translateProvider.translations(name, ctrl.i18n[name]);
//////////////////////////////
}
};
//EDITED FOR BREVITY
};
return new lib();
});
anyone with a bright idea?
So, to answer your question: there's no way to extend existing translations during runtime with $translate service without using asynchronous loading. I wonder why you want to do that anyway, because adding translations in such a way means that they are already there (otherwise you would obviously use asynchronous loading).
Have a look at the Asynchronous loading page. You can create a factory that will load a translation from wherever you want.
I created an Angular constant to hold new translations. If I want to add a new translation, I add it to the constant. Then in my custom loader, I first check the constant to see if the translation exists (either a new one, or an updated one). If so, I load it from the constant. If not, I load it from a .json file (or wherever you load your initial translations from). Use $translate.refresh() to force translations to be reloaded and reevaluated.
Demo here
The demo is pretty simple. You would need to do a little more work if you wanted to just change a subset of the translations, but you get the general idea.
From the AngularJS docs (https://docs.angularjs.org/guide/providers):
You should use the Provider recipe only when you want to expose an API for application-wide configuration that must be made before the application starts. This is usually interesting only for reusable services whose behavior might need to vary slightly between applications.
Providers are to be used with the application's .config function. $translateProvider for configuration, $translate for other services and controllers.

Backbone Marionette modules as Widgets similar to Twitter Flight

I'm reading up in choosing the correct client-side framework to segment/modularize my frontend code in Widgets.
Basically what I have/want is:
a complex website with multiple pagetypes, so no single-page application.
all pages are able to render a complete page WITHOUT the use of javascript. IOW: javascript is used as enrichment only.
Lots of pages have a very dynamic way in which widgets can be shown on screen. To overcome complexity at the server-side I've modularized my code into widgets (composite pattern), where each widget is responsible for it's own:
server-side controller code
server-side templating (using hogan/mustache)
routing endpoints, should it need to be called from the client
structural css (css converning the structure of the widget as opposed to the look&feel)
a server-side RegionManager ultimately decides which widgets are rendered and where they are rendered on screen. Endresults is that the RegionManager spits out the entire html (server-generated) as the composite of the rendering of all of it's widgets.
Now, some of these widgets DO have client-side logic and need rerendering on the client. Take a searchpage for instance, which needs to be able to update through ajax. (I've described this process, which uses DRY templating on client and server, here)
What I ultimately want is that, given I already use the composite pattern on the server, to extend this to the client somehow so that a Widget (1 particular logic block on the screen) contains all mentioned server-side code, plus all needed client-side code.
I hope this makes sense.
Would Marionette be suited to be used as a client side framework in this scenario? I'm asking since I'm not 100% sure if the concept of a Marionette Module is what I describe as being a Widget in above scenario. (I'm mentioning Twitter Flight in my question, since I believe this would be a fit, but it currently is so new that I'm hesitant to go with it at the moment_
I think basically what I'm asking is if anybody has some experience doing something along these lines.
I think just using Backbone.js is perfect for this type of application you are describing. You have probably already read this, but most of the backbone literature is focused around your views having associated server generated JSON models and collections, then using the View's render function to generate (on the client) the HTML UI that represents the model/collection.
However it doesn't have to be used this way. In fact there is nothing stopping you attaching views to existing elements that contain content already, which gives you all of the benefits of Backbone's modularity, events system and so on. I often use views that have no model or collection, purely because I like the conformity of style. I have also used an approach like I describe below in the cases where I have had to work with older, existing applications that have not yet got, or never will have a nice REST API, but they do provide content in HTML.
Firstly, lets assume the following HTML represents one of your widgets:
<div id="widget">
<div class="widget-title"></div>
<div class="widget-body">
<!-- assume lots more html is in here -->
Do something!
</div>
</div>
In this case, you could use backbone with a single Widget Model. This would be a very simple model, like this:
App.WidgetModel = Backbone.Model.extend({
intialize: function () {
this.url = this.options.url;
}
});
Take note of the fact the Widget receives a URL as a parameter to its constructor/initialize function. This widget model would represent many of your widgets (and of course you could adopt this general approach with more complicated models and pluck different data from the rendered HTML). So next for your views. As you probably know, normally you pass most views a model or collection when you instantiate them. However in this case, you could create the Widget model in your View's initialize Function and pass it a URL from the pre-rendered HTML as follows:
App.WidgetView = App.View.ComboboxView = Backbone.View.extend({
initialize: function () {
this.model = new App.WidgetModel({}, { url: this.$("a").attr("href") });
}
// rest of the view code
});
So instantiating the view would be something like:
new App.WidgetView({el: $("#widget")})'
By doing all of the above you can do pretty much everything else that backbone offers you and its modular and encapsulated nicely, which is what you are after.
The end result of this whole approach is:
You have rendered the Widget UI as pure HTML which (I assume) is functional without JavaScript.
You attach a View to the existing HTML.
You pass into the View as options, content by extracted (such as a URL) from the rendered HTML with jQuery.
The View is responsible for instantiating the Model passing on the relevant options the model needs (such as a URL).
This means all dynamic server side content is intially contained in the rendered HTML and your View is a modular JavaScript component that can do stuff to it, which I think is the end result you're after.
So you mentioned that you would like to have AJAX functionality for your widgets and that fine with this approach too. Using this approach, you can now use the standard Backbone fetch and save functions on the Widget model to get new content. In this example it is from the URL retrieved from the rendered HTML. When you get the response, you can use the view's, render function, or other finer grained functions to update the HTML on the page as required.
A few points:
The only thing to look out for is that you'll need to change the content type of the fetch and save functions to "text/html" if that's what the server is providing. For example:
this.model.fetch({
type: "POST",
contentType: "text/html"
});
Lastly, the model I have proposed is instantiated with no content. However if your ajax calls are a content type of "text/html", you may need to play around with you model so it can store this content in its attributes collection properly. See this answer for more information.

CakePHP - Render a view that is actually plugin's view from Component

Morning guys,
So this is my first time developing a plugin for CakePHP. Here's what I am doing in startUp of the component.
//component
function startUp(&$controller){
//....
if($render){
$controller->render("return", "ajax");
}
}
By default render will look at app/views/<controllers>/return.ctp and app/views/layouts/ajax for this render call.
Is there anyway that I can give a directive to render from app/my_plugin/views/awesome_stuffs/return.ctp and app/my_plugin/views/layout/ajax.ctp instead?
I believe the third param of Controller::render($file, $layout, $file) could do the job, but is there any better Cake way of doing things?
Plus, is that considered a good practice to take over controller's rendering function like that?
One way is to call the PLUGIN controller/action URL in your AJAX call, instead of the main app controller/action URL.
ex:
instead of:
http://domain.com/controller/action
you call:
http://domain.com/my_plugin/controller/action
When you do it this way, the plugin views & layouts are called automagically. See:
http://book.cakephp.org/view/1118/Plugin-Tips
http://book.cakephp.org/view/1115/Plugin-Views
Otherwise, the only way I know of is manually setting paths as you mentioned or controller-wide via:
var $viewPath = 'path/to/plugin/views/';
var $layoutPath = 'path/to/plugin/layouts/';
You might want to try setting $this->view to the plugin dotted view file you want to render.
add to your source
$controller->plugin = "pluginname";

Resources