Using Slugs in Meteor's Iron Router - javascript

I'm trying to define an Iron Router route to use a slug: ie. a string title but with spaces replaced by hyphens.
Instead of /news/breaking%20news, we want it to be /news/breaking-news.
Router.map(function() {
this.route('news', {
path: '/news/:title'
})
})
To achieve this, do we need to create a new document field slug with the slug string and use path: '/news/:slug? Or is there a more elegant method that avoids this redundant field?
App is using packages aldeed:simple-schema, aldeed:collection2 and dburles:collection-helpers if those help.

The constraining issue is the 1-way, non-reversible transformation from the actual title to the slug (e.g., "foo-bar" and "foo bar" both end up with the same slug, "foo-bar", so when you see "foo-bar" you don't know what the actual title is, so you can't look it up). So unless you can control for that (e.g., adding the slug to the document as you suggest, or enforcing a strict no-hyphens policy for titles in the application), you'll need a way to store the mapping.
One potential way around this would be to include the document ID in the URL and have a meaningless slug, which can be filled in by the router itself:
Router.route('/news/:_id/:slug?', ...)
Then only use the _id in the actual routing. Note that this allows you to put whatever you want in the URL after the _id, so that 'news/[_id]/foo-bar' and 'news/_id/foo%20bar' both go to the same page, (news/[_id]), but so does 'news/[_id]/this_is_completely_meaningless.' In the router, you can then redirect to the appropriate URL as needed:
Router.route('/news/:_id/:slug?', {
name: 'news',
data: function() { return news.findOne({_id: this.params._id}); },
onBeforeAction: function() {
var data = this.data();
if (data) {
var realUrl = '/' + data._id + '/' + slugify(data.title); // the desired URL, incl. whatever your slug fn is
if (this.url.indexOf(realUrl) < 0) {
this.redirect('/news' + realUrl); // if we aren't at the desired URL, take us there
}
}
this.next();
}
});
You'd then have to control for any "share this page" functionality to ensure that it gives the desired URL, but if anyone tries to go to news/[_id]/this_is_completely_meaningless it'll redirect them to news/[_id]/foo-bar.

Related

Next.js - On Shallow routing for dynamic path it is removing the basePath from browser url

i have a dynamic routes:
Pages
- [name]
- [brandid]
the browser url should base [basePath]/[name]/[brandid]. After doing shallow routing
router.push("?page=2", undefined, { shallow: true });
the url removes the basePath. And only shows /[name]/[brandid]
When you have dynamic routing in Next.js, and want to do a shallow adjustment of the route to reflect updated query params, you probably want to do something like this:
const router = useRouter()
const url = {
pathname: router.pathname,
query: { ...router.query, page: 2 }
}
router.push(url, undefined, { shallow: true })
This will retreive the current path (router.pathname) and query (router.query) details, and merge them in along with your new page query param. If your forget to merge in the existing query params you might see an error like:
The provided href value is missing query values to be interpolated
properly

How to pass state between routes in NEXT JS?

I have a data inside an object in my page. I want to redirect from that page to another page along with the data like the code below
const redirectAppointmentStep1 = (value) => {
router.push({
pathname: '/Appointment/bookingstep1',
query: {value : value},
})
}
and I received that data in bookingstep1 page like this
const {query} = useRouter()
but the problem is the query appear on the url and the url does not look clean. How can I send the data from one page to another page but without appearing it on the url?
I am new in Next.js so any help will be highly appreciated.
If you want to send "route state" you have to do it via query string, but you can actually "mask" the path that is shown in the browser via the as property.
Router.push
router.push(url, as, options)
as - Optional decorator for the URL that will be shown in the
browser.
You can decorate the URL to match the path name.
const redirectAppointmentStep1 = (value) => {
router.push(
{
pathname: '/Appointment/bookingstep1',
query: {value : value},
},
'/Appointment/bookingstep1', // "as" argument
)
}

cypress: comparing information in 2 sites

I'm starting with cypress and need to compare 2 different environments.
I did a script, but is not working properly.
My goal is:
1 - search for a specific selector value at 2 different environments.
2 - get it's value (in both env) , and then compare it if equal.
The below comparision work, but seems very poor code and it stop in first error assert and can't query reference selector, just text.
Any help is appreciated.
describe('Testing Page', function() {
//urls i need to test
var relative_urls = [
'/products/test1',
'/products/test2',
]
relative_urls.forEach((url) => {
//each url is compared here...
var productInfo = [];
//here goes the environments URL.
var testURL = 'https://www.myurl.com' + url;
var referenceURL = 'http://test.myurl.com' + url;
it('Comparing data from url:' + url, function() {
cy.visit(testURL)
//get data from selector and add it to array
cy.get('body').find(".myselector h1").should(($input) => {
productInfo.push($input.val())
})
cy.get('body').find(".myselector h2").should(($input) => {
productInfo.push($input.val())
})
//requesting second url
cy.request(referenceURL)
.its('body').should( ($input) => {
for (var j=0;j<productInfo.length;j++) {
//expect works, but compares to all site, and i need to search in a specific selector.
//Also, when it gets the first error, it stops and do not search all elements of array
expect($input.includes(productInfo[j]), 'Notice: ' + productInfo[j]).to.be.true
}
}
})
})
})
})
From reading the documentation, cy.request is really making a plain HTTP request without doing any parsing, which means you basically have to parse the body of the response yourself. cy.visit will actually get the DOM elements so you can use Cypress's query syntax to navigate the page.
I think once you have the element values from the first page you should just do cy.visit again and parse the second page.
EDIT: Apparently you can't use cy.visit cross-domain. In that case, maybe you could try parsing the response body into a DOM node like this:
var el = document.createElement( 'html' );
el.innerHTML = request.body // you can get the request object from cy.request;
Then use el.querySelector to navigate the DOM tree using CSS syntax.

