Durandal.js: change navigation options per area - asp.net-mvc-4

I want to have different navigation per "area" of my durandal application. I've achieved this with ASP.NET MVC when using Areas by defining a Nav section in the layout page and having nested layout pages which implement the nav for each area. The view structure in durandal is as follows:
http://i1346.photobucket.com/albums/p697/user2269352/viewstructure_zps5e21e724.gif
I'm using the ASP.NET MVC4 durandal template and I am guessing that I might need to change the following segement from shell.html
<ul class="nav" data-bind="foreach: router.visibleRoutes">
<li data-bind="css: { active: isActive }">
<a data-bind="attr: { href: hash }, html: name"></a>
</li>
</ul>
I suppose that ideally I'd like to have separate html pages that could be loaded into this section depending on which area / page I am viewing.

You can accomplish this by adding a settings object to your route info, and specifying the area name there. With that in place, create a computed observable against the router's visibleRoutes collection that selects only the routes for the current area.
Not sure what your route configuration looks like, but an example of adding settings would be something like this:
var routes = [
{ url: 'one/page1', moduleId: 'viewmodels/one/page1', name: 'Page 1', visible: true, settings: {area: 'one'} },
{ url: 'two/page1', moduleId: 'viewmodels/two/page1', name: 'Page 1', visible: true, settings: {area: 'two'} }
];
router.map(routes);
In your view model where you are controlling the navigation html:
//filter the visible routes for the current area
viewModel.areaRoutes = ko.computed(function () {
var area = this.area;
return ko.utils.arrayFilter(router.visibleRoutes(), function (route) {
return route.settings.area === area;
});
}, viewModel);

Joseph Gabriel based solution works for me and playing with router.activeItem.settings.areSameItem I can set every route's group anywhere at my layout.
router.activeItem.settings.areSameItem = function (currentItem, newItem, activationData) {
return currentItem == newItem; //replace this with your own code
};

Related

Laravel Inertia Ziggy Link Routing Issue

I'm currently having a problem in my routing. Here's the scenario:
Inertia is working fine when there's no id query.
But after navigating to edit and I want to click any of the navigation links like clicking the Dashboard link, it throws a 404 code saying the page does not exist. Simply because instead of removing the /category/{id}, it adds dashboard at the end instead of removing the query.
Is there a way to fix this by not violating the inertia routing?
Here's the code:
Authenticated Layout
const navigation = [
{ name: 'Dashboard', href: 'dashboard', current: false },
{ name: 'Category', href: 'category', current: false },
]
<nav class="hidden lg:flex lg:space-x-8 lg:py-2" aria-label="Global">
<Link v-for="item in navigation" :key="item.name"
:href="item.href" :class="[item.current ? 'bg-gray-100
text-gray-900' : 'text-gray-900 hover:bg-gray-50
hover:text-gray-900', 'rounded-md py-2 px-3 inline-flex
items-center text-sm font-medium']" :aria-
current="item.current ? 'page' : undefined">{{ item.name
}}</Link>
</nav>
Have you tried using Ziggy? Ziggy Github repo
Then the routes you create using Laravel are made available using the same route function in Vue. It works well with inertia when creating laravel apps.
<Link
:href="route('frontend.categories.show', [categories, post.slug])"
>
If you check your console in the page inspector it should show you the ziggy routes pulled from the "web.php" in json format.
It automatically makes the standard Controller class functions available for your routes in javascript (so index, create, edit, show, destroy functions).
I would be happy to share my code I created from a tutorial utilising breeze like you are.
Solved it. Just needed to add "/" to the href navigation object.
const navigation = [
{ name: 'Dashboard', href: '/dashboard', current: false },
{ name: 'Category', href: '/category', current: false },
]

Sending variables from one view to another view and url in vue

