Vue Routing for filters with multiple queries - vue.js

I’m writing my first vue-app and while implementing the filters a lot of questions arise.
I have created lists with checkboxes for each property. The checkboxes have a change-event to adjust the url and send a get-request. One key can have several values. How can I implement this with vue?
A first test to adjust the URL looked like this:
router.push({ name: 'transactions' , query: { property: item.value} });
This works fine with one value, but I want to have multiple values for one key.
Many pages solve this with: xy.com/z?property=x%y
I could not find out if this scheme is only recommended or if it has to be solved that way. How I can implement it with vue I could not find out too.
I would also like to know how I can extend the route. The following statement always overwrites the URL instead of extending it.
this.$router.push({ name: 'transactions', query: Object.assign({}, this.$route.query, { property: item.value })});
I am grateful for any help

Related

Nuxt encode/decode URI with double colon

My URLs have double colon on them.
I push a path to Nuxt router which has : as a part of it.
export default {
router: {
extendRoutes (routes, resolve) {
routes.push({
name: 'custom',
path: 'towns' + '(:[0-9].*)?/',
component: resolve(__dirname, 'pages/404.vue')
})
}
}
}
When I point to http://localhost:3000/towns:3 , for example, the : is translated as %3Aon the URL leading to this error message:
Expected "1" to match ":[0-9].*", but received "%3A2"
How to revert this to : ?
I tried encodeURI(), decodeURI(), encodeURIComponent() and decodeURIComponent() in vain.
A demo for the ones who wants to try: nuxt-extend-routes
Any suggestions are welcome
Vuex is using vue-router and vue-router is using path-to-regexp to parse router path configuration
It seems to me, that you are trying to use Unnamed Parameters which doesn't make sense because vue-router/vuex need the name of the parameter to pass it down to Vue component behind the route
Why don't just use named parameters ?
{
path: '/towns:id(:\\d+)',
name: 'Page 3',
component: Page3
}
Sure, result will be that $route.params.id value will be prefixed with : and all router-link params must be :XX instead of 'XX' but that's something you can deal with. vue-router (path-to-regexp) is using : to "mark" named path parameters ...there's no way around it
You can take a look at this sandbox. Its not Nuxt but I'm pretty sure it will work in Nuxt same way....
Update
Well it really doesn't work in Nuxt. It seems Nuxt is for some reason applying encodeURIComponent() on matched path segments and throws an error. It works when server-side rendering tho (it throws some error on client still)...
Firstly, I concur with Michal Levý's answer that there's a library bug here. The line throwing the error is here in the Nuxt source:
https://github.com/nuxt/nuxt.js/blob/112d836e6ebbf1bd0fbde3d7c006d4d88577aadf/packages/vue-app/template/utils.js#L523
You'll notice that a few lines up the segment is encoded, leading to : switching to %3A.
However, this line appears to have originated from path-to-regexp:
https://github.com/pillarjs/path-to-regexp/blob/v1.7.0/index.js#L212
It isn't trivial to fix this bug because the encoding is not simply 'wrong'. There's a lot of stuff going on here and by the time that line is reached the parameter values have been URL decoded from their original values. In the case of our unencoded : that causes problems but in other cases, such as matching %3A, the encoding would be required.
The handling of encoding within path-to-regexp is a delicate topic and we aren't helped by the old version being used. This also makes it more difficult to come up with a suitable workaround in your application.
So, let's see what we can do...
To start with, let's consider the path:
path: 'towns' + '(:[0-9].*)?/',
Bit odd to concatenate the strings like that, so I'm going to combine them:
path: 'towns(:[0-9].*)?/',
The / on the end isn't hurting but it seems to be unnecessary noise for the purposes of this question so I'm going to drop it.
On the flip side, not having a / at the start can cause major problems so I'm going to add one in.
The .* is suspicious too. Do you really mean match anything? e.g. The current route will match towns:3abcd. Is that really what you want? My suspicion is that you want to match just digits. e.g. towns:3214. For that I've used [0-9]+.
That leaves us with this:
path: '/towns(:[0-9]+)?',
Now, the : problem.
In general, route paths are used in both directions: to match/parse the URL and to build the URL. Your use of an unnamed parameter makes me wonder whether you only intend to use this route for matching purposes.
One option might be this:
path: '/towns:([0-9]+)',
By moving the : outside the parameter it dodges the encoding problem.
There are two problems with the code above:
The colon/number suffix is no longer optional on the URL. i.e. It won't match the path /towns as the original route did. This can be solved by registering /towns as a separate route. I'm not aware of any other way to solve this problem with the available version of path-to-regexp.
You won't be able to use it to build URLs, e.g. with nuxt-link.
If you need to be able to use it to build URLs too then you could use a named parameter instead:
path: '/towns::town([0-9]+)',
The :: part here is potentially confusing. The first : is treated literally whereas the second : is used as a prefix for the town parameter. You might then use that with nuxt-link like this:
<NuxtLink :to="{ name: 'custom', params: { town: 4 } }">
...
</NuxtLink>

