load js depending on url - node.js

I'm sure this question has been asked many a time before but I am having zero luck finding my 'question'.
I'm trying to figure out what the best solution is to loading a js file dynamically depending on the page/url. Should I handle this in controllers?
Am I right in assuming I should be using something like Browserify or requireJS? I do not understand how they are used in this manner, though...
I'm using Expressjs
I had something like this...
assets = {
js: {
validation: 'path/to/file'
}
}
route/controller
res.render('example', {
scripts: [assets.js.validation]
}
)
view (using a view engine helper)
just an example of logic
each script in scripts
<script src="{[script}}">
This works but I'm wondering what's best practice.

Related

How to create a static website generator in React

Tech stack - Node.js, MongoDB for the database, Strapi CMS for editing and API, React - my application.
I have a database with a long list of entries and a ready-to-use application that allows users to read data from the database. I need to be able to generate a simple website with a single entity from my database as a source to fill the template.
Mockup
Here is a mock-up. Hopefully, it will make things a bit clearer.
Clarification
After a day of thinking about the task, I believe I need something like a simplest static website generator - an application that will allow me to select a single bit of data from the list and generate a small website filled with it. The end goal is to get a website in some subfolder of my application where I can get it and use it however I need.
A bit more about specifics:
It will be used locally
Security can be neglected
Running always in development is not a problem (just in case, thinking about additional question #2)
Few additional questions:
Is it possible to run NPM scripts from the application (like npm build)
Is there any way to show one component in development mode, but replace it with another during building for production?
App.js
//...
function App() {
if() {
return <AdminUI /> // This one is to be shown in development mode
} else {
return <Website /> // This one is to be used instead of AdminUI in the build
}
UPDATE
Well, I'm digging a path to create a site generator and so far I come up with the following basic plan:
Get my template ready
Create a new directory for my website
Copy a template to the new folder
Get an HTML file, parse it to a string to modify
Swap some bits with my data
Save to a file from the modified string
repeat if needed for other files.
If that works as expected, the whole process probably might be improved by moving from a fixed template to a component, that will be prepared with a JavaScript bundler and started with the help of something like node-cmd (to run shell commands from my application)...
What you want could be achievable, but if it's just a string and little else, I'd say it's much simpler to fetch the data at startup from a given file, and populate from there. You can put a JSON file under the public folder (together with other static data, like images) and have the file being your configuration.
In the App.js file, write an async componentDidMount() and you can do an await axios.get("") with your configuration.
So App.js would look like (code written on the fly, didn't check in an IDE):
export class App extends React.App {
constructor(props) {
super(props);
this.state = { loading: true, };
}
async componentDidMount() {
const response = await axios.get("your/data.json");
this.setState({ loading: false, ... whatever})
}
render = () => (
<>
(this.state.loading && <div>Still loading...</div>)
(this.state.adminData && <AdminUI data={this.state.admingData} />)
(this.state.devData && <Website data={this.state.devData} />)
</>
)
}
If you don't care about security, wouldn't be much simpler like this? And if you use TypeScript you'll have a much much simpler life in handling the data too.
Maybe it's worth doing an AdminUI to generate the JSON, and the another UI which reads the JSON, so you end up doing two UIs. The template-generated UI could even ask for a JSON file to bootstrap directly to the user, if it simplifies... In general, an approach based on simple JSON sounds a lost simpler than going for a CI/CD pipeline.

How to use Magento2 with RequireJS?

I'm just migrating from Magento 1.x to Magento 2.x. I found that Magento2 uses RequireJS for handling JavaScript files. So I learnt what RequireJS is, and how to use it.
I found that most of the examples uses data-main="main" to define the configuration file.
In Magento2's default_head_blocks.xml file, I found the script tag like this:
<script src="requirejs/require.js"/>
Here they did not specify any data-main.
These are my questions:
How Magento2/RequireJS knows which JS should be loaded for configuration? (I found requirejs-config.js for this in multiple places)
By default Magento2 loads lots of JS (more than 20), how can I limit them?
I could not find enough documentation on this.
The best place to get all your answers for Magento 2 JS development is the Magento 2 docs, it really is a useful resource on this. http://devdocs.magento.com/guides/v2.0/javascript-dev-guide/javascript/js_init.html explains in detail about component initialisation.
To answer your two questions above -
Q.1. How Magento2/RequireJS knows which JS should be loaded for
configuration? (I found requirejs-config.js for this in multiple
places)
In each Magento 2 module there is a requirejs-config.js file to load all that modules configuration. i.e.
var config = {
map: {
'*': {
compareItems: 'Magento_Catalog/js/compare',
compareList: 'Magento_Catalog/js/list',
relatedProducts: 'Magento_Catalog/js/related-products',
upsellProducts: 'Magento_Catalog/js/upsell-products',
productListToolbarForm: 'Magento_Catalog/js/product/list/toolbar',
catalogGallery: 'Magento_Catalog/js/gallery',
priceBox: 'Magento_Catalog/js/price-box',
priceOptionDate: 'Magento_Catalog/js/price-option-date',
priceOptionFile: 'Magento_Catalog/js/price-option-file',
priceOptions: 'Magento_Catalog/js/price-options',
priceUtils: 'Magento_Catalog/js/price-utils',
catalogAddToCart: 'Magento_Catalog/js/catalog-add-to-cart'
}
}
};
This is telling requirejs where all the required JavaScript files are located.
There are multiple way to tell Magento when to use your JS file -
data-mage-init on a HTML element. e.g. <div class="block upsell" data-mage-init="{"upsellProducts":{}}" data-limit="0" data-shuffle="0">
script tag on the page e.g
<script type="text/x-magento-init">
{
"[data-role=tocart-form], .form.map.checkout": {
"catalogAddToCart": {}
}
}
</script>
within a JS file e.g. $('.yourSelector').yourPlugin();
Q.2. By default Magento2 loads lot's of JS (more than 20), how can I
limit them?
The sheer number of JS files that are loaded as a result of multiple modules is one of the downsides, however, with the correct usage of full page caching with a reverse proxy like Varnish the performance reduction is negligible, even in a development server.

How to set active links (navbar) in an async ufront app

I try to figure out how to set active links in a navbar or sitebar for an async ufront application.
On the server I can load and parse it dynamically inside the main (top level) controller via an api call like:
#inject public function init(context:HttpContext) {
ufTrace("HomeController::init");
var navStr:String = "";
//getNavbar loads the navbar html snippet and parses the code to set 'active' some tags in relation to the request uri
var navbarSurprise = siteApi.getNavbar(context.request.uri);
navbarSurprise.handle(function (outcome) {
switch (outcome) {
case Success(navbarStr): navStr = navbarStr;
case Failure(err): navStr = "<h1>Could not load navigation: $err</h1>";
}
} );
ViewResult.globalValues["navBar"] = navStr;
}
but that doesn't work on the client for pushstate urls. (navStr would always be empty)
The ViewResult.hx (line:126) doc states:
Helpers (dynamic functions) can be included in your ViewResult also.
Could this be a place to handle that?
But unfortunately I couldn't find any help/examples how to add helper functions to a ViewResult.
I was also thinking about doing it in a custom ViewEngine. But that seems a bit like overcomplicating things.
Any thoughts about that would be appreciated.
Seems your are looking to render the navbar on the server when processing the request.
I did something like that some time ago by using sipple (another templating engine) but you can also use other engine (i think) like haxe template or erazor etc.
This issue sums up how i processed different partials using stipple
Hope it helps.

Routing in locomotive using ejs

I'm trying out node and some frameworks for node atm, specifically locomotive. However, i seem to be stuck on routing using locomotive. A couple questions i can't find the answer to, so here goes:
why does the locomotive out-of-box install use index.html.ejs as a
filename? Why not just index.ejs? What's the benefit?
i'm trying to add a route to a view: searchName.html.ejs which i
added in the views folder. To achieve this i made a toolController
like this:
var locomotive = require('locomotive').Controller,
toolController = new Controller();
toolController.searchName = function() {
this.render();
}
module.exports = toolController;
I also added a route in routes.js like so:
this.match('searchName', 'tool#searchName');
However, that doesn't work (and yet it's what the documentation says ought to work). The result is a 404 error. So how do i make that route work?
Suppose i want to make a route to eg, anExample.html? How do i go
about that? I notice that in the out-of-the-box app from
locomotive, you cannot enter localhost:3000/index.html . Nor even
localhost:3000/index This seems highly impractical to me, as there
are plenty of users who'll add the specific page they want to go to.
So how can i make that work?
PS: I went through all questions regarding this on stackoverflow and searched the web, but i still can't figure this out.enter code here
The benefit is that this naming scheme allows you to specify several different formats for a single route. So you could have search_name.html.ejs and search_name.xml.ejs, then respond with either view depending on what your client is expecting.
There are a couple issues with the example code you posted. You should be seeing a more descriptive error than a 404, so I'm not sure what's happening there, but here are the fixes to your code that work in my environment.
In the controller:
//tool_controller.js
var locomotive = require('locomotive');
var toolController = new locomotive.Controller();
toolController.searchName = function() {
this.render();
};
module.exports = toolController;
In routes.js:
//routes.js
module.exports = function routes()
{
this.match('searchName', 'tool#searchName');
}
Then, you'll need to change the view to this: views/tool/search_name.html.ejs. It's not clear from the documentation, but locomotive automatically lowercases and underscores actions that are camel-cased, like searchName.
Now start the app and browse to http://localhost:3000/searchName
If you just want to serve a static html file, the easiest way is to just drop it in the public folder. This folder is specifically for serving up static content like client-side js, css, etc. And it works just fine for serving static HTML as well.

Get return value of `include` in jade template

What I basically try to accomplish is to re-use jade partials/templates when getting data through a socket connection. Non working example:
socket.on('company_created', function(company) {
var html = include _company;
$('#companies ul').append(html);
});
Normally I had to create a new li and set the content like so (which is working as expected):
$('#companies ul').append($('<li>').text(company.name));
This is okay for a simple list, but if I had complexer list and stuff, this could get messy pretty quick, plus I had to write plain HTML again, so I figured re-using my already existing jade templates with all their goodness would be awesome, but had not luck, yet.
Any clue?
PS: Please do not tell my to use Ember, Backbone, Derby, Meteor, Angular or whatsoever.
Thanks in advance!
You can compile your jade sources to JS with jade.compile. Then include these sources in the client-side javascript, include jade's runtime.min.js, and refer to your jade templates as to normal JS functions in your client-side code.
For example,
server.js
app.get('/templates/:template.js', function (req, res) {
var template = req.params.template;
response.end([
"window.templates = window.templates || {};",
"window.templates[\"" + template + "\"] = " + jade.compile(template + ".jade", { client: true; });
].join("\r\n"));
});
client.js
$(function() { $("#placeholder").html(window.templates["content"]({user: "Daniel" })); });
content.jade
h1: Hello #{user}!
index.jade
!!!
html
head
script(src='/lib/jquery/jquery.js')
script(src='/lib/jade/runtime.min.js')
script(src='/templates/content.js')
script(src='/scripts/client.js')
body
#placeholder
Note that the code above might be syntactically incorrect and is provided solely to illustrate the idea.
we have a build step that compiles them to functions sort of like penartur mentioned. I dont use extend or include (which dont work on the client anyway ATM), but personally I find we have absolutely no need for that on the client at all since the DOM provides all the separation we need.

Resources