What I want is when I click an image, it redirects to a URL (another view) which is made by the something/image_name. I am making the project in vue.js. I am thinking of doing this by using props (passing all the variables needed from the first view to the next view). But the data is not displayed in the 2nd view. Also, I want to know how can I make the URL like something/image_name. I am using it by hardcoding the URL.
routes.js
const router = new Router({
mode: "foo",
base: "foobar",
routes: [
{
path: "/event/:title",
name: "event",
component: event,
props: true,
}
where title is the variable (event.title to be more precise) I want to pass from other view. I also want to get title in URL also.
view 1
<template>
<div src="image_location" :to="/event/{{event.title}}"></div>
</template>
<script>
export default {
name: "event",
components: {
Event
},
data: function initData() {
return {
event: {},
};
},
};
</script>
view 2 (Event.vue) (its URL should be foobar/event/{{ title }})
props: {
event: Object,
}
I tried router-link also but lack of its documentation restricts me from using it efficiently.
You can add an #click event to the image and programmatically move the user to the new URL when they click on the image. Something like:
<imge src="..." #click="$router.push(`/event/${event.title}`)" />

Where should route meta data be loaded in a Vue app?

I'm in the process of setting up a VueJs SPA. I'm using vue-router and I'm trying to find the best solution to the following problem. I have a series of routes. Each of which needs to call an API to get the meta data for the given ID.
/industry/:id/overview
/industry/:id/top-stories
/industry/:id/top-tweets
/brand/:id/overview
/brand/:id/top-stories
/brand/:id/top-tweets
I've been looking at using created or beforeRouteEnter/beforeRouteUpdate and I'm a bit lost. Ideally, I would only fetch new data when a new /industry/:id is reached, not when navigating between pages within the same ID. Also, I'd like to avoid having to define the fetch to grab data in every page component. Also don't want to over complicate this, so my question is, Is there a standard method for tackling this issue?
Clarification:
When I say meta here, I mean data returned from an API about the given industry or brand which I pull using the ID in the route. The api call includes the name of the industry/brand which I want to have on page as soon as the page is presented to the user.
I have something similar. I tackle this using the following approach:
I use the same component for all /industry/:id Vue likes to reuse components wherever it can so if two routes (for example /industry/:id/overview and /industry/:id/top-stories) are using the same component it will stay the same.
What does change, however, is the route meta. So if you add a page key to the meta object in the route objects, and probably add a computed property called page that return this.$route.meta.page, you can use v-if attributes to conditionally render any component. So you might have something like <div v-if="page === 'overview'"></div><div v-else-if="page==='top-stories'"></div>
What this allows you to do is fetch all the data from the API during created or mounted lifecycle and store it as the state. Since the route change doesn't reload the component the state stays the same.
Here is a code example
// router.js
const Project = () =>
import(/* webpackChunkName: "projects" */ "./views/projects/_id");
export default new Router({
mode: "history",
routes: [
{
path: "/projects/:project_id/views",
name: "ViewProject",
component: Project,
meta: {
page: "views",
}
},
{
path: "/projects/:project_id/export",
name: "ExportProject",
component: Project,
meta: {
page: "exports"
}
},
{
path: "/projects/:project_id/recommendations",
name: "ProjectRecommendations",
component: Project,
meta: {
page: "recommendations"
}
},
]
});
And here is the template
<template>
<div v-if="project">
<h1>{{ project.name }}</h1>
<router-link :to="/project/someid/views">Views</router-link>
<router-link :to="/project/someid/exports">Exports</router-link>
<router-link :to="/project/someid/recommendations">Recommendations</router-link>
<ul v-if="page==='views">
<li v-for="(view, i) in project.views" :key="i">{{ views }}</div>
</ul>
<ul v-else-if="page==='exports">
<li v-for="(export, i) in project.exports" :key="i">{{ export }}</div>
</ul>
<ul v-else-if="page==='recommendations">
<li v-for="(recommendation, i) in project.recommendations" :key="i">{{ recommendation }}</div>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
project: null
}
},
computed: {
page() {
return this.$route.meta.page;
}
},
mounted() {
this.getProject()
},
methods: {
getProject() {
axios
.get(`/projects/someid`)
.then(res => this.project = res.data)
}
}
}
</script>

Experiencing navbar flicker with Vue.js

