VuePress : Custom Contents in Sidebar for Exery Single Page - vue.js

I would like to have custom links and different sidebar for every single page I don't want my headings render as table of contents in sidebar I would like to have custom content like this.
Node JS
-Lecture 1 Introduction
--Sub Lecture
-Lecture 2 Basics
--Sub Lecture
---Nested Lecture
Where all the lectures are custom links. So how can I do that.

sidebar
Type: false | 'auto' | SidebarConfigArray | SidebarConfigObject
Default: 'auto'
Details:
Configuration of sidebar.
You can override this global option via sidebar frontmatter in your pages.
Set to false to disable sidebar.
If you set it to 'auto', the sidebar will be automatically generated from the page headers.
To configure the sidebar items manually, you can set this option to a sidebar array, each item of which could be a SidebarItem object or a string:
A SidebarItem object should have a text field, could have an optional link field and an optional children field. The children field should be a sidebar array.
A string should be the path to the target page file. It will be converted to a SidebarItem object, whose text is the page title, link is the page route path, and children is automatically generated from the page headers.
If you want to set different sidebar for different sub paths, you can set this option to a sidebar object:
The key should be the path prefix.
The value should be a sidebar array.
In order to add custom links external as well as internal see this:
module.exports = {
themeConfig: {
// sidebar object
// pages under different sub paths will use different sidebar
sidebar: {
'/guide/': [
{
text: 'Guide',
children: ['/guide/README.md', '/guide/getting-started.md'],
},
],
'/reference/': [
{
text: 'Reference',
children: ['/reference/cli.md', '/reference/config.md'],
},
],
},
},
}
For more info see Sidebar Config

Related

How do I achieve this route structure with search filter and tag using Vue Router?

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?

How to fix navbar overlap on dropdown items with a vuepress site

There's always an overlap with Navbar dropdown when more than one is clicked. It focuses and takes a few minutes to clear this becomes a problem because it causes clutter.
The configuration for this in the Vuepress docs is just to add navbar items and ariaLabel any know how I can stop this behaviour.
themeConfig: {
nav: [
{
text: 'Languages',
ariaLabel: 'Language Menu',
items: [
{ text: 'Chinese', link: '/language/chinese/' },
{ text: 'Japanese', link: '/language/japanese/' }
]
}
]
}
Here's an example
To answer your question one would need to address two distinct issues:
how do I run custom JavaSCript in VuePress?
how do I close any previously open dropdowns on click in my current VuePress theme, using JavaScript?
For the first problem there are several solutions (one of them being by using a custom component with code run in its mounted() hook, but this would require you to include that component in every page and make sure it doesn't run more than one time (since you want to bind events to elements).
I believe the cleanest way would be by adding a <script> to <head> which can be achieved by adding this to the head prop of your .vuepress/config.js export:
head: [
// ...existing stuff, if any,
['script', {}, `
(function() {
// your code here...
})();
`]
]
However, there are a few problems with the above solution. Firstly, it's going to be run as soon as it's parsed, and that's inside the <head> tag. Which means none of the contents of your page are rendered yet. And the second problem is you're in a template literal. You don't really want to be writing JavaScript code in a template literal. Ideally you should be able to put your code in a '.js' file and append it as a <script> tag.
In order to do that, you need to create a .vuepress/public/ folder, if you don't already have one. Place your .js file in there (I used test.js but feel free to name it as you like). Modify the above code to:
['script', {}, `
(function() {
var s = document.createElement('script');
s.src = './test.js';
var h = document.querySelector('head');
h.appendChild(s);
})();
`]
Change ./test.js to your file's name.
Now your file has clean JavaScript and the door is open. Your code executes in the window object context.
To answer the second part of your question, well..., it largely depends on the theme you are using. If you're using the default theme (which seems to be the case, from the SS you posted), this should work, if placed inside your .js file:
document.addEventListener('DOMContentLoaded', fixDropDowns);
function fixDropDowns() {
document.body.addEventListener('click', (ev) => {
const header = document.querySelector('header');
if (header) {
const dds = header.querySelectorAll('.dropdown-wrapper');
[...dds].forEach(el => el.classList.remove('open'));
const curr = ev.target.closest('.dropdown-wrapper');
if (curr) {
curr.classList.add('open');
}
}
})
}
But it's based on a close inspection of the generated markup.
Specifically on the fact the dropdowns have a class of .dropdown-wrapper and that they're opened by toggling class open on them. The above is just an example and will likely not work on other themes and might even stop working on the default theme in some future version.

Vuex state structure and fetching when using same type of data with different values on different routes

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);
}

Make the sidebar sub-menu active even after reloading page

I want to make the sidebar active even after reloading a page.
For e.g In user sidebar there are 2 sub-menu of user. When I click on user sidebar menu it expands and show below 2 fields with active menu:
User name
User address
Like I selected user address option it is active now which is working properly. But I want to remain active even after reloading a page. For now, on reloading page, user sidebar will collapsed and not active. In data file, I have defined the menu as mentioned below:
{
action: 'zmdi-widgets',
title: 'message.widgets',
items: [
{ title: 'message.username', path: '/widgets/user-name' },
{ title: 'message.useraddress', path: '/widgets/user-address' }
]
}
When above data contains an active field(boolean) which is true, sidebar menu shows active. I want to make an active field dynamically. On giving static value of active field it works fine even after reloading a page but on giving dynamically it does't work. Why Anyone knows? I am managing all this with vuex.

Dynamic parameter in href option in Aurelia Routing config.map

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.