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 '/'
Related
I'm working on a project and i want to define url object that will be passed to fetch ex.
https://t1.testing.com/test/api/v1/blog?pagingindex=0&pagingresults=10
const url_object = {
url: `https://t1.testing.com/test/api/v1/blog`,
url_params: {
method: "POST",
pagingindex: 0,
pagingresults: 10
}
};
and when i call fetch(url_object.url, url_object.url_params) i get an error. How can i incorporate this into fetch? So i need to allow user to define method and query string that will be passed. Thanks in advance!
You can have your cake and eat it too -- easily convert dictionary style objects to query parameters with no fuss:
var url = new URL('https://example.com/');
url.search = new URLSearchParams({blah: 'lalala', rawr: 'arwrar'});
console.log(url.toString()); // https://example.com/?blah=lalala&rawr=arwrar
The object you have labeled url_params is described by the MDN documentation as:
An options object containing any custom settings that you want to apply to the request.
It then goes on to list the properties you can include there.
pagingindex and pagingresults are not among them.
The query string is part of the URL. If you want to put data there, then put it in the URL.
const url_object = {
url: `https://t1.testing.com/test/api/v1/blog?pagingindex=0&pagingresults=10`,
url_params: {
method: "POST"
}
};
You may wish to use the URL object to construct the URL (it will handle escaping for you).
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.
I seem to be having an issue in stringify'ing then pasing from a url object
I simply stringify my object and set the location (with angulars $location) like so
currentUrl = {"module1" : {"is" : true} }
$location.search(JSON.stringify(currentUrl));
So this parses to the url just fine, however when I try to grab it from the url i get this back
console.log($location.search());
---
Object {{"module1":{"is":true}}: true}
How do I parse this back into an object so I can use it? If I do
JSON.parse($location.search());
I get a syntax error. I maybe because of how search returns the object? I am a bit confused here, could use some help. Thanks!
So I put it in the url with
$location.search(JSON.stringify(currentUrl));
What are the steps I need to take to get it back into this form :
{"module1" : {"is" : true} }
Edit -
It just appears it's setting the json object as the key in the location like
{ "mystrigifiedobject": true }
Edit2 :
based off the first edit, I was able to solve it (assming it's set in the locations object key) like so :
currentUrl = $location.search();
currentUrl = JSON.parse(Object.keys(currentUrl));
console.log(currentUrl);
This just feels a little weird though, am I doing something wrong here?
$location.search() returns the parsed search items from the url path as an object. This means this kind of url:
?a=b&c=d
will result in this object:
{ a: 'b', c: 'd' }
When you call this function:
currentUrl = {"module1" : {"is" : true} }
$location.search(JSON.stringify(currentUrl));
your path will look like this:
?%7B%22module1%22:%7B%22is%22:true%7D%7D
and the parsed object returned from $location.search will look like this:
{{"module1":{"is":true}}: true}
not that this is an object with one entry and the key is your JSON
So what you need to do in order to get your object back is this:
var parsedObject = $location.search();
var yourObject = JSON.parse(Object.keys(parsedObject)[0]);
see this jsfiddle: http://jsfiddle.net/HB7LU/11633/
But please note: you should encode your string when putting it in a url:
$location.search(encodeURIComponent(JSON.stringify(currentUrl)));
$routeParams provides access to both routing template values and query string values (as a string).
function ctrl($routeParams) {
var yourValueAsString = $routeParams.yourKey;
}
function ctrl2($location) {
$location.search('yourKey', JSON.stringify(...));
}
A better alternative would be to switch to using the UI Router which deals with this better.
I've got a very simple object, called "tag" that has only a string primary key. When I go to edit it...
var tag = Restangular.one('tags', 'cat')
.get(function() { // fetches { id: 'cat' } from server
// edit the tag
tag.id = 'dog';
// save changes
tag.post().then(beHappy, beSad);
});
The request that gets sent off is a call to:
POST /tags/cat/dog
with the correct data:
{ id: 'dog' }
I have no doubt that everything work fine if I wasn't trying to modify my primary key, but my server-side REST API isn't expecting the /oldId/newId format, and so I get a 404. Is this a bug in Restangular, or does my REST API need repairing?
Apparently I've been doing CRUD wrong, doing POST for updates and PUT for creates, which is exactly backwards.
Changing it to tag.put() fixed it.
I don't get what I'm doing wrong.
I'm trying to populate a form from a JSON string from the server and it doesn't work. I get nothing at all back. I examine the object and it's undefined. I've been beating my head against the wall for 3 days now. I need a simple example that works and I'll build from there.
Here's the simple example that I've been trying to use:
var messages = new Ext.data.JsonStore({
url: '/user/' + user_id,
method: 'GET',
root: 'user',
fields: [
{name: 'user_id'},
{name: 'first_name'}
],
listeners: {
load: messagesLoaded
}
});
messages.load();
function messagesLoaded(messages) {
console.log(messages);
}
Here's my JSON string:
{"success":"true","user":{"user_id":"2","first_name":"Test","last_name":"Test","email":null,"password":null,"city_id":"6379","birth_date":"2009-06-09","gender":"F","created_on":"2009-06-01 17:21:07","updated_on":"2009-06-14 17:20:14","active":"Y","cuisine_id":null}}
I really don't see what I'm doing wrong, but my JSON string isn't loading. Thanks!
Ok so you're almost there, but one problem. The root ("user" in this case) has to be an array. Even if it's an array with only 1 object. Ext.data.JsonReader (the default reader for a Ext.data.JsonStore) only accepts an array of results.
So your javascript looks just fine, but the JSON object returned by the server needs to look more like this.
{
"success":"true",
"user": [{
"user_id":"2",
"first_name":"Test",
"last_name":"Test",
"email":null,
"password":null,
"city_id":"6379",
"birth_date":"2009-06-09",
"gender":"F",
"created_on":"2009-06-01 17:21:07",
"updated_on":"2009-06-14 17:20:14",
"active":"Y",
"cuisine_id":null
}]
}
One more thing, consoloe.logging your store object will produce something like [Object] in Firebug... not too useful. You should either console.dir it, or log your actual data instead.
One comment about loading your form, once you get past loading your JSON (even though this example does not show that). Make sure your form is actually rendered before trying to load it with data, e.g. if trying to use something like form.loadRecord. Otherwise you'll end up with an empty form and no errors.