I'm experiencing a flicker in my navbar before a function is evaluated to either true or false.
The function that needs to evaluate is the following:
export default {
methods: {
isAuthenticated () {
return this.$store.state.user.authenticated
}
},
data: () => {
return {
unauthenticated: [
{
title: 'Link1',
url: '/link1'
},
{
title: 'Link2',
url: '/link2'
},
{
title: 'Link3',
url: '/link3'
}
],
authenticated: [
{
title: 'otherLink1',
url: '/otherlink1'
},
{
title: 'otherLink2',
url: '/otherlink2'
},
{
title: 'otherLink3',
url: '/otherlink3'
}
]
}
}
}
And the navbar has the following:
<template v-if="isAuthenticated()">
<b-nav is-nav-bar>
<b-nav-item v-for="nav in authenticated" :key="nav.title" :href="nav.url">{{nav.title}}</b-nav-item>
</b-nav>
</template>
<template v-else>
<b-nav is-nav-bar>
<b-nav-item v-for="nav in unauthenticated" :key="nav.title" :href="nav.url">{{nav.title}}</b-nav-item>
</b-nav>
</template>
However, when I click through the navigation, the unauthenticated links appear for a second and then the authenticated links appear as if the isAuthenticated() function hasn't evaluated yet. What can I do to remove this flicker?
My store file (user.js) file looks like this:
export const state = () => ({
headers: {},
profile: {}
})
export const mutations = {
updateHeaders (state, headers) {
state.headers.access_token = headers['access-token']
state.headers.token_type = headers['token-type']
state.headers.client = headers['client']
state.headers.expiry = headers['expiry']
state.headers.uid = headers['uid']
if (state.headers.expiry == null) {
state.authenticated = false
} else {
let timeToExpiry = new Date(state.headers.expiry * 1000)
let now = new Date()
state.authenticated = now < timeToExpiry
}
},
signout (state) {
state.headers = {}
state.profile = {}
}
}
The login/logout methods occur via API calls to a Rails app. The Devise gem handles the rest.
Thanks in advance!
EDIT:
I am using Nuxt.js for the layouts/pages/components so I believe that links submit with a this.$router.push(url) under the hood.
The b-nav tags are coming from Bootstrap Vue
When using bootstrap-vue there are two ways to add links to the navbar. One is to bind to :href attribute, which creates a regular html anchor. The other is to use :to attribute, which creates a link that interacts with vue-router.
<b-navbar-nav v-if="isAuthenticated()">
<b-nav-item v-for="nav in authenticated" :key="nav.title" :to="nav.url">{{nav.title}}</b-nav-item>
</b-navbar-nav>
<b-navbar-nav v-if="!isAuthenticated()">
<b-nav-item v-for="nav in unauthenticated" :key="nav.title" :to="nav.url">{{nav.title}}</b-nav-item>
</b-navbar-nav>
No reason to use <template> tags here to encapsulate the . Also note that 'is-nav-bar' is deprecated. See here where they note the deprecation.
What code executes when you click one of the links is not stated, I assume it's something like this.$router.push(url). If this is the case, you've probably have included your navbar in the <router-view>, so when you switch current route, components inside <router-view> rerender, so the navbar flashes. Move them out of the <router-view> should fix this.
edit: so the OP is not using vue-router yet, in this case, either manually change the root component's data to make parts other than the navs change, or add vue-router and use this.$router.push() to navigate so parts outside <router-view> won't change or flash.
Anyway, we need the vue component to stay to let vue to rerender only part of the view, while simply navigating by <a> or something will destruct everything and reconstruct them again, hence the flashing.

What's the diff between router isActive and router.isNavigating in Durandal

http://durandaljs.com/documentation/Using-The-Router.html says
Note that each navigable route in the model has an isActive flag which
will be true when the associated route is active. The final thing to
notice is that we have bound a simple spinner animation to the
router.isNavigating.
First, could someone please help me to clarify what's the diff between isActive and isNavigating?
Second, isActive refers to the code below
<ul class="nav" data-bind="foreach: router.navigationModel">
<li data-bind="css: { active: isActive }">
<a data-bind="attr: { href: hash }, html: title"></a>
</li>
</ul>
I searched the official code hosted here but I failed to locate where active class is defined. Thanks for your help!
router.isNavigating is true if the router is currently transitioning from one route to another.
isActive will be true for a route if that route is currently begin displayed.
Say you have two routes in your navigation model:
var routes = [
{ route: '', moduleId: 'home', title: 'Home' },
{ route: 'subpage', moduleId: 'subpage', title: 'Sub Page' }
]
You start off viewing the home route, so home has isActive true.
router.isNavigating is false as you're not transitioning.
Then you click a link to take you to subpage
home isActive is now false, subpage isActive is true and isNavigating becomes true while durandal loads the view and viewmodel and performs the transition.
Once it's finished loading isNavigating becomes false. Home is not active, subpage still is.
You can't find the .active class because it's not in the durandal css. It's in bootstrap.css