Vuex: Can I use function names of mutations/actions instead of constants?

Is it a good idea to use function name property instead of constants for actions/mutations, like the code below?
Author of Vuex official documentation says that it is not required to use constants for actions/mutations. So I want to try to use type is based on function name.
Component:
this.$store.dispatch(authActions.login.name, {
email: this.email,
password: this.password
})
Action:
async login(context, { email, password }) {
// some code
}
I am waiting for the following answers:
1) Yes, you can use it, there are no potential problems with this
approach.
2) Yes, but these problems [problems] can happen.
3) No, there are a lot of problems: [problems].
I think it's option number 2).
The problem comes when you want to dispatch namespaced modules actions.
You can do this and it does work, but I noticed when I minify my code for production it stops working.
I suspect there's some code that maps the function names (ie login()) to their minified version (ie h()) during bundling, and that's breaking things.
I've been trying to figure this out for a while with no luck. I agree defining constants to use as function names, instead of grabbing the name after like myFunction.name, is stupid. I'll update my comment if I figure it out. I'll keep trying...

Losing router.params on page refresh

I'm encountering an issue with vue-router.
I got a list of posts in the homepage. When I click on one of them, it redirects to the post page such as :
<router-link :to="{ name: 'article', params: {id: article.id, slug: article.slug } }"></router-link>
Everything is working perfectly, I can retrieve the data of the correct article using this.$route.params.id in a getter.
My issue : When I reload on an article page, this.$route.params.id is undefined, which causes the entire application to crash.
How can I save the router.params despite a page reload ?
What backend are you using? You need to enable history mode on your router, as well as make some additional configuration changes on your backend web server. Please refer to this link for the additional server side configuration changes you will need to make for this to work properly.
Also, please make note of this 404 caveat when using history mode..
Edit: you could try something like this since the ID remains persistent in the URL: Look for the solution from MIKE AXLE
I don't know if anyone else if facing the same issue, but I was having a problem getting route params on refresh. The route parameter I was trying to get was an ID number and I use that ID to fetch data and populate the page. I found (through many console logs) when I refreshed, the number was turning into a string and thats why the page was not working. I sorted it out but casting the ID to number before using it:
Number($route.params.id)
You can also use query instead of params
<router-link :to="{ name: 'article', query: {id: article.id, slug: article.slug } }"></router-link>
and to get value on redirecting page
this.$route.query
I'm not sure what is your route definition, the problem can be that only one of the params presented in the route URL (e.g. /article/:slug).
When you invoke a route (by clicking on router-link or calling router.push), you passing both params directly and they persist in the memory. That's why both are accessible.
But when you reload the page - everything that Vue can do is to parse your URL.
Means only one param parsed because only one param is present.
As a solution you can:
use both params in the route URL (e.g. /article/:id/:slug);
or use one param and call your API to retrieve remaining information (e.g. get id by slug if your route is /article/:slug).
I had the same issue pass the url in router
/article/:id/:slug
you will not loose your params after resfresh because whenrefreshng vue will take only data from url and forget if you passed params
You should route like this:
{
path: '/article/:id/:slug',
name: 'article',
}
In your view when routing do it like this:
this.$router.push({ name: 'article', params: { id: articleID, slug: articleSlug}});
You're welcome! 😉

