Ember Optional URL Segments - javascript

Is there a way in Ember to have optional segments in a url?
That is I have the :locale in /:locale/reservations/:id be optional so that /fr/reservations/12345 works as well as /reservations/12345?
My current router looks like this:
App.Router.map ->
#resource 'reservation', path: '/*locale/reservations/:id', ->
#route('status', path: '/status')

Have a look here emberjs using multiple paths / urls for one route or in serialize try smthing like:
serialize: function(model, params) {
var locale, id;
if(model.locale)
locale = model.locale;
else
locale = '';
id = model.id;
return { locale: locale, id: id };
}

Related

How to set URL query params in Vue with Vue-Router

I am trying to set query params with Vue-router when changing input fields, I don't want to navigate to some other page but just want to modify url query params on the same page, I am doing like this:
this.$router.replace({ query: { q1: "q1" } })
But this also refreshes the page and sets the y position to 0, ie scrolls to the top of the page. Is this the correct way to set the URL query params or is there a better way to do it.
Edited:
Here is my router code:
export default new Router({
mode: 'history',
scrollBehavior: (to, from, savedPosition) => {
if (to.hash) {
return {selector: to.hash}
} else {
return {x: 0, y: 0}
}
},
routes: [
.......
{ path: '/user/:id', component: UserView },
]
})
Here is the example in docs:
// with query, resulting in /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
Ref: https://router.vuejs.org/en/essentials/navigation.html
As mentioned in those docs, router.replace works like router.push
So, you seem to have it right in your sample code in question. But I think you may need to include either name or path parameter also, so that the router has some route to navigate to. Without a name or path, it does not look very meaningful.
This is my current understanding now:
query is optional for router - some additional info for the component to construct the view
name or path is mandatory - it decides what component to show in your <router-view>.
That might be the missing thing in your sample code.
EDIT: Additional details after comments
Have you tried using named routes in this case? You have dynamic routes, and it is easier to provide params and query separately:
routes: [
{ name: 'user-view', path: '/user/:id', component: UserView },
// other routes
]
and then in your methods:
this.$router.replace({ name: "user-view", params: {id:"123"}, query: {q1: "q1"} })
Technically there is no difference between the above and this.$router.replace({path: "/user/123", query:{q1: "q1"}}), but it is easier to supply dynamic params on named routes than composing the route string. But in either cases, query params should be taken into account. In either case, I couldn't find anything wrong with the way query params are handled.
After you are inside the route, you can fetch your dynamic params as this.$route.params.id and your query params as this.$route.query.q1.
Without reloading the page or refreshing the dom, history.pushState can do the job.
Add this method in your component or elsewhere to do that:
addParamsToLocation(params) {
history.pushState(
{},
null,
this.$route.path +
'?' +
Object.keys(params)
.map(key => {
return (
encodeURIComponent(key) + '=' + encodeURIComponent(params[key])
)
})
.join('&')
)
}
So anywhere in your component, call addParamsToLocation({foo: 'bar'}) to push the current location with query params in the window.history stack.
To add query params to current location without pushing a new history entry, use history.replaceState instead.
Tested with Vue 2.6.10 and Nuxt 2.8.1.
Be careful with this method!
Vue Router don't know that url has changed, so it doesn't reflect url after pushState.
Actually you can push query like this: this.$router.push({query: {plan: 'private'}})
Based on: https://github.com/vuejs/vue-router/issues/1631
Okay so i've been trying to add a param to my existing url wich already have params for a week now lol,
original url: http://localhost:3000/somelink?param1=test1
i've been trying with:
this.$router.push({path: this.$route.path, query: {param2: test2} });
this code would juste remove param1 and becomes
http://localhost:3000/somelink?param2=test2
to solve this issue i used fullPath
this.$router.push({path: this.$route.fullPath, query: {param2: test2} });
now i successfully added params over old params nd the result is
http://localhost:3000/somelink?param1=test1&param2=test2
If you are trying to keep some parameters, while changing others, be sure to copy the state of the vue router query and not reuse it.
This works, since you are making an unreferenced copy:
const query = Object.assign({}, this.$route.query);
query.page = page;
query.limit = rowsPerPage;
await this.$router.push({ query });
while below will lead to Vue Router thinking you are reusing the same query and lead to the NavigationDuplicated error:
const query = this.$route.query;
query.page = page;
query.limit = rowsPerPage;
await this.$router.push({ query });
Of course, you could decompose the query object, such as follows, but you'll need to be aware of all the query parameters to your page, otherwise you risk losing them in the resultant navigation.
const { page, limit, ...otherParams } = this.$route.query;
await this.$router.push(Object.assign({
page: page,
limit: rowsPerPage
}, otherParams));
);
Note, while the above example is for push(), this works with replace() too.
Tested with vue-router 3.1.6.
Here's my simple solution to update the query params in the URL without refreshing the page. Make sure it works for your use case.
const query = { ...this.$route.query, someParam: 'some-value' };
this.$router.replace({ query });
My solution, no refreshing the page and no error Avoided redundant navigation to current location
this.$router.replace(
{
query: Object.assign({ ...this.$route.query }, { newParam: 'value' }),
},
() => {}
)
this.$router.push({ query: Object.assign(this.$route.query, { new: 'param' }) })
You could also just use the browser window.history.replaceState API. It doesn't remount any components and doesn't cause redundant navigation.
window.history.replaceState(null, '', '?query=myquery');
More info here.
For adding multiple query params, this is what worked for me (from here https://forum.vuejs.org/t/vue-router-programmatically-append-to-querystring/3655/5).
an answer above was close … though with Object.assign it will mutate this.$route.query which is not what you want to do … make sure the first argument is {} when doing Object.assign
this.$router.push({ query: Object.assign({}, this.$route.query, { newKey: 'newValue' }) });
To set/remove multiple query params at once I've ended up with the methods below as part of my global mixins (this points to vue component):
setQuery(query){
let obj = Object.assign({}, this.$route.query);
Object.keys(query).forEach(key => {
let value = query[key];
if(value){
obj[key] = value
} else {
delete obj[key]
}
})
this.$router.replace({
...this.$router.currentRoute,
query: obj
})
},
removeQuery(queryNameArray){
let obj = {}
queryNameArray.forEach(key => {
obj[key] = null
})
this.setQuery(obj)
},
I normally use the history object for this. It also does not reload the page.
Example:
history.pushState({}, '',
`/pagepath/path?query=${this.myQueryParam}`);
The vue router keeps reloading the page on update, the best solution is
const url = new URL(window.location);
url.searchParams.set('q', 'q');
window.history.pushState({}, '', url);
With RouterLink
//With RouterLink
<router-link
:to="{name:"router-name", prams:{paramName: paramValue}}"
>
Route Text
</router-link>
//With Methods
methods(){
this.$router.push({name:'route-name', params:{paramName: paramValue}})
}
With Methods
methods(){
this.$router.push({name:'route-name', params:{paramName, paramValue}})
}
This is the equivalent using the Composition API
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
router.push({ path: 'register', query: { plan: 'private' }})
</script>
You can also use the Vue devtools just to be sure that it's working as expected (by inspecting the given route you're on) as shown here: https://stackoverflow.com/a/74136917/8816585
Update
That will meanwhile mount/unmount components. Some vanilla JS solution is still the best way to go for that purpose.

