I'm building an app with file manager like functionality with Ember.js. I'd like the URL for nested folder in the form of ".../#/files/Nested/Inside/" and it works fine with linkTo; however if I refresh (or go to the URL directly) I have the error message "No route match the URL '/files/Nested/Inside'". Is there any way to make Ember.js works in situation like this? Thanks.
Here is my current route setup:
FM.Router.map(function() {
this.resource('folders', { path: '/files' })
this.resource('folder', { path: '/files/:path' })
})
FM.FoldersRoute = EM.Route.extend({
model: function() {
return FM.Folder.find('/')
}
})
FM.FolderRoute = EM.Route.extend({
model: function(params) {
return ns.Folder.find(params.path)
},
serialize: function(folder) {
return { path: folder.get('path') }
}
})
Wow, interesting question. It should be possible but I've not tried it myself or seen any examples of this in the wild.
Under the hood, ember uses the tildeio router and route-recognizer to resolve routes. The route's readme explains how to define more elaborate routes like:
router.map(function(match) {
// this will match anything, followed by a slash,
// followed by a dynamic segment (one or more non-
// slash characters)
match("/*page/:location").to("showPage");
});
So to get nested folders working, you might be able to do something like this:
FM.Router.map(function() {
this.resource('folders', { path: '/files' })
this.resource('folder', { path: '/files/*path' })
})
Hope this helps.
Related
I'm trying to create all my pages using NextJs with the slugs/uri I get when fetching the data, however when trying to create the home page it says.
Error: The provided path `/` does not match the page: `/[slug]`.
[slug].tsx is located in the page folder. I removed the index.js since I would like to have the home page the same page layout as other pages.
Currently getting the static paths like this:
export async function getStaticPaths() {
const allPages = await getAllPostsWithSlug();
return {
paths: allPages.edges.map(({ node }) => `${node.uri}`) || [],
fallback: true,
};
}
The error I got makes sense, but I couldn't really find a solution to create my home page in the same page template.
As per the documentation, you can use an optional catch-all route.
Name your page file [[...slug]].js, and then use something like the this for the paths prop in getStaticPaths:
paths: [
{ params: { slug: [] } },
{ params: { slug: ['foo'] } },
{ params: { slug: ['bar'] } },
]
FWIW - if you want to share components from the main template (i.e. header, nav, footer etc), I recommend using a custom _app.js page. If your homepage really is identical to your other pages, it might be worth re-visiting the design!
Good luck!
Since [...slug] is not the same as [slug] as it grabs sub-paths as well, you can achieve a closer match using just [slug].js and adding a rewrite to your next.config.js like so:
async rewrites() {
return [
{
source: '/',
destination: '/homepage',
},
]
},
Then you handle the homepage slug as your homepage, just like any other CMS page or post.
(/homepage can be anything you want, e.g. /index)
I have an issue with how my React Redux SSR application is handling site navigation I have a route for list pages which will display different data depending on the params in the URL.
Routes.js file
export default [
{
...App,
routes: [
{
...HomePage,
path: '/',
exact: true
},
{
...ListPage,
path: '/list/:id',
exact: true
},
In my Index.JS file where my express backend is running I iterate through my routes directory to see the path(s) that matches the request path...
const app = express();
app.use(express.static('public'));
app.get('*', (req, res) => {
const store = createStore(req);
const promises = matchRoutes(Routes, req.path)
.map(({ route }) => {
console.log("Looking at Route: ", route);
if (route.loadData) {
const params = req.path.split('/');
console.log('my params are: ', params)
return route.loadData(store, params[2])
}else{
return null
}
})
.map(promise => {
if (promise) {
return new Promise((resolve, reject) => {
promise.then(resolve).catch(resolve);
});
}
});
Promise.all(promises).then(() => {
const context = {params: req.params};
const content = renderer(req, store, context);
if (context.url) {
return res.redirect(301, context.url);
}
if (context.notFound) {
res.status(404);
}
res.send(content);
});
});
My understanding is that there should only be 2 things to iterate over, the App component Route, and the ListPage component Route it then calls their respective loadData() functions and the websites continues to run. However after it goes through the first 2 routes and populates my page with the relevant information the Index.js file gets called again and iterates through the routes but this time instead of having the URL that the user is trying to access it replaces it with "bundle.js" and I don't understand what's going on here. This is the output I get I would love to only have the top half of the output.
NOTE this image is taken from my console (I've combined both the client and server side output in 1 window) below I'll include a screenshot of my config Files
Of course my code wasn't expecting this as a path and the application breaks because it's trying to get information on a list with the ID of "bundle.js" instead of a standard number.
Question can someone explain to me what my codes doing wrong here or if this is how it's supposed to behave how I work around this I'd greatly appreciate it.
I'm currently trying to create my first SSR application so I'm new to this technology so I might be missing something obvious.
Upon further investigation I noticed that the file bundle.js that I could see in the console was referring to a file at location /list/bundle.js but my bundle was actually in my public directory so I had to modify the script Src so that it would refer to the http://localhost:3000/bundle.js after I did this app functioned how It was supposed.
Really new to ember and trying to setup basic (in my mind) routes.
I have calendars resource and I want to display individual calendars.
My app/router.js has the following:
this.route('calendar', {path: 'calendars/:calendar_id'}, function () {
this.route('show');
this.route('edit');
});
this.route('calendars', function(){
this.route('create');
});
Folders are as following:
app/routes: [
calendars: [create, index],
calendar: [edit, show]
]
app/templates: [
calendars: [create, index]
calendar: [edit, show]
]
In app/routes/calendar/show.js:
import Ember from 'ember';
export default Ember.Route.extend({
model(params) {
return this.store.findRecord('calendar', params.calendar_id);
}
});
Problems start when I go to http://SERVER/calendars/5/show (5 is a :calendar_id part, SERVER is what hosts ember app) :
when I log params - they are undefined
In dev tools I see that Ember somehow makes a POST request to my server as http://SERVER/calendars/5
(a :calendar_id part, SERVER is on same domain and where my back-end resides).
This happens regardless if I comment out model() function in app/routes/calendar/show.js file.
Apparently Ember knows what calendar_id to use for that request.
But I don't know where that call to the server happens:
If I comment out model(){} altogether, my template renders model record (the calendar record that Ember fetches).
If I on the other hand try to log params in model() and I comment out this.store.findRecord part out, the params are undefined and it raises an error.
I thought at first that it is my DS.RESTAdapter since I have defined updateRecord changes to fake PUT request (my server does not allow that), but I commented out the whole file and it still does this query.
I've cleaned both dist/, tmp/, upgraded to 2.9.0, but it does the same thing.
I have no controllers defined
How does Ember make POST request if model() hook is missing from route, I have no controllers difined. Also how do I fix it so that it works? ;p
Edit [2]:
I am trying this now and I think it kinda works, but looks ugly:
this.route('calendars',{ path: '/calendars'}, function(){
this.route('create');
});
this.route('calendar', { path: '/' }, function () {
this.route('show', { path: '/calendars/:calendar_id/show' });
this.route('edit', { path: '/calendars/:calendar_id/edit' });
});
this.route('index', { path: ''});
Ember is smart enough to generate a default route if you do not create one, and a default model if you do not create a model function.
It does this based on the routes name ie if your route is "calendar" it generates a model function based on the "calendar" model.
Try explicitly define your route path with the parameters as per ember docs:
https://guides.emberjs.com/v2.9.0/routing/defining-your-routes/
this.route('calendar', function () {
this.route('show', { path: '/:calendar_id/show' });
this.route('edit', { path: '/:calendar_id/edit' });
this.route('create');
});
Is there a way to execute when a "base" route is activated/re-activated? I have the following urls:
Router.map(function() {
// /projects
this.resource('projects', function() {
// /projects/2
this.resource('project', {path: ':id'});
});
});
I'd like to transitionTo the individual project route if there is only one project returned from the projects route. I can do this:
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.findAll('project');
},
afterModel: function(projects, transition) {
if (projects && projects.get('length') === 1) {
this.transitionTo('project', projects.get('firstObject'));
}
}
});
This works nicely...except i can still go to the projects route and since the model has already been loaded that hook doesn't fire again. Is there a way to listen to always enforce that rule when the projects route is activated?
For example, if i go to /projects/23 and then i go to /projects i'd like to auto-transition them to the single project route if there is only one. I don't know how to accomplish that since it's a nested route and the activate and afterModel methods have already been fired when visiting /projects/23 for the first time.
Does that make sense? and how can i accomplish this?
Use an index route on your projects resource. It will only get hit when you hit /projects
ProjectsIndex
export default Ember.Route.extend({
model: function() {
var projects = this.modelFor('projects');
if (projects && projects.get('length') === 1) {
this.transitionTo('project', projects.get('firstObject'));
}
},
});
I have set up the following paths:
this.resource('areaManagement', { path: '/management/areas' }, function() {
this.route('assign', { path: '/assign' });
this.route('new', { path: '/new' });
this.route('subdivide', { path: '/:id' });
});
If you wanted to create a new area, you would go to /management/areas/new, but in testing, I accidentally went to /management/areas/create and noticed no error was thrown. Then I realized, this path is matching the subdivide dynamic segment.
If this is how dynamic segments work, then technically, /management/areas/anythingyouwant will be a valid/recognized path. I could always implement a model() hook and redirect/error if :id isn't an integer, but is there a way to make the router enforce this?
I think an "easy" workaround would be to add a new nesting level by adding a
path: '/id'
to have a URL like
/management/areas/id/1
Not necessarily idealbut would help you here.