Implementing Google Custom Search with filtering

I have to implement a page with multiple google-powered search forms. We have a license from Google for CSE, and this is the situation:
I have a search form that's present at the top of every page that performs a simple search and displays the results in a separate page. This works.
I have a particular page that, in addition, shows another two search forms: one should filter articles by category, another should filter articles by category and restrict the result to a certain month. I have added a meta key with the publication date to each article for this.
I have gotten a bit lost in the documentation, though: if I add
<gcse:searchbox-only resultsUrl="/[site]/stat/search/google_search_results.html"></gcse:searchbox-only></div>
to the page, I can't filter the results. If I start to meddle with a CustomSearchObject, I don't see an option to show results on a different page.
For category-based filtering, I've tried appending
more:pagemap:metatags-taxonomies:news
to the query argument in the results page URL, and it does work, but I don't understand how to inject this to the form.
For restricting based on dates, I tried adding
&sort=more:pagemap:metatags-pubdate:r:YYYYMMDD:YYYYMMDD
but haven't been able to make it work. Getting the XML does work:
http://www.google.com/search?q=intitle:[mysite]%20more:pagemap:metatags-taxonomies:News&sort=metatags-pubdate:r:20120401:20120830&cx=[mykey]client=google-csbe&output=xml
returns correct results.
Is there documentation that doesn't assume so much? All I find are code snippets without context. I've checked Filtering and sorting, Custom Search Element Control API, and of course this site, but I can't put all the pieces together.
I managed to implement what I wanted. In the search page, I built simple forms pointing to my results pages (this might not be doable if you must implement google branding), and in the results page I put the following:
(in the <head>)
<script src="http://www.google.com/jsapi"></script>
<script>
// This function extracts the query from the URL (if GET) or builds a search query.
// Code removed to simplify the example.
function buildQuery () {
return '<?php echo $_POST['q'];?> more:pagemap:metatags-taxonomias:News'); // injecting the taxonomy metatag filter
}
google.load('search', '1', {language : 'es'});
google.setOnLoadCallback(function() {
var customSearchOptions = {};
customSearchOptions[google.search.Search.RESTRICT_EXTENDED_ARGS] = {'sort':'metatags-pubdate:d,metatags-pubdate:r:<?php echo $_POST['startdate'];?>:<?php echo $_POST['enddate'];?>'}; // these come from the POST request, are processed earlier in the script.
var customSearchControl = new google.search.CustomSearchControl('XXXXXXXXXXX', customSearchOptions); // Put your own App key here.
customSearchControl.setResultSetSize(google.search.Search.FILTERED_CSE_RESULTSET);
var drawOptions = new google.search.DrawOptions();
drawOptions.enableSearchResultsOnly(); // I don't want the search box here
customSearchControl.draw('cse-results-press', drawOptions);
var query = parseQuery();
if (query) {
customSearchControl.execute(query);
}
}, true);
</script>
In the <body>:
<div id="cse-results-press">Loading...</div>

Passing variables into JavaScript in ExpressJS/PassportJS/Jade app

This is essentially a continuation of the question here: Nodejs Passport display username.
app.get('/hello', function(req, res) {
res.render('index.jade', { name: req.user.username });
});
So users log in via PassportJS, and goes to index.jade, which contains #{name} in the body, which will be replaced by the value of req.user.username.
Question: Is it possible to use the value of req.user.username in index.jade's JavaScript? I tried assigning its value to a variable but it doesn't work.
I have been using the trick of having a hidden input with #{name} as value:
input(type='hidden', id='variableName', value='#{name}')
Then JavaScript can access this value using:
$("#variableName").val()
This works. But does it have any potential downside like security issues? What is the right way to do this?
You have a few options. One of them is what you did and put the value inside you html. You can also solve it by doing:
script
window.name = #{name};
This will create an inline script that sets the variable. The other option you have is using ajax. That means you probably need to make an extra route to reply to that request.