Render a view ignoring router template - vuejs3

I am creating an Admin Dashboard with vitejs, vue3 and vue-router. Exists 2 different templates for all views:
Views having a sidebar:
/buildings
/users
/companies
View with a full-width where the user can login or signup:
/login
/signup
Each component is render in the App.vue file with a sidebar:
<template>
<div class="grid grid-cols-6">
<div class="bg-blue-400">
<ul>
<li><router-link to="/buildings">Buildings</router-link></li>
<li><router-link to="/companies">Companies</router-link></li>
</ul>
</div>
<div class="col-span-5">
<div class="p-10">
<router-view/>
</div>
</div>
</div>
</template>
However, for /login view I need to display a different template because there is no reason to display a sidebar with links in a login form. Because both cases differ in template format, how can I use different templates for a unique router but for different views? Many thanks for considering my request.
I have tried Nested routes, but I am forced to changing all my routes to uses children key, for example:
// Routes dashboard
{
path: '/admin',
component: TemplateWithSidebar, // template 1
children: [
{
path: 'companies',
component: CompaniesView,
}
]
},
// Routes for login
{
path: '/l',
component: TemplateWithoutSidebar, // template 2
children: [
{
path: 'login',
component: LoginView,
}
]
},
Then my App.vue was changed to:
<template>
<div>
<router-view/>
</div>
</template>
I am not experienced enough to determinates if this practice is valid for a production environment o weather exists another best alternative.

Related

Vue - requests and watch data on unmounted components

I'm new to vue and strugling with some props and attributes.
I have a vue application where the main app is calling three different components:
Navbar,
Sidebar
MapContainer
In Navbar user will fill a selector with information about city and states. After user presses search, the list of results will then show up in the Sidebar menu.
Sidebar itself is a simple component carrying only a router-view for it's children components which are Results and Details
Results will receive the result of the search performed in the Navbar component. When user clicks any item in the Results component, Sidebar will then load Details taking the place of Results with detailed information about that place.
the problem is that the data used to make the request(city and states) comes from the first component navbar. I'm passing this data to sub-components using vue-router params option. When component Results gets unmounted, I lost all the data that was passed, even adding a Watch couldn'd fix the problem and thus can't return back to the previous page. Even adding a Watch couldn'd fix the problem. What's the proper way to handle data across components that area unmounted?
Navbar.vue
<template>
<div class="navbar">
<div class="logo">
Logo
</div>
<div class="middle">
<div class="flex-selectors">
<div class="navbar-options">
<select v-model="state_id" class="main-selectors" #click="load_cities">
<option v-for="state in states" :value="state.state_id" :key="state">
{{ state.state }}
</option>
</select>
<select v-model="city_id" class="main-selectors">
<option v-for="city in cities" :value="city.city_id" :key="city">
{{ city.city }}
</option>
</select>
</div>
<div class="search">
<button #click="seach">
<router-link :to="{name: 'results', params: {state: state_id, city: city_id} }" aria-current="page" title="Resultados">
<div>Search</div>
</router-link>
</button>
</div>
</div>
</div>
</template>
Sidebar.vue
<template>
<div class="sidebar">
<router-view></router-view>
</div>
</template>
Results.vue
<template>
<div v-if="areas.length">
<router-link :to="{name: 'details' }" tag="div" class="container" #click="load(area)" v-for="area in areas" :key="area" :value="area">
<!-- BUNCH OF DIVS AND V-FOR -->
</router-link>
</div>
<div v-else>
NA
</div>
</template>
<script>
export default {
data() {
return {
state_id: this.$route.params.state,
city_id: this.$route.params.city,
areas: [],
}
},
methods: {
search(state_id, city_id) {
load_areas.get(state_id, city_id).then(
result => {
this.areas = result.data
}
)
}
},
mounted() {
this.emitter = inject('emitter')
this.searchAreas(this.state_id, this.city_id)
},
created() {
this.$watch(
() => this.$route.params,
(toParams, previousParams) => {
this.searchAreas(toParams.state, toParams.city)
}
)
},
watch: {}
}
</script>
What's the proper way to handle data across components that are
unmounted?
You cannot access data from an unmounted component.
When multiple components need to access or modify the same data, a good option to look into is state management. Pinia is the new official library recommendation for state management.
Instead of passing data through vue-router params, create a store for it. Set results of your search query from Navbar component and access it in Results component. When Results component gets unmounted, you won't lose the data.

How to route html pages on clicks in meteor?

