I have a route for different pages of store profiles in my Node app. Been googling, and I know I can edit the parameters, but I have absolutely no idea how to change the content (loading with React) based on name. I'm just using res.render() to render the specific page.
For example, https://www.instagram.com/adidas shows different content than https://www.instagram.com/nike however, they have the same HTML structure and base profile HTML. It's just the content that is different!
Right now, I just have an store.ejs that is the html structure of each profile.
What's an example for how to change and load content based on the name/id?
Route parameters is probably what you're looking for.
app.get('/:name', (request, response) => {
let locals = {
name: request.params.name,
foo: 'bar'
};
res.render('store.ejs', locals);
});
The local variables can be referenced within your template file.
<h1>Hello <%= name %></h1>
<button><%= foo %></button>
Then navigating to /rohit is going to render a page that contains the following markup.
<h1>Hello rohit</h1>
<button>bar</button>
That said, there are many other ways to build application routes, so it's definitely worth reading the routing docs.
Related
I am building a simple Ember app, but I have run into difficulty passing an action closure to a child component when that component is rendered in the {{outlet}} of a navigable container.
For context, here is a quick look at the aesthetically-astonishing app I have been building:
I have a roles/role path that displays a component (the yellow section above) with the following markup. Note that the model for this component is an instance of a Role:
// file: app/components/role.hbs
<p>{{#role.name}}</p>
<div>
{{sel-nav-tabs items=this.tabConfig}}
<div class='route-content'>{{outlet}}</div>
</div>
(Where "sel" stands for "someone else's library".)
this.tabConfig is defines in the corresponding class:
// file: app/components.role.js
import Component from '#glimmer/component';
export default class RoleComponent extends Component {
get tabConfig() {
return [
{ label: 'Users', route: 'roles.role.users' },
{ label: 'Privileges', route: 'roles.role.privileges' },
];
}
}
Into the outlet in role.hbs will be rendered the appropriate list component, either users or privileges.
The users list is rendered by the following component. Note that the model is the list of User instances associated with the Role from its parent:
// file: app/components/role/user-list.hbs
<ul>
{{#each #users as |user|}}
<li>
{{user.name}}
{{#sel-button type="toolbar" onActivate=this.removeUser}}
{{sel-icon/remove-circle}}
{{/sel-button}}
</li>
{{/each}}
</ul>
and when the button is clicked it calls an action defined in the RoleUserListComponent class:
// file: app/components/role/user-list.js
import Component from '#glimmer/component';
import { action } from "#ember/object";
export default class RoleUserListComponent extends Component {
#action removeUser(user) {
// remove the user model from the role... but which role?
}
}
The catch is that the relationship between users and roles is many-to-many, so I can't simply unset the user's owner and let Ember Data take care of things. The obvious answer seemed like passing an action closure from the role component to its child user-list component.
Except, there seems to be no way to pass the action closure through the {{outlet}}. What I was hoping for was something like:
{{outlet onActivate=(action removeUser #role)}}
which would pass the closure to any component that was rendered there. I tried instead to use {{yield user}} in the child to let the parent render the delete button and give it the appropriate action, but that also hit the outlet wall.
I also tried to use controllers, which aren't documented that well, probably since their role seems to have been evolving dramatically over Ember's maturation. But while this brief explanation does mention passing down actions, it doesn't go into details, and the few up-to-date examples I found all seem to break when an outlet joins the party.
I'm suspecting that {{outlet}} just plain isn't closure-friendly.
While defining a service would probably work, that doesn't seem to be what services are intended for, and I'd be cluttering up my global space to solve a local problem.
What is the best practice (or, really, any practice) for dealing with getting messages through outlets? I looked for ways to query the earlier parts of the path, but I didn't find any that were defined in the relevant classes.
EDIT to add more detail:
The route template for /roles/role is simply:
// file app/templates/roles/role
{{role role=#model}}
Where the Role component is in the first listing above. (I also added the role.js file contents above.) My reasoning for doing that was that by making a component I created a logical place to put the config (rather than inline helper functions) and it just gave me a sense of tidiness to have all ui elements be in components.
If a refactor can be the anchor to a good solution (essentially copying the entire Role component into the route template), however, I'll happily do it.
{{outlet}} only supports one optional string argument for a named outlet and nothing else, so you won't be able to achieve this through the use of {{outlet}}!
I'm working on a React frontend that gets data from a python JSON API. One section of my website has premium content and is reserved for paying users; currently I ensure that other users don't have access to it by fetching the content as a JSON object and converting it to JSX on the frontend according to a certain convention. For example:
{
{ 'type': 'paragraph', 'value': 'some text'},
{ 'type': 'anchor', 'href': 'some url', 'value': 'some description'}
}
would be rendered as :
<p>some text</p>
some description
Not surprisingly, things started to get pretty complicated as the content began to get more structured, simple things like making part of the text bold require a disproportional amount of effort.
As a potential solution, I had this idea: instead of sending the content as an object and parsing it, why not send a string of JSX and evaluate it on the frontend?
I started like this:
import * as babel from "#babel/standalone";
export function renderFromString(code) {
const transformed_code = babel.transform(code, {
plugins: ["transform-react-jsx"]
}).code;
return eval(transformed_code);
}
I imported this function in my premiumContent page and tried passing a complete component as a string (with import statements, etc) but got errors because the modules can't be found. I assumed this happens because the code is being interpreted by the browser so it doesn't have access to node_modules?
As a workaround, I tried passing only the tags to renderFromString and call it in the context of my component where all the modules are already imported :
import * as babel from "#babel/standalone";
export function renderFromString(code, context) {
const _es5_code = babel.transform(code, {
plugins: ["transform-react-jsx"]
}).code;
return function() {
return eval(_es5_code);
}.call(context);
}
This also failed, because it seems that eval will still run from the local context.
Finally, I tried doing the same as above but executing eval directly in my component, instead of from my function .This works as a long as I store "React" in a variable : import ReactModule from "react";const React = ReactModule, otherwise it can't be found.
My questions are:
Is there any way I can make my first two approaches work?
I know eval is considered harmful, but since the content is always completely static and comes from my own server, I don't see how this wouldn't be safe. Am I wrong?
Is there a better solution for my problem? That is, a way to safely deliver structured content to only some users without changing my single page app + JSON api setup?
The best solution for this is React server-side rendering.
Since you need markup that is client-side compatible but at the same time dynamically generated through React, you can offload the markup generation to the server. The server would then send the rendered HTML to the client for immediate display.
Here's a good article about React SSR and how it can benefit performance.
I am new-ish to jade templates and some of the more complex feature that exist in the templates. As of right now, I am building a mock portfolio site using node and jade. I have a layout that is inherited by all other jade files. The layout has all of the universal site navigation inside of it. I am wanting to highlight what section of the website the user is currently in. I am currently doing this by having a unique js files that will load when the user is in a particular section. This js file will add a class on to the list item in which will add the highlighting to the navigation.
My question, do I need to create and load these js files in order to accomplish this? Is there a better way to get a universal navigation in one template while having a unique highlighter?
If the highlighting does not have functional purposes (e.g. accessibility), or it's done using URI fragments (hashtags), it might be better to leave it to the JS.
If your navigation highlight is per page, you could set it up in nodejs/jade, for example:
app.get('/about', (req, res) => {
var locals = {};
locals.navHighlight = 'about';
res.render('about', locals);
});
And in your jade template you could do something like this:
ul.navigation
li(class=(navHighlight=== 'about' ? 'highlight' : '')) About Me
li(class=(navHighlight=== 'blog' ? 'highlight' : '')) My Blog
Whatever you pass as properties to the locals object of res.render, is available in Jade as variables. And in jade, when using the tag(attr=value, attr2=value,...) format you have access to JS syntax and the exposed variables.
As long as you set a variable (e.g. navHighlight based on which route it enters, you can use that to set the class attribute of the tag. Just make sure to separate the class names with spaces, if you have multiple classes.
I'm trying to load in partials from a separate file while using mustache.js but it's proven difficult. The PHP implementation makes this very easy, but not so much in the JS side.
I've gone through the docs and can't find anything related to this, only using an object as a partial instead of a file.
Here's some sort of psuedocode to explain what I'm trying to do.
$.Mustache.load('/mustaches.php', function () {
var item = { thing: 'one' };
var partial = 'partials/actions.mustache';
$('.container').mustache(
'main_mustache',
{item: item},
partial
);
});
So, I'd like to be able to load a secondary file as a partial an include it with the template to be used so I don't have to duplicate code across all the different templates I use.
With something like Blaze I can import a template with {{> templateName}} but it doesn't seem to be quite so easy with Mustache.js.
If this isn't possible with Mustache, what other libraries would you recommend?
You could load both templates with mustache, and then pass one of them (the partial) to the one that should render the partial. Be aware that in order to make it work, in the mustache where you want to render the partial, the variable name should be enclosed in triple curly bracers in order to tell mustache that it should be html:
<div>
<h1>This is my title</h1>
{{{partial}}}
</div>
and then:
$('.container').mustache(
'main_mustache',
{item: item, partial: partial},
);
It may be a very dumb question... I am using Meteor-ui-progress-circle and I want redrawing the template when the percentage (wich is store in a reactive collection Progress) is changed (currently, when I click on a "play" button).
I think I have to use Blaze.render but I don't really understand how it work.
Here a part of my main template (in Jade) :
div.panel-body
div.col-md-9.col-sm-8
p Lorem ipsum...
div.col-md-3.col-sm-4#progress-circle
+progressCircle progress="0" radius="100" class="green"
And my JavaScript :
Template.controlBar.events(
{
"click .play-button": function ()
{
var tmp = Progress.findOne({});
if (!tmp)
{
Meteor.call('createProgress');
tmp = Progress.findOne({});
}
var val = tmp.progressValue;
val += 10;
if (val > 100)
return;
Meteor.call('updateProgess', tmp._id, val);
Template.progressCircle.progress = tmp.progressValue;
Blaze.render(Template.progressCircle, $("#progress-circle")[0]);
},
Doing this... I have several template that are displaying each time I click on the play button. I don't understand how to specify that I don't want a new template but just re-render the one I already have.
Not sure I quite understand your question, but I'll try to help by giving my best understanding of templating and how I have come to use them. If someone sees any incorrect information here, please speak up so I can get a better understanding myself and correct this answer.
First, the Template.XXX.events handlers. In your event handler, you are using a function with no arguments. You can actually accept 2 arguments for these event handler functions: the event and the template. So, you can do something like thus:
Template.controlBar.events({
'click .play_button': function(event, tmpl) {
tmpl.$('div#progress-circle').doSomething();
}
});
Notice the tmpl.$() call? That says to use jQuery to find the specified selector, but ONLY in the current template. This is a wonderful way to use classes to generalize your components, but then be able to filter the selection to only those within the same template...
...Which brings me to my next bit of advice: Use child templates excessively. Any component that I can identify as an "autonomous component" on my page I will consider as a separate template. For instance, I was recently working on a custom reporting page that had a table and some D3 graphs representing some real-time data. In this report page, I had one main template defined for the "page", then each of the D3 graphs where defined as a separate template, and the table was another separate template. This allows several advantages:
Compartmentalization of the "components" of the page, allowing code reuse (I can now put the same graph on ANY page, since it's now an autonomous "component"
The advantage of using the Template.XXX.events trick above to "narrow" the scope of my element searches to elements within that template
Prevents total page refreshes as Meteor is smart enough to only refresh templates that need to be refreshed, which also speeds the responsiveness of the page itself
As a result, I try to apply my Templates liberally. In your case, it would sound to me that if I were to have multiply progress bars on the page that I might turn those into separate templates. I might even do it if I had a single progress bar if it made sense to separate it out for ease of data handling.
Finally, inter-communications between Templates. This can be tricky at times, but the best, most efficient way to do this I have found is through Session variables. The pattern I typically use is to have my data for my template be returned by a Template .helper, which does something like this:
Template.controlBar.helpers({
progressData: function() {
if (!Session.equals('playId', null)) {
return Progress.findOne({_play_id: Session.get('playId')});
}
}
});
Because Helpers are reactive, and Sessions is reactive, the template is re-rendered anytime the 'playId' is altered in the Session. The corresponding Session variable can be set from anywhere in the client code. Again, this tends to work best when you narrow the scope of your templates to the individual components. It is important to note here that the Session object in Meteor is NOT the same as "sessions" in other languages like Java and such, which typically use cookies and a session token/id. Meteor sessions work considerably different, and do not survive page reloads or closing of browsers.