Get Current URL with query parameters in Backbone Marionette

URL : http://localhost:3000/dashboard?ID=10400&Name=10400
I'm trying to get the query params ID and Name from the URL but I get undefined. I have also tried backbone-queryparams but still it does not work. Any idea how to get the current URL with params in Backbone Marionette
define([
'jquery',
'backbone',
'marionette',
'modules/dashboard/controllers/dashboardController',
'backbone.queryparmas'
], function ($, Backbone, Marionette, Controller) {
'use strict';
return Marionette.AppRouter.extend({
appRoutes: {
'': 'dashboard'
},
initialize: function(){
console.log( Backbone.history.fragment ); // getting undefined
},
controller: new Controller()
});
});
I had to do this to get the query params. Not sure if there is any better way.
messagedashboard: function () {
var searchParams = window.location.search.slice(1); // returns 'ID=10400&Name=10400'
var getParamsFromSearchParams = $.deparam(searchParams); //changes into object
}
For using $.deparam check jquery.bbq library.
From the example here (https://stackoverflow.com/a/11671457/3780922), it looks like the best option is to set a route with a catchall parameter, which will give you the entire query string.
appRoutes: {
'': 'showDash', //default for blank/empty route
'dashboard': 'showDash',
'dashboard?*queryString' : 'showDash'
},
showDash: function (queryString) {
var params = parseQueryString(queryString);
if(params.foo){
// foo parameters was passed
}
}
You'll have to write your own query string parser, however, but if it is not null, then you have your query parameter passed in the queryString object, which in your example would be "ID=10400&Name=10400"

EmberJS: Customize the REST URL of a related model

In the Ember Documentation, it mentions this:
An adapter is an object that knows about your particular server
backend and is responsible for translating requests for and changes to
records into the appropriate calls to your server.
For example, if your application asks for a person record with an ID
of 1, how should Ember Data load it? Is the URL /person/1 or /resources/people/1?
In my example, I have a User model with related Messages. When the Ember store retrieves related messages for a user, it generates a query in the form /messages/?ids=18 using something like this:
user = this.store.find('user', params.user_id);
user.get('messages');
I'd like to load these messages with a URL like /users/2/messages/.
I looked through Ember's documentation on adapters and wasn't able to find anything to implement this specifically. How do I customize the URL?
You can create a message adapter that builds the right url for the end point.
import Ember from 'ember';
import DS from 'ember-data';
export default DS.ActiveModelAdapter.extend({
find: function(store, type, id) {
return this.ajax(this.buildURL(type.typeKey, id), 'GET');
},
createRecord: function(store, type, record) {
var data = {};
var serializer = store.serializerFor(type.typeKey);
serializer.serializeIntoHash(data, type, record, { includeId: true });
return this.ajax(this.buildURL(type.typeKey, null), "POST", {data: data});
},
buildURL: function(type, id) {
var url = [],
host = Ember.get(this, 'host'),
prefix = this.urlPrefix(),
user_id = this.container.lookup('controller:user').get('id');
// append user and id to the url
url.push('users/' + user_id);
if (type) { url.push(this.pathForType(type)); }
if (id) { url.push(id); }
if (prefix) { url.unshift(prefix); }
url = url.join('/');
if (!host && url) { url = '/' + url; }
return url;
}
});
I hope this is helpful.
Cheers

Strict parameter matching in backbone.js router

I've got a router defined as such:
var MyRouter = Backbone.Router.extend({
routes: {
// catch-all for undefined routes
"*notfound" : "notFound",
},
initialize: function(options) {
this.route("my_resource/clear_filters", 'clearFilters');
this.route("my_resource/:id", 'show');
},
show: function(id){
console.log('show', id);
},
clearFilters: function() {
console.log('clearFilters');
},
notFound: function() {
console.log('notFound');
},
});
var app = {};
app.MyRouter = new MyRouter();
Backbone.history.start({silent: true});
Thus the following URLs would map as:
var opts = {trigger: true};
app.MyRouter.navigate('/foo', opts); // logged -> 'notFound'
app.MyRouter.navigate('/my_resource/123', opts); // logged -> 'show', '123'
app.MyRouter.navigate('/my_resource/clear_filters', opts); // logged -> 'clearFilters'
app.MyRouter.navigate('/my_resource/some_thing', opts); // logged -> 'show', 'some_thing'
How can I restrict the my_resource/:id route to only match on numeric parameters so that app.MyRouter.navigate('/my_resource/some_thing') is handled by notFound?
From the fine manual:
route router.route(route, name, [callback])
Manually create a route for the router, The route argument may be a routing string or regular expression. Each matching capture from the route or regular expression will be passed as an argument to the callback.
So you can always say things like:
this.route(/my_resource\/(\d+)/, 'show')
in your router's initialize if you need finer grained control over the routes than Backbone's string patterns give you.

Setting up Backbone router

Can't figure out what's going wrong with my Backbone router. Can anyone spot a mistake in the following block of code? The index route is working fine but the classes route isn't ever triggering (e.g. when I navigate to a URL like localhost/classes/test)
var app = app || {};
$(function() {
app.Router = Backbone.Router.extend({
routes: {
'' : 'index',
'classes/:id' : 'classes'
},
initialize: function() {
this.classList = new app.ClassCollection();
},
index: function() {
this.menuView = new app.ClassCollectionView({collection: this.classList});
},
classes: function(id) {
console.log("hello")
var _class = new app.ClassModel({id: id});
this.classView = new app.ClassPageView({model: _class});
}
});
router = new app.Router();
Backbone.history.start({pushState: true});
})
If everything looks in order, there's probably a bug somewhere else in my code.
Backbone.router is extending hashbang navigation.
so
localhost/#classes/test
should lead to your method. ALSO! pay attention that emty route should be at the end of the routes list.
It's like else if construction, if route matches "" (default # ?!) it will never match other routes
by default the route will work with the hash try localhost/#classes/test

Categories