ui-router: Transform parameter from URL

Is there any possibility to transparently transform URL parameters? As it's kind of difficult to explain, I will give a scenario. We have a URL structure similar to this one:
/shopping/nuts/:productId
/shopping/berries/:productId
/shopping/juice/:productId
Our products shown in the app (delivered by some API) might look something like this:
{ type: 'berry', price: 123, text: 'lorem ipsum', id: '12345' }
Note the discrepancy between singular and plural form: The URL contains the plural, e.g. 'berries', whereas the products as delivered by our REST API use the singular form 'berry'.
Part from our state definition looks something like this:
.state('shop.product', {
url: '/shopping/:type/:productId',
templateUrl: 'type.html',
controller: 'TypeController'
})
Problem: In every controller I need to invoke a function for transforming the $state parameter to the singular form (toSingular($stateParams.type)), and when building links, I need to do the same thing in reverse. This is extremely tedious and error prone.
So, my ideal solution would be something like
For URL /shopping/berries/12345 I will have a $stateParams.type === 'berry', and when creating the URL through ui-sref="shop.product({type: 'berry', id: '12345'})" I will get the URL /shopping/berries/12345 again.
I've been banging my head on the table while trying to find a hook where I could integrate this logic without any success. Any feedback greatly appreciated!
You want to register a custom parameter type.
https://ui-router.github.io/docs/latest/classes/url.urlmatcherfactory.html#type
Your parameter type should encode and decode the value in the URL.
To encode, take the internal representation (nut, berry, etc) and return the string to show in the URL.
To decode, take the string from the URL and return the internal value.
var productTypes = {
berry: ['berries', 'berry'],
nut: ['nuts', 'nut'],
juice: ['juice'],
}
$urlMatcherFactoryProvider.type('productType', {
encode: val =>
productTypes[val][0],
decode: string =>
Object.keys(productTypes).find(key => productTypes[key].indexOf(string) !== -1),
});
Here's a plunker that demonstrates the code: http://plnkr.co/edit/Eed8h7U6x0j8RAWs7qT9?p=preview
Possible solution:
If your URL is /shopping/berries/12345 that means you didn't pass stateParams.type. Beacause if you pass type, the url definitely shopping/berry/12345. and If you make url shop.product({type: 'x', id: '12'}) the url will be shopping/x/12. That is what stateParams in the url. I think you are making url that is not a state. Thats why you are remains at the same url. Include $urlRouterProvider.otherwise() to get more clarification.
$stateProvider.state('shop', {
url: 'shopping'
})
.state('shop.product', {
url: '/:type/:id'
});
$urlRouterProvider.otherwise('/');
If you make a wrong url, then you will be redirected to '/'

In Ember, how can I find out the exact URL a call like store.query will hit?

When debugging my API, I want to know, for a given line of code
store.query('foo', {filter: ...})
exactly what URL this causes Ember to hit. Currently, to find this out, I just open devtools and look for the request in the network tab.
This isn't ideal because:
it's harder to find the relevant network request if there are a lot of them
I have to do a whole refresh and wait for the page to load
Is there a better way, maybe a programmatic one using a low-level API?
I'm looking for something like:
store.buildRequest('query', 'foo', {filter: ...}).url()
In application adapter, override urForQuery.
http://emberjs.com/api/data/classes/DS.JSONAPIAdapter.html#method_urlForQuery
console.log(this._buildURL(modelName));
You can create this exact method by extending the store service and calling the buildURL method on your model's adapter.
// services/store.js
export default Ember.Service.extend({
buildRequestURL(requestType, modelName, data) {
let id, query;
// determine if a query or an id
if (Ember.typeOf(data) === 'object') {
query = data;
} else {
id = data;
}
return this.adapterFor(modelName) // retrieve the adapter for your model
.buildURL( // build the url for your request
modelName,
id,
null,
requestType,
query
);
}
});
The buildURL internally calls a specific url building method based on the request type. (urlForFindRecord, urlForQuery, .etc)
You would use it pretty much identically to what you suggested (just without the .url() at the end):
// get the url for for store.query('foo', { filter: ... })
const urlQuery = store.buildRequestURL('query', 'foo', { filter: ... });
// get the url for store.findRecord('bar', 1);
const urlFind = store.buildRequestURL('findRecord', 'bar', 1);
// ...

Categories

Resources