Im a novice in NuxtJS. My page is structured with a navbar/menu, blog page with all articles listed and a couple of mostly static pages.(like most company websites)
I am retrieving my data from a Strapi API, where I can only fetch single entries by their ID.
What I have to do:
When the user clicks on the navbar link, I need to pass the ID of the corresponding article/post to the article component in order to retrieve the article from the API. So far it is working quite well, but doing it via router params, an article will have an URL like https://www.example.com/posts/63ndjk736rmndhjsnk736r
What I would like to do:
I would like to have an URL with a slug https://www.example.com/posts/my-first-Post and still pass the ID to the article component/page.
Whats the best way to do that?
You can use query. Here is some example:
I have some data:
data = [{id: "63ndjk736rmndhjsnk736r", name: "my-first-post"}, {id: "88ndjk736rmndhjsnk736r", name: "my-second-post"}]
In my navbar list:
<template v-for="(item, index) in data" key="index">
<router-link :to="{ path: '/posts/' + item.name, query: { id: 'item.id' }}"
>{{item.name}}</router-link>
</template>
How your routes show:
http://example.com/posts/my-first-post?id=63ndjk736rmndhjsnk736r
What you need:
Create dynamic Route -> https://nuxtjs.org/guide/routing/#dynamic-routes
Pass query named id which is your single entry ID
Get query (id) and fetch your single entry using query id:
const postId = this.$route.query.id
You can use like _slug.vue in pages folder.
and your routes like this;
{
name: 'posts-slug',
path: '/posts/:slug?',
component: 'pages/posts/_slug.vue'
}
then create a new vue file.
export default {
validate ({ params }) {
return /^\d+$/.test(params.id)
}
}
this way you can also access the parameter
Related
I am reading a set of links from a data source (using $content) and would like to generate a list of HTML elements that will link to the respective page.
// test.yaml in content directory
links:
- id: a
title: A
- id: b
title: B
- id: p
title: P
Now, I would like to loop through this data and generate a set of HTML links
<!-- page.vue in pages directory -->
<template>
<div v-for="link in this._links" :key="link.id">
<NuxtLink to="/whatToPutHere">{{link.title}}</NuxtLink>
</div>
</template>
// script
<script>
export default {
async asyncData({ $content, params }) {
const _links = await $content("test").fetch();
return _links;
}
};
</script>
For each of the items I would like a link such as:
For id a, the link should be /content/a
For id b, the link should be /content/b
Assume that the slug for the above links exist and the pages work as intended. Thanks
you can use
<NuxtLink :to="`/content/${link.id}`">{{link.title}}</NuxtLink>
and it should work
In vue, you don't need to add the whole v-bind:tag and can just use :tag.
also if you v-bind any tag, whatever is in between quotations will be javascript code.
I'm attempting to set data fields provided by an array based on the Vue Router query. For example, when someone lands on my website using example.com/?location=texas, I want to set the location data by an array.
An example the array:
locations {
{
slug: "texas",
tagline: "Welcome to Texas",
}, {
slug: "california",
tagline: "Welcome to California",
}
}
I know this should be done using a computed property, however I am unable to get anything functioning. I've tried simple tests like if (this.slug.location === "texas"), and I cannot get the location data to populate. I would also like to provide default data in case there are no route matches.
Any help is extremely appreciated!
Edit:
I can accomplish this in a very manual way. Right now, I'm setting the query in data by the following:
slug: this.$route.query.location
I can display specific text by doing something like:
h3(v-if="slug === 'texas'") This will show for texas
h3(v-else-if="slug === 'california'") This will show for California
h3(v-else) This is default
The issue with this approach is there are various elements I need to customize depending on the slug. Is there any way I can create an array, and move whichever array matches a key in an array to the data??
You should be able to access a query param using the following (link to Vue Router documentation):
this.$route.query.location
So based on what you listed I would do something like...
export default {
computed: {
displayBasedOnLocationQueryParam() {
switch(this.$route.query.location) {
case 'texas':
return 'Welcome to Texas'
default:
return 'hello there, generic person'
}
}
}
}
Note that I'm not using your array explicitly there. The switch statement can be the sole source of that logic, if need be.
Newbie to Vue Router and really struggling to put this together even after reading the docs like 2 times
What pages?
I have a news website where the page shows list of items on the left and shows details on right when clicked on item list
I have a filter search and tag functionality on that page
Default URL
'/' corresponds to home page with filter=latest search=empty string and tag=all and shows latest posts in descending order of their published date with any tag on them
Search
/news?search=china
search query is not present in the URL when search is empty string
Filter
/news?filter=likes
filter has 3 values: 'likes', 'dislikes' and 'latest'
filter query is not present in the URL when filter is 'latest'
Tag
/news/trump will show all posts with the tag trump on them
tag param is not present in the URL when tag is 'all'
Combinations
Search, filter and tag can be combined together
/ where filter = latest, search = empty string and tag = all
/news?filter=likes&search=china where filter = likes, search = china and tag = all
/news/trump?filter=likes&search=china where filter = likes, search = china and tag = trump
Item URLS
Clicking on an item in the list changes URL at the top to an item URL
/news/:id/:title for an item when no tag is specified meaning tag=all
/news/:tag/:id/:title for an item when clicking on an item while viewing say items under trump tag
What have I tried?
import Vue from 'vue'
import Router from 'vue-router'
import Index from '~/pages/index'
Vue.use(Router)
export function createRouter() {
return new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'Index',
component: Index,
},
{
path: '/news',
name: 'News',
component: Index,
},
{
path: '/news/:tag',
name: 'TaggedNews',
component: Index,
},
{
path: '/news/:id([a-f0-9]{32})/:title',
name: 'NewsItem',
component: Index,
},
{
path: '/news/:tag/:id([a-f0-9]{32})/:title',
name: 'TaggedNewsItem',
component: Index,
},
],
})
}
What is the problem?
Lets say I am at /news?filter=likes&search=china , I click on an item and go to /news/123456/some-random-title now I have lost the query and search values if try to navigate to another list
When I change my filter to latest it needs to be removed from URL
When I set search to empty string it needs to be removed from URL
If I enter /?filter=likes it should take me to /news?filter=likes
If I enter /news I should go back to /
If I enter /news/all I am back to /
If I enter /news/all?search=china I am redirected to /news?search=china
Basically the default values are managed in the URL by not showing them
How do I differentiate between me changing the URL by pushing it vs user pressing back/forward buttons and the URL getting changed because of that?
How do I achieve this?
I'm creating a portfolio with vue, vuex and vue-router that will show images.
On the homepage i will show images with 'show_home: true'.
Then there is "tag" pages (portfolio/:tagSlug) that will show images based on a slug, eg. 'weddings' with infinite scroll (auto populate pagination).
An image object will look something like:
{
id: 1,
title: 'Lorem..',
path: '..',
show_home: true
tags: [ {id: 1, slug: 'weddings'} ]
},
{
id: 2,
title: 'Lorem2..',
path: '..',
show_home: false
tags: [ {id: 1, slug: 'weddings'}, {id: 2, slug: 'water'} ]
}
Endpoints examples:
Homepage: GET: ../images/homepage?p=1
Tag page: GET: ../images/:slug?p=1
I can't figure out how I should structure this in vuex and handle the fetching..
Should i just create i single 'images: []' state and populate it with ALL the images after fetching them from the api in each route, then filter them with getters? How can i get the pagination in there in that case? Or do you have a better solution?
Thanks in advance
My preferred approach is to "flatten" the relationships and pull them as needed. This also allows you to only pull what you need from the server or related modules.
tags vuex module:
all: {
1: { <-- indexed by tag id
name: "weddings"
images: [1,2,3,4] <-- image ids
}
}
active: false <-- When there is an active tag, this becomes the id of the tag.
The vuex images module would follow this same pattern:
all: {
1: { <-- indexed by image id
title: 'Lorem..',
path: '..',
show_home: true
tags: [1,2,3,4] <-- tag ids
}
}
active: false <-- When there is an active image, this becomes the id of the image.
Then use a getter to hydrate the images or tags from the respective vuex module.
There is a great write up on this approach on this blog: https://medium.com/js-dojo/structuring-vuex-modules-for-relationships-speed-and-durability-de25f7403643
With this approach you will have fewer and smaller api calls, pagination is manageable and you don't need to worry about stale data in your relationships.
EDITED -- API info:
Two approaches come to mind.
1) always load the images with the tag.
Tag index request would not load any images, just the basic info for each tag.
When the user clicks on a tag, this inits an API call for the tag details:
Tag show request (tags/1 or tags/weddings) would return the tag with loaded relationships:
public function show($id)
{
$tag = Tag::where('id', $id)->with('images')->firstOrFail();
return new TagResource($tag); <-- will have all related images loaded.
}
2) set up a nested REST endpoint if needed
You can use the the resource controllers to shortcut the boilerplate like this:
api.php
Route::apiResource('tags.images', 'tags\TagImageController');
This route will watch your api calls and determine if it is index/store/show/delete. From your front end you can make a call like https://backendsite.com/tags/1/images (If wedding tag has an id of 1)
Then in the TagImageController you would have something like this:
public function index(Request $request, $id)
{
$tag = MemTag::find($id);
$images = $tag->images()->get();
$images->load(Image::allowedIncludes); <- or you can manually list relationships you want to load
return ImageResource::collection($images);
}
This seems like a really simple issue, but it's driving me crazy...
Does anyone know how I can specify a dynamic :id parameter in the href routing configuration option?
The following unfortunately doesn't work:
config.map([
// ... default parameterless routing here
{
route:[':id/request'],
moduleId:'processes/bdd/request/request',
name:'Request', title:'Request', href:`#/bdd/request/${id}/request`, settings:{type:'Request', icon:''}, nav:true,
},
{
route:[':id/requestAuth'],
moduleId:'processes/bdd/request/requestauthorization',
name:'RequestAuthorization', title:'Request Authorization', href:`#/bdd/request/${id}/requestAuth`, settings:{type:'Request', icon:''}, nav:true,
},
// ... some additional mappings here
]);
The href property is static. If you want to generate a route for a link using this route, you could use the route-href custom attribute like this:
route-href="route: request; params.bind: { id: someProp }"
Note that I changed the route name to be camelCase (all lowercase since it is one word here) to match the route naming convention.
I had a similar use case and I was able to get this to work by adding a pipeline step to the router that alters the config on the fly.
My use case may be a little different in that I only want the item to appear in the nav bar when the route is active -- say I have routes /abc, /def/:id, and /ghi -- when the active route is ABC or GHI, only those items will appear in the nav bar, but when the active route is DEF, it should appear in the nav bar, and clicking it should lead to the same DEF ID you're currently looking at. So my route configuration includes a setting that indicates the route should only appear in the nav bar when it's the active route.
Here are the interesting parts of my actual configureRouter function:
configureRouter(config, router) {
config.addPreActivateStep(Preactivate); // explained below
config.map([
// some routes
{ route: 'patients/:patientId', name: 'patient-master',
moduleId: 'patients-and-cases/patient-master/patient-master',
title: 'Patient', nav: true, settings: { renderOnlyWhenActive: true },
href: '#' // it doesn't matter what this is
},
// more routes
]);
}
And here is the Preactivate class that sets the href on preactivate:
class Preactivate {
run(routingContext, next) {
routingContext.config.href = routingContext.fragment;
return next();
}
}
If, unlike me, you want this to display in a nav bar all the time, this will still work -- the href will simply remain set to the last thing it was set to when the route was active. In that case you'd probably want to initialize it to some default value that makes sense.