Is it possible to structure a route that contains dynamic parts in predefined format, such as /[name]-id[id], for example to have routes /bob-id303 or /mary-id205?
What I tried is create a file [name]-id[id].js. Inside getInitialProps I console.log the ctx and it contains
pathname: '/[name]-id[id]',
query: { 'name]-id[id': 'bob-id303' },
asPath: '/bob-id303',
On the other hand, calling the file [[name]]-id[id]].js gives
Failed to reload dynamic routes: Error: Optional route parameters are not yet supported ("[[name]-id[id]]").
I'd like to get the name and id directly, then pass them through initial props to the page. I'm aware I can parse asPath, but is there another way to do this?
You could use /[slug] and then do slug.split("-id"). However you may be better off doing the id alone followed by a fetch for metadata because the name could change and then that url would potentially 404.
Related
Here is the issue I am trying to solve with creating dynamic routes in NextJS.
-products
-[category]
-[size]
-[product]
The issue is some products don't have a size parameter and would need to bypass this parameter to get to the product landing page. Here is an example of the different routes I would like to have:
/products/ceiling-lighting/2x4/light-one
/products/ceiling-lighting/2x4/light-two
/products/ceiling-lighting/2x2/light-one
/products/ceiling-lighting/2x2/light-two
/products/light-switch/switch-one
/products/light-switch/switch-two
Is this possible to do with dynamic routes?
Nope it will not work, you have to pass required number to parameters/segments to reach that specific dynamic page, but as juliomalves said, you can use catch all routes, after using this you will get your parameters in the form of array and you can navigate programmatically depending on number of parameters or whatever logic you want.
In Chat-Demo, there is a syntax where the code (https://donejs.com/Guide.html#switch-between-pages) is split into two blocks: one for <chat-messages/> to load whenever page='chat', and another for <chat-home/> for home.
Those two blocks are very similar.
Imagine if there where not two, but, let's say, ten or more different components to load that way (for example, a big menu of options, each one linking to a different page/component).
Do I need to create as many "if" blocks as the number of options in the menu, or there is another more compact way to do this?
The multiple "if" blocks are an easy way to get up and running with an app quickly. However, it does not work well when your app begins to grow. There is a technique that I have used in apps which works really well. I have uploaded a version of the donejs chat demo with my customizations. I suggest you pull it down before reading further:
https://github.com/DesignByOnyx/donejs-with-route-config
There are 3 commits so you can see the changes between each step of the process. The most important parts are in the 3rd commit:
Generate the DoneJS chat app with no modifications: e0398af4c23207d527c054f1fb1ea65b419119a0
Add the home.component and messages component: 56c2202c117049f67ff7dc52b054ad30cc5b71eb
Add route-config, navigation component, dynamic loading, bundling: 4a924693bfd8a3469d69a6ccb5abe8675724e8a9
Description of Commit #3
The last commit contains all of the magic (and the result of many hours of work from my last project). You should start by looking at the src/route-config.js file, as it contains all of the information about routing and dynamic modules for the app. There are a couple things you should know:
The "modules" object is a simple mapping of url-friendly names to the module which they should load. You can rename these however you want.
NOTE: for each item, a separate bundle will be generated during the build process.
const modules = {
'home': '~/home.component',
'messages': '~/messages/messages',
};
The main export is an array of routes which will be registered for your app:
module.exports = [
{ route: '/', nav: 'Home', data: { moduleId: modules.home } },
{ route: '/chat', nav: 'Chat Messages', data: { moduleId: modules.messages } },
];
For each item in the array, there are 3 properties:
route: the parameterized route - you can include parameters like /user/{userId} for dynamic routing as described in the docs.
data: the default data for the route. Whenever the URL matches the route, the default data will be merged onto the app viewmodel. See the moduleId property on the app viewmodel.
nav (optional): if specified, the value will be used to generate a link in the main navigation component.
I am trying to pass the signInSuccessUrl parameter dynamically to the widget page as an URL parameter. Unfortunately without success.
According to the gitkit forum (https://groups.google.com/d/msg/google-identity-toolkit/grF6C4CByEk/Dz4l2P-mTOwJ) this should work.
Am I missing something? Thank you.
That's what I tried:
The signin.html page showing the widget is configured like this:
JS Config:
var config = {
apiKey: '...',
idps: ["googleplus"],
//signInSuccessUrl:NOT SPECIFIED CAUSE WE PASS IT VIA URL,
oobActionUrl: '//127.0.0.1:8888/gwt/servlet/gitkit/email',
siteName: 'SN',
};
window.google.identitytoolkit.start(...);
Open browser and show Javascript console of browser
Enter the widget page url in browser:
http://127.0.0.1:8888/signin.html?signInSuccessUrl=127.0.0.1%3A8888%2Fgwt%2Fservlet%2Fgitkit%2Fsignedin%0A&o=dynamic
Continue with sign in...
This will not redirect to signInSuccessUrl but rather produce the error:
Uncaught Error: Configuration signInSuccessUrl is required.Si # gitkit.js:217Ik # gitkit.js:248(anonymous function) # gitkit.js:257(anonymous function) # gitkit.js:152(anonymous function) # gitkit.js:213Fc # gitkit.js:38h.dispatchEvent # gitkit.js:36zi # gitkit.js:210U.onReadyStateChangeEntryPoint_ # gitkit.js:208U.onReadyStateChange_ # gitkit.js:208
NOTE: if I set the signInSuccessUrl param in widget config, like:
signInSuccessUrl:"//127.0.0.1:8888/gwt/servlet/gitkit/signedin?o=hardcoded",
It will work but NOT use the signInSuccessUrl provided in the URL but the hardcoded one, i.e. the 'o' param in this example will not be overriden.
The reason it did not work was related to using the open scheme in oobActionUrl
'//127.0.0.1:8888/gwt/servlet/gitkit/email'
When using 'http://127.0.0.1:8888/gwt/servlet/gitkit/email' it did work.
the signInSuccessUrl in the widget configuration is a required field. You always have to provide it. The signInSuccessUrl parameter in the widget url if provided will override the config value. This makes sense since the signInSuccessUrl query parameter is optional and may not always be provided in the url. Try providing it in the config and then pass it in the url. It should work the way you want it to.
In a Template Helper I get the current path from Iron.Router (iron:router) as follows:
Router.current().route.path(this);
This works fine, unless the route path does contain parameters (e.g. /client/:_id/edit). In that case the path() function returns null.
How do I get the current path within a Template Helper, when the route contains parameters?
There are posts around covering the issue but the solution mentioned there seem not to fit.
I'm using Meteor 1.1.5 with iron:router1.0.7
According to this iron-router/issues/289 there are problems when the path contains parameters. The suggestion to use Iron.Location.get().path This works well for me.
As we all know, Meteor's initial payload sent to the client comprises (in production) a concatenated javascript file containing the Meteor platform, packages, and all templates parsed into Meteor's reactive templating system. Server-side rendering, where the templates are rendered to HTML and sent to the client in the initial payload, is on its way but doesn't have an expected release date yet.
I'm looking for a way to "hack" or approximate server-side rendering given the available functionality in Meteor 0.8.x. Specifically, I want to:
enable the page to render its initial contents without first waiting for the several hundred KB Meteor platform javascript file to be downloaded and parsed.
alternatively, modify Meteor so it only sends the templates it needs to render the initial request in the javascript payload, and fetches the remaining templates once render is complete.
The use case is http://q42.com. I recognise Meteor isn't the best fit for static websites like this one but I want to try and see how far I can get anyway. Right now the Meteor platform JS file is over 600 KB in size (±200 KB gzipped) and I'd like to reduce this size if possible.
Note: I'm aware of and already using Arunoda's fast-render package, which is intended to send data with the initial payload. In this case I want to cut down on time-to-first-render by also getting the templates themselves down faster.
This is a little bit tricky. But there are some things you could do. This may not be the beeest way to do it but it could help you get started somehow.
Meteor is build with many packages as 'default', sometimes some are not needed. You can remove the standard-app-packages and add the packages (that you need and use manually) listed here: https://github.com/meteor/meteor/blob/devel/packages/standard-app-packages/package.js
To cut down the templates you would have to include the bare templates that you use and include the other template's separately and perhaps send down the Template information via a Collection, using a live observer handle to initiate the templates
You would have to 'render' the templates on the server side or store them manually in your collection using Spacebars.compile from the spacebars-compiler package which is a little tricky but you could have it done decently:
This should give you a rough idea, not sure how to get passed the 'eval' bit of it though:
HTML file in /private/template.html
<template name="test">
Hello {{name}}
</template>
JS file in /private/template.js
Template.test.name = function() { return "Bob" }
Server side code
var collection = new Meteor.Collection("templates");
var templateData = Assets.getText("template.html");
var templateJs = Assets.getText("template.js");
var compiled = Spacebars.compile(templateData).toString();
var jsData = templateJs;
collection.insert({templateName:"test", data: templateData, js: templateJs});
Client Side code
collection.find().observeChanges({
added: function(id, fields) {
var template = fields.data,
name = fields.name,
js = fields.js;
Template["name"] = UI.Component.extend({
kind: "name",
render: eval(template),
});
eval(js);
}
});
Then just subscribe to the collection asking for your template and it should exist. If you use iron-router I think (not sure) you could make the subscription wait before the template is rendered so you could have it work.
Again this is just a 'hacky' solution, one thing I personally don't like about it is the use of eval, but javascript needs to run somehow...
You could loop through files in a particular folder using fs = Npm.require('fs') to render each template too.
One alternative would be to inject a 'script' tag calling the compiled js template and template helpers to let the template exist.