I am a newbie in Meteor. I am developing an app having a login page that must redirect to certain pages as per the login id.There are certain click events which opens up html pages.I have the hard code data in the pages to check the flow now.I have the html pages as well designed, but I am not able to link them for click events and login. Please help.
Here is one way to do it:
In your HTML file, something like this:
<head>
<title>Duckbilled Platypus</title>
</head>
<template name='layout'>
{{> banner}}
{{> yield}}
</template>
<template name="banner">
<h1 class="chocolatefont">Platypi of the World Unite! (Duckbilled, that is)</h1>
<hr/>
</template>
<template name="main">
<div id="templateMain" name="templateMain">
<h2>RAVES</h2>
<p>No Raves yet</p>
<h2>RANTS</h2>
<p>No Rants yet</p>
<h2>RANDOM</h2>
The Legend of NFN Oscar
<br/><br/>
NFN Oscar's donut
<br/><br/>
Alliteration Station ("Ben's Bizarre Bazaar")
<br/><br/>
Boomeranging Telescopic and Kaleidoscopic Phrase Mazes
<br/><br/>
Acrostics
<br/><br/>
Homonym Homie
</div>
</template>
...and then add whichever templates you want for the pages you want to route to; in my case, it's one for each "href" referenced in the anchor tags (nfnoscar, nfnoscarsdout. etc.)
The "yield" (which means, "insert here whatever the router says corresponds to the URL") requires Iron:Router, which you say you already have.
In your JS file, something like this:
Router.configure({
layoutTemplate: 'layout'
});
Router.route('/', {
name: 'main',
template: 'main'
});
Router.route('/nfnoscar', {
name: 'nfnoscar',
template: 'nfnoscar'
});
Router.route('/nfnoscarsdonut', {
name: 'nfnoscarsdonut',
template: 'nfnoscarsdonut'
});
Router.route('/alliterationstation', {
name: 'alliterationstation',
template: 'alliterationstation'
});
Router.route('/btakpm', {
name: 'btakpm',
template: 'btakpm'
});
Router.route('/homonyms', {
name: 'homonyms',
template: 'homonyms'
});
Router.route('/acrostics', {
name: 'acrostics',
template: 'acrostics'
});
Now, whichever link is clicked, the corresponding page is loaded by means of the "yield" and the Iron Router routing.
You can see this particular app and how it works when you click the links, etc., at my "sandbox" Meteorsite here.

Path works on desktop, but not mobile

I've a tab bar on a responsive app I'm building:
<template name="tabNav">
<nav class="bar bar-tab">
<a class="tab-item" id="groups-nav" href="{{pathFor 'groupsList'}}">
<span class="icon icon-star-filled"></span>
<span class="tab-label">Groups</span>
</a>
<a class="tab-item active" id="games-nav" href="{{pathFor 'locationSet'}}">
<span class="icon icon-list"></span>
<span class="tab-label">Games</span>
</a>
<!-- more code -->
</template>
The pathFor 'groupsList' works on desktop, but not on mobile. You can try it out here: pp-groups.meteor.com.
This is a prototype only and doesn't use any real data. All of my views code is available here: https://github.com/stewartmccoy/pp-groups/tree/master/groups/client/views
These are my defined routes:
Router.map(function() {
this.route('layout', {
path: '/',
template: 'getLocation',
layoutTemplate: 'getLocation',
yieldTemplates: {
'tabNav': {to: 'footer'}
}
});
this.route('locationSet', {
path: '/locationSet',
template: 'locationSet',
layoutTemplate: 'locationSet'
});
this.route('groupsList', {
path: '/groupsList',
template: 'groupsList',
layoutTemplate: 'groupsList'
});
});
Why doesn't the pathFor work on mobile? (It at least doesn't work in Xcode iOS simulator or on iPhone Mobile Safari or Chrome).
The push.js component is causing the issue. You can still use Rachet with Iron Router by disabling push.js. Per rachet's documention you can disable push by adding a data-ignore tag to your HTML link.
<!-- Use data-ignore="push" to prevent the push.js interception -->
<a href="http://www.google.com" data-ignore="push">Google<a>
Routing issue:
Removing the ratchet package fixed it for me. Looks like ratchet uses it's own way of linking between templates which is incompatible with iron-router. Removing ratchet removes the UI elements, but the routing works on mobile: http://pp-groups-fixed.meteor.com. You could use a strictly UI library, like bootstrap to make the UI elements, or maybe even just the ratchet's UI components. If you want to fully use ratchet, you will most likely have to forgo IronRouter.
Other things to fix:
Layout Templates
When using meteor and iron-router, a layout template is a template with common elements, with a {{> yield}} placed where you want the regular templates to show up.
You actually only have one real layout template in your code, in groups.html there is a layout template named layout, and it is unused.
In your code, regular templates are being misused as layout templates, because they don't have {{> yield}} in them. Also, the tabNav template is being placed using iron-router, yet you have already included it in each template with {{> tabNav}}.
So, you can simply get rid of the layout template code in your iron router, and your app will still function:
Router.map(function() {
this.route('layout', {
path: '/',
template: 'getLocation',
// layoutTemplate: 'getLocation',
// yieldTemplates: {
// 'tabNav': {to: 'footer'}
// }
});
this.route('locationSet', {
path: '/locationSet',
template: 'locationSet',
// layoutTemplate: 'locationSet'
});
this.route('groupsList', {
path: '/groupsList',
template: 'groupsList',
// layoutTemplate: 'groupsList'
});
});
A better way is to take out all the common code, the header, general structure of the page, tab bar, and put it in a layout template. Add a {{> yield}} where you want the page template to render. Refer to this layout template in your router as layoutTemplate.
Another sidenote, iron-router automatically looks for the template with the same name as the route, if no template is defined. So if you are writing this.route('groupsList', ... you don't need to write template: 'groupsList' as well.
Data
Your past-game.js file should be named get-location.js. Yes, the name itself doesn't matter, but that is getLocation's complimentary code, not postGame's. Same with scheduled-games.js and locationSet. Look at the Template.templateName.helpers to see how the code corresponds.
Of course, ideally this data should be in a collection. For now, instead of creating the data as arrays with var, you could create a seperate file with your data as global variables. Simply define as PastGames = [...], then use the template helpers to return the data you need.

Router only render certain templates

I have a layout with 2 containers 1)productView and 2)calendarView. My Router is configured as follows:
this.route('productDetail', {
layout: 'main',
path: '/products/:_id',
waitnOn: function(){...},
data: function(){...},
yieldTemplates: {
'calendarView: { to: calendar }
}
});
I now want to achieve that whenever the route changes (e.g. from 'products/1' to 'products/2') only the productView is re-rendered and that calendarView does nothing. As for now every time the 'productDetail/:id' route is called the 'calendarView' function 'Template.calendarView.rendered' gets called and re-renders the template. My 'main' template looks like this:
<template name="main">
<div>
{{yield}}
</div>
<div>
{{yield 'calendar'}}
</div>
</template>
Is there a way to tell the router to only render certain templates? Or is there a better solution?
Any help is much appreciated.
Is there a reason why you use the router for the calendar view ?
I think one solution may be not to use the router for the calendar view
<template name="main">
<div>
{{yield}}
</div>
<div>
{{>calendar}}
</div>
</template>

