I've created a routes.js file that is as follows:
const nextRoutes = require('next-routes');
const routes = module.exports = nextRoutes();
routes.add
('presenterallcurrent', '/presenter/:ccyear','speaker');
routes.add
('speakersessiondetail', '/presenter/:ccyear/:slugSpeaker','speakerdetail');
I am expecting that when I browse to:
/presenter/2018/douglas-crockford-1124
I will be taken to my /pages/speakerdetail.js file with query.ccyear and query.slugSpeaker populated to 2018 and douglas-crockford-1124
I am expecting that when I browse to
/presenter/2018
I will be taken to /pages/speaker.js with query.ccyear set to 2018
The second case works as I expect (/presenter/2018) does take me to /pages/speaker.js, but (/presenter/2018/douglas-crockford-1124 gives me a 404.
What am I not understanding and why does this not work?
In my /pages/speaker and /pages/speakerdetail:
static async getInitialProps({query}) {...}
For optional parameters in your route, add a trailing ? and adjust speaker.js to render either the year view or detail view based on whether the query.slugSpeaker is undefined.
routes.add
('speaker', '/presenter/:ccyear/:slugSpeaker?');
Related
In my React application, I'd like to be able to navigate to sub-pages of my domain using the name property of my entity, rather than its id.
For example:
Now: https://.../companies/1
Desired: https://.../companies/company-name
The problem that I am trying to deal with is that with the first approach, I can easily fetch the company object from my backend, since I can simply use the passed id for that. However, for the second case, that is not anymore possible. Of course, I could create a method that fetches the company by name instead of id but that is neither elegant nor completely correct, considering that for some company names (e.g. those with spaces), the url parameter will be encoded, thus will not match with the original one.
Is there a smart way to achieve the desired outcome without storing some kind of mapping for ALL the potential company-urls in my Redux store? (e.g. company1 -> 1, company2 -> 2)
May be you need this:
Use Link to dynamically generate a list of routes.
Use : to indicate url params, :id in this case
Use the match object passed as props to the rendered route component to access the url params. this.props.match.params.id
<BrowserRouter>
/* Links */
{heroes.map(hero => (<Link to={'heroes/' + hero.id} />)}
/* Component */
<Route path="heroes/:id" component={Hero} />
</BrowserRouter>
class Hero extends Component {
render() {
return (
<div>
{this.props.match.params.id}
</div>
);
} }
What you're asking for is most definitely possible, but there is more work on the backend than the client (React).
If your route is defined as /companies/:id the :id can be anything. It can be 1234, it can be h3h3h3 and it can be amazon-company.
What :id marks is the name of the param which will hold your value.
For example:
https://.../companies/amazon
const { useParams } from 'react-router-dom';
const { id } = useParams(); // id will be 'amazon'
or, alternatively:
https://.../companies/1
const { useParams } from 'react-router-dom';
const { id } = useParams(); // id will be '1'
Now you have a specific problem with your idea, what about duplicate names? There are definitely more than 1 "Amazon" in the world, maybe not LLC, maybe D.o.o, maybe GMBH, but the point is, all of those would use the same name in your system.
What you want to do is incorporate some form of a slug in your backend.
When you're creating a Company Model in your backend, add something like this in the controller:
// CreateCompany.js
const Schema = {
slug: req.body.title + Math.random(...)
}
...
This will create a unique slug for the company name, such as amazon-235h35. You can learn more about proper slug usages on google, this is just an example ;)
And then instead of routing by companies/${company.id} you would do companies/${company.slug}
Does this make anything clearer?
How can I prevent links automaticly from beeing displayed in the Template ctp files?
I will give you an example:
User(id = 1) is allowed to see teamcalendars/view/1
User(id = 2) is not allowed to see teamcalendars/view/1.
User1 is member of the team 1 and should see and follow the link. User2 is not member in any teams and neither should see the link to the calender nor follow it. But I would like to place the link in the teams/index file where both users can go to and see all teams, but with different options per team.
If User2 follows the link (or types it into the browser manually), the controller will return a redirect and a error message about missing privileges. User2 will anyhow never get there. But how do I prevent cake from displaying the link for User2 (its missleading)?
Is there a possibility to connect the link to the controller and action and the id of the object where it is leading to, so I don't neet to take care of building and passing variables for each view just to decide which links can be displayed?
Sorry for not providing any code, but I think anyone knows how to send an array from the Controller to the View and how to validate it with if(){echo $this->Html->link()}, which is what I am doing currently.
Thank you for any help or remarks in advance.
One option is to define your own HtmlHelper and override the link function, such that it checks the permissions on the link first and only outputs it if they are allowed access. Something like the following:
namespace App\View\Helper;
use \Cake\View\Helper\HtmlHelper;
// Or, if you're already using a third-party HTML helper, something like
// use BootstrapUI\View\Helper\HtmlHelper as HtmlHelper;
class MyHtmlHelper extends HtmlHelper
{
function link($title, $url = null, array $options = [])
{
if (checkMyPermissions($url)) {
return parent::link($title, $url, $options);
}
}
}
And then in your AppController:
use App\View\Helper\MyHtmlHelper;
public $helpers = [
'Html' => ['className' => 'MyHtmlHelper'],
// ... and all your other helpers
];
i have a situation here that i can't seem to figure out. Please if anybody knows how to resolve this i would love to hear suggestions.Thanks
I have a "global view" that is visible on a subnavbar in the app, that is a calendar, this calendar serves as a global calendar throughout the application, so almost all the views use the calendar view & model to set show data according to the date selected.
This calendar view/model should have some way to store in history each time the date is changed, this (i think) is done using a single URL or query string parameters each time the date is changed, something like
webapp/view1?date=20120301
and when the date is changed, so its the query string.
I would like to use query string parameters for this so i don't have to specify on each route the (/:date) optional parameter.
THE THING IS backbone stopped firing a route change or a history event when query strings are changed, they simply ignore query strings on the Backbone.History implementation, this is breaking all my implementation cause i can't track each time the querystring is changed, so the "back" button will not fire a change event and therefore i can't change the date on the model that would change the date on the calendar.
I know a simple solution to this would be just using "pretty URL" and adding that date parameter to each view, but im trying to avoid that.
Any suggestions?
Thanks in advance
UPDATE
I ended up using "pretty URLs" like the Backbone documentation suggests, cause using query strings would bring me a lot of trouble for tracking the URL change and history, also wouldn't work as expected when using hashchange instead of pushState.
So, my code ended up like this:
Attaching somewhere in your router, view, controller, something, to the "route" event of your router, to check the URI for the date and set this date to the calendar picker:
this.listenTo(myRouter, "route", this.routeChanged);
Then, this method would do something like:
checkURIdateParameter: function (route, arr) {
var found = false;
for (var i = 0; i < arr.length ; i++) {
if (arr && arr[i] && arr[i].indexOf("date=") != -1) {
if (app.models.dateControl) {
var dateFromParameter = new Date(arr[i].substring("date=".length).replace(/\$/g, ":"));
dateFromParameter = (isNaN(dateFromParameter.getTime())) ? app.models.dateControl.defaults.date : dateFromParameter;
app.models.dateControl.set("date", dateFromParameter);
found = true;
}
}
}
if (!found) app.models.dateControl.set("date", app.models.dateControl.defaults.date, {changeURI:false});
}
This serves as the function that will read params from the URI and reflect those changes on the dateControl.
There should be another method that will be the one in charge of updating the URI to a new one each time the date is changed (so that the params are in sync with the view) and the link can be copied and pasted with no problems.
Somewhere on your router, view, controller:
this.listenTo(app.models.dateControl, "change:date", this.updateURIdateParameter);
This is a method that is attached to a model that has the current date, this model is updated by the calendar picker (the UI control) or the method that was linked with the route event (routeChanged, look above).
This method should do something like this:
, updateURIdateParameter: function (a, b, c) {
if (c && c.changeURI == false) return; //this is in case i don't want to refresh the URI case the default date is set.
var currentFragment = Backbone.history.fragment;
var newFragment = "";
var dateEncoded = app.models.dateControl.get("date").toJSON().replace(/\:/g, "$");
newFragment = currentFragment.replace(/\/date=([^/]+)/, "") + "/date=" + dateEncoded;
if (newFragment != currentFragment) {
app.router.navigate(newFragment, false);
}
}
This method gets the currentDate selected from the corresponding model, parses it, then takes the URL from the Backbone.history.fragment, execs a nice regexp against it that will replace the old date parameter (if exists) and then appends the new encoded date.
Then navigates to this route in silent mode so that the method that checks the route is not called (this prevents annoying loops).
I hope this helps.
I would suggest using "Pretty URL".
FYI Page URL in the browser bar will blink in this example.
Somewhere inside your Backbone.Router:
this.route('*action', function() {
console.log('404');
});
this.route(/^(.*?)\?date=(\d+)$/, function(route, date) {
// same current route (with ?date)
var fragment = Backbone.history.fragment;
// navigate to main route (to create views etc.)
this.navigate(route, {trigger:true, replace:true});
// silent change hash
this.navigate(fragment);
});
this.route('any', function() {
// need wait for hash change
_.defer(function() {
console.log('parse date here and do what you want', window.location.hash.match(/^(.*?)\?date=(\d+)$/));
});
});
this.route('route', function() {
// need wait for hash change
_.defer(function() {
console.log('parse date here and do what you want', window.location.hash.match(/^(.*?)\?date=(\d+)$/));
});
});
Am working with views and am wondering if there is a way to get the view to update the breadcrumb trail. When on my first view called homme the breadcrumbs are not updated it still just says "home >" as if it is still on the homepage. When I click a post the breadcrumbs update to "Home › Blogs › admin's blog › ". I need it to say Home > Homme > Name of Article, basically what you would expect when going to a blog site or post.
Can I get the view to act like a blog?
One option is to try overriding the themeable output generated by the default breadcrumb function.
Assuming you've created your own theme - create a file called template.php at the root of your theme. Create a function named YOURTHEME_breadcrumb, where YOURTHEME is the name of the theme. The HTML returned by this function will be the breadcrumb. Modify the return values as necessary here to get what you want. Consider using Drupal's menu functions to build a more satisfactory breadcrumb.
Check the comments of this API article for more detail: http://api.drupal.org/api/drupal/includes--theme.inc/function/theme_breadcrumb/7
Adding this to your template.php file should work with d7 sites:
function theme_breadcrumb($breadcrumb)
{
if (substr($_GET['q'], 0, 13) == 'news/category') {
$breadcrumb[] = l('News', 'news/');
}
if (count($breadcrumb) > 1) {
if ($breadcrumb) {
return '<div class="breadcrumb">'. implode(' › ', $breadcrumb) ."</div>\n";
}
}
}
I'm looking for a constant or variable that will provide a public path to my application root.
I have got so far as FULL_BASE_URL which gives me http://www.example.com but I have the added problem of my application being in a sub directory (e.g. http://www.example.com/myapp/).
Is there any way to get the path like http://www.example.com/myapp/ in my controller?
$this->Html->url( '/', true );
In general you should generate all links with that function, see http://book.cakephp.org/view/1448/url
$this->base;
http://api.cakephp.org/class/dispatcher
<?php
...
$this->redirect( Router::url( "/", true ));
...
?>
Router is the static class used by the HtmlHelper::link, Controller::redirect etc. the Router::url method takes a string, or array and matches it to a route. Then it returns the url that matched the route info as a string.
If you pass "/" to the Router::url call you get a relative link to the root of your app. If you pass "/" and true to the Router::url call you will prepend the full BASE_URL to the resulting relative path. This should give you what you need. If not, here is the link to the Router documentation. Try experimenting with the second boolean param - it may or may not work as expected depending on what you read / your own testing.
http://api.cakephp.org/class/router#method-Routerurl