Rerun Helper Functions on Route Change for Active Links

I've been playing around with Meteor + Iron Router for a multi-page app I'm working on and I'm getting stuck on helper functions for named yields. Specifically, I've been trying to get the active class for my navbar tabs to update on each route change.
Below is the relevant code for my project:
router.js
Router.configure({
layoutTemplate: 'mothership',
yieldTemplates: {
'header' : {to: 'header'},
'footer': {to: 'footer'}
},
});
Router.map(function () {
// Home page
this.route('home', {
path: '/',
template: 'home',
});
this.route('about', {
path: '/about',
template: 'about',
});
this.route('emails', {
path: '/emails',
template: 'emails',
});
this.route('people', {
path: '/people',
template: 'people',
});
});
mothership.html
<template name="mothership">
Skip to content
<div id="wrap">
<!-- header -->
<div>{{yield 'header'}}</div>
<div id="content">{{yield}}</div>
</div>
<div id="push"></div>
<div id="footer">
{{yield 'footer'}}
</div>
</template>
header.html
...bootstrap stuff...
<a class="navbar-brand" href="{{pathFor 'home'}}">Mailchacho</a>
<li class="{{activeRoute 'about'}}">About</li>
<li class="{{activeRoute 'emails'}}">Sent Emails</li>
<li class="{{activeRoute 'people'}}">People</li>
...bootstrap stuff...
header.js
Handlebars.registerHelper('activeRoute', function(name) {
var active = location.pathname === Router.path(name);
return (active) ? 'active' : '';
});
// I know I can use Template.headers.helpers... to do this as well, I just found the registerHelper to be cleaner.
When I load a page from scratch, the proper active class is assigned, but when a route is changed on-page, the active class doesn't update. Using breakpoints, I can see the 'activeRoute' function isn't called on a change.
What's interesting is if I add a data dictionary to router.js, it does update. My guess is having the data dictionary indicates that something has changed between routes, forcing a refresh. What I'd like to do is have this refresh occur without needing to pass a data dictionary.
Since Iron Router is still fairly new, I haven't been able to able to find much online. The closest I've found is this issue on github (https://github.com/EventedMind/iron-router/issues/103), but the last comment was never resolved, which seems to be similar to mine.
With the above in mind, is there any way I can signal for a helper function to be rerun on a route change without passing a dummy data dictionary? I was thinking that something like Deps.autorun might be needed, but that doesn't feel right. I'm still pretty new to Meteor & Iron Router so any help here would be appreciated. Thanks!
Yours is a common problem so Mike Fitzgerald has built a package just for this purpose:
https://atmosphere.meteor.com/package/iron-router-active
The given example is like:
<nav>
<ul>
<li class="{{ isActive 'dashboard' }}">...</li>
<li class="{{ isActive 'dashboard|root' }}">...</li>
<li class="{{ isActive 'users' 'on' }}">...</li>
<li class="{{ isActivePath 'products' }}">...</li>
</ul>
</nav>
andd works through handlebars helpers which are called isActive, isActivePath, isNotActive and isNotActivePath.
I use meteor add zimme:active-route now. It works with iron:router, kadira:flow-router and meteorhacks:flow-router.
Just two examples: Output active class:
<li class="{{isActiveRoute 'home'}}">...</li>
Custom class:
<li class="{{isActiveRoute 'home' class='is-selected'}}">...</li>
https://atmospherejs.com/zimme/active-route

Resources