I'm using the Aurelia skeleton for my project. Everything seemed so intuitive, however I'm stuck with a problem which I suspect is fairly easy (if you know how).
The problem is that the app.html / app.js is initially showing a nav bar and loading some default styles.
Now I need a login page, which does not load anything but its own styles, no navbar no nothing - just its own login form.
So I tried something like this:
app.js
<template>
<div if.bind="auth.isNotAuthenticated()">
<require from="components/login/index" ></require>
<login router.bind="router"></login>
</div>
<div if.bind="auth.isAuthenticated()">
<require from="nav-bar.html" ></require>
<require from="../styles/styles.css"></require>
<div class="container" id="banner">
<div class="row">
<img src="images/logo.png" />
</div>
</div>
<nav-bar router.bind="router"></nav-bar>
<div class="page-host">
<router-view></router-view>
</div>
</div>
</template>
Obviously that doesn't work (unless you refresh the page/f5), since the app.js / app.html is the root route which is always present and never changes. But I hope the logic within the markup helps illustrate what I'm looking to solve?
I guess my if only I knew how to reload the parent route (app.js) when I navigate from the login route, on login success, to another route. And again when I logout, the parent route (app.js) should be refreshed as well once again. Then all my problems would be solved.
What am I missing here? :-)
I think aurelia's setRoot(module) function will help with this.
Here's the standard main.js file that "bootstraps" the aurelia app:
main.js
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging();
aurelia.start()
.then(a => a.setRoot()); // this is the equivalent of setRoot('app')
}
When setRoot is called with no arguments Aurelia looks for an app.js + app.html viewmodel and view.
We can adjust the logic to check whether the user is logged in and if not, show the login screen:
main.js
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging();
aurelia.start()
.then(a => {
if (userIsLoggedIn()) {
a.setRoot('app');
} else {
a.setRoot('login');
}
});
}
Then in your login view model you can call setRoot('app') after the user has successfully logged in:
login.js
import {Aurelia, inject} from 'aurelia-framework';
import {AuthService} from './my-auth-service';
#inject(Aurelia, AuthService)
export class Login {
userName = '';
password = '';
constructor(aurelia, authService) {
this.aurelia = aurelia;
this.authService = authService;
}
submit() {
// attempt to login and if successful, launch the app view model.
this.authService.login(userName, password)
.then(() => this.aurelia.setRoot('app'));
}
}
Note: if your app includes a "logout" feature that will send the user back to the login screen (eg setRoot('login')), be sure to reset the router and update the url accordingly. This will prevent issues when the user signs back in. More details in here and here.
For a working example of setRoot you can check also
https://foursails.github.io/sentry
https://github.com/foursails/sentry
Related
I'm storing nav items in my Vuex store and iterating over them for conditional output, in the form of a Vue/Bulma component, as follows:
<b-navbar-item
v-for='(obj, token) in $store.state.nav'
v-if='privatePage'
class=nav-link
tag=NuxtLink
:to=token
:key=token
>
{{obj.text}}
</b-navbar-item>
As shown, it should be output only if the component's privatePage data item resolves to true, which it does:
export default {
data: ctx => ({
privatePage: ctx.$store.state.privateRoutes.includes(ctx.$route.name)
})
}
The problem I have is when I run the dev server (with ssr: false) the component doesn't show up initially when I navigate to the page via a NuxtLink tag. If I navigate to the page manually, or refresh it, the component shows.
I've seen this before in Nuxt and am not sure what causes it. Does anyone know?
recommendation :
use mapState and other vuex mapping helper to have more readable code :).
dont use v-for and v-if at the same element
use "nuxt-link" for your tag
use / for to (if your addresses dont have trailing slash)
<template v-if='privatePage'>
<b-navbar-item
v-for='(obj, token) in nav'
class=nav-link
tag="nuxt-link"
:to="token" Or "`/${token}`"
:key="token"
>
{{obj.text}}
</b-navbar-item>
</template>
and in your script :
<script>
import {mapState} from 'vuex'
export default{
data(){
return {
privatePage: false
}
},
computed:{
...mapState(['privateRoutes','nav'])
},
mounted(){
// it's better to use name as a query or params to the $route
this.privatePage = this.privateRoutes.includes(this.$route.name)
}
}
</script>
and finally if it couldn't have help you , I suggest to inspect your page via dev tools and see what is the rendered component in html. it should be an <a> tag with href property. In addition, I think you can add the link address (that work with refresh and not by nuxt link) to your question, because maybe the created href is not true in navbar-item.
NOTE: token is index of nav array . so your url with be for example yourSite.com/1.so it's what you want?
This question has been answered here: https://stackoverflow.com/a/72500720/12747502
In addition, the solution to my problem was a commented part of my HTML that was outside the wrapper div.
Example:
<template>
<!-- <div>THIS CREATES THE PROBLEM</div> -->
<div id='wrapper'> main content here </div>
</template>
Correct way:
<template>
<div id='wrapper'>
<!-- <div>THIS CREATES THE PROBLEM</div> -->
main content here
</div>
</template>
After trying for hours I gotta ask here, how can I have a simple Auth based Navigation inside my App?
I have a firebase auth user inside my vuex set by a auth listener, all good so far.
Now I simply wanna show the <Main /> screen when the user is signed in, otherwise I wanna show the <sign-in /> screen.
I tried a lot of solutions with v-if, a navigation on creation of the components etc. but did not find any example on how to accomplish this.
How I imagine it how it should work: App.vue
<template>
<Page>
<Frame v-if="user" id="main">
<main />
</Frame>
<Frame v-else>
<sign-in-page />
</Frame>
</Page>
</template>
There is multiple way of approaching this. My prefer way is to create a custom router that uses the $navigateTo or modal navigation. You should see your app as multiple distinct page. Your router then can be something like
let routes = {
navigate(instance, routeName, options) {
return instance.$navigateTo(routeName, options);
}
}
export default routes
then in your components
<script>
import routes from '~/router'; // Path to your router
router.navigate(this, LoginView, {}) // use in methods
</script>
With this basic idea it is possible to add pre and post navigation rules and functions. You can also centralize all the routes and register them. Here it is an import but it can also be a plugins for vue.
You should have 1 login page, 1 loading page, 1 main page, ...
When the app start it always go to the loading where you decide to reroute depending on firebase auth state.
Sorry to revive a question. Im using Angular 7 and Im trying to use Router Link.
This is my app-routing-module
const routes: Routes = [
{ path: 'locations' , component : LocationManagerComponent },
{ path: 'locations/create' , component : CreateEditLocationComponent },
{ path: 'locations/create/:id', component : CreateEditLocationComponent },
{ path: '404' , component : PageNotFoundComponent},
{ path: '**' , redirectTo: '/404'}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
This is router link:
<a [routerLink] = "['/locations']" routerLinkActive="active"> test link </a>
When I click on link, nothing happens. The URL on browser changed but component is not loaded.
If I press F5, component is loaded and from that point on, routers link works.
I've tryed a lot of stackoverflow solution like writing link in any sort of variant like
<a routerLink="/locations" ...
<a [routerLink]= ['/locations'] ...
<a [routerLink]= "['/locations']" ...
With or without LinkAttive attribute. Putting
<base href="/">
in index.html etc....
Following this topic: TOPIC I've tried to include Router in my Layout component:
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
#Component({
selector: 'app-layout',
templateUrl: './layout.component.html',
styleUrls: ['./layout.component.css']
})
export class LayoutComponent implements OnInit {
constructor(
private route : ActivatedRoute
) { }
[...]
but nothing changes.
The strange part is that after an F5, all routes works, even route to component not yet loaded.
In this topic TOPIC 2 the user resolved removing css class. I've tried to put my link in a completely cleaned component HTML and it not working (but still works after a refresh).
<p>
dashboard works!
<a routerLink = '/locations' routerLinkActive="active"> test link </a>
</p>
UPDATE: This is layout.component where route tag is.
I can't figure out how to have a Sidenav without having route-outlet inside it.
<mat-sidenav-container fullscreen>
<mat-sidenav #sidenav mode="over">
<div class="profile_container">
<span> User Name </span>
</div>
<mat-nav-list>
<mat-list-item><a [routerLink]="['/locations']" routerLinkActive="active"> Locations
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content>
<app-header (toggleSidenav)="sidenav.toggle()"></app-header>
<div style="padding: 20px 10px 10px 20px;">
<router-outlet></router-outlet>
</div>
</mat-sidenav-content>
</mat-sidenav-container>
Note: this answer is based on the previous version of your question, before you added the code of layout.component.html. So, instead of layout component, I am using the simplified dashboard component.
The below is working for me in Angular 8.1.
app.component.html
<app-dashboard></app-dashboard>
means that the DashboardComponent is contained within (is the child of) the AppComponent.
No change to the default app.component.ts
dashboard.component.html
<p>
dashboard works!
<a routerLink = '/locations' routerLinkActive="active">
Locations test link </a>
</p>
<p><a routerLink = '/locations/create' routerLinkActive="active">
Locations/create </a></p>
<p><a routerLink = '/locations/create/:id' routerLinkActive="active">
Locations/create/:id </a></p>
<p>router-outlet is below:</p>
<router-outlet></router-outlet>
All the links are working with click and with manually entering the url (eg: http://localhost:4200/locations/create/:id) in the browser and with reload (F5).
New Components
Generated using the ng generate component command:
Dashboard
LocationManager
CreateEditLocation
PageNotFound
app-routing-module.ts
The same as your file, but also added import statements for the newly generated components.
I figured what cause the problem but I can't unserstand why and I was not able to reproduce in StackBlitz.
This was my app.component.html, the root of all app:
<main>
<!-- Showing All Site Pages -->
<span *ngIf='isLogged()'>
<app-layout style="height:100%"></app-layout>
</span>
<!-- Showing Login Page -->
<div *ngIf='!isLogged()'>
<router-outlet></router-outlet>
</div>
</main>
The App-Layout code is above.
THIS NOT WORKS!
I changed it with a simply:
<main>
<app-layout style="height:100%"></app-layout>
</main>
As you see from my question, Layout has its own router-outlet.
I think the problem is the two router-outlet tag. Maybe Angular is not able to understand thats they are mutually exclusive. Maybe when I was clicking on menu, for some reason, Angular was updating the "first" router-outlet encountered and only after a refresh (F5), when the isLogged was already triggered and the app-layout was loaded directly, Angular knows which router-outlet to use.
In the new Way all pages, even Login, has to be child of AppLayout so every Layout component that's exists only if logged, has to be manually hide with *ngIf='!isLogged()'
A little price to pay to have routes works.
I have a Angular 5 project where users navigate to routes like this when clicking on an item.
my_domain/{account_id}/item/{item_id}/open/
my_domain/{account_id}/item/{item_id}/closed/
my_domain/{account_id}/item/{item_id}/snoozed/
Users should be able to change the account_id or item_id. So I want to be able to reload the page only changing the parametes (the ids). How can I achieve this ?
Angular 8
in .component.ts
You can use current url and put param to navigate array
this.router.navigate([this.router.url, 'open']);
or in .component.html
You can use dot as part of url "./youparam" - in this case "./" == current url
< a [routerLink]="['./open']">open</a>
Simple:
import { Router } from '#angular/router';
...
export class YourComponent{
constructor(public router: Router) {
}
...
myRouteMethod(accountId, itemId, endpointUrl){
// Use String literals
this.router.navigate([`/${accountId}/item/${itemId}/${endpointUrl}/`]);
}
}
On your HTML you can have:
<div class="wrap">
<div (click)="myRouteMethod(accountIdSomehow, itemIdSomeHow, 'open')">
open </div>
<div (click)="myRouteMethod(accountIdSomehow, itemIdSomeHow, 'close')"> close </div>
<div (click)="myRouteMethod(accountIdSomehow, itemIdSomeHow, 'other')"> other </div>
</div>
It would be useful to know how you're getting accountId and itemId.
Reusable functions will help out, always think of parameters instead of repetition, the trick is adjusting your templates to work dynamically with your component.
I have a page that has pictures (index.js) and when you click a picture, a detail page with bigger version of the picture and its content (pic.js) opens. When I was using hard-coded data, I created a service and put the data in it. By this way, the model hook wasn't skipped when I click a picture. I did it because my links are dynamic {{#link-to}} helper and I saw that model hook gets skipped when you have it. But now, I need to use JSON api to get the data from an URL, when I do it in the index.js there is no problem with displaying it but when I try to open any link in new tab or paste a link in URL bar, model hook doesn't work in pic.js.
//routes/index.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return Ember.$.getJSON('My jsonApi Url');
}
});
I read that I need to use ember-data in order to fix it. I created a model "news-list" and put attributes in it. Also I created an adapter and take the code which I call API from index.js and put there.
//adapters/application.js
import JSONAPIAdapter from 'ember-data/adapters/json-api';
import Ember from 'ember';
export default JSONAPIAdapter.extend({
model(params){
return Ember.$.getJSON('My jsonApi Url',params.NewsUrl);
}
});
//templates/index.hbs
{{image-list model=model.Data currentPos=currentPos }}
{{outlet}}
//templates/components/image-list.hbs
{{#each model as |pic|}}
<div>{{#link-to "pic" pic}}
<p class="info">{{pic.Title}}</p><br/>
<img src={{pic.Image}} width="300">
{{/link-to}}</div> {{/each}}
{{yield}}
//routes/pic.js
import Ember from 'ember';
export default Ember.Route.extend({
activate: function() {
this._super(...arguments);
window.scrollTo(0,0);
},
model() {
//return this.store.findAll('news-list');
}
});
//templates/pic.hbs
<p class= "back">{{#link-to 'index'}}Home Page{{/link-to}}</p>
<p class="detail"><img src="{{model.Image}}" width="600" ></p>
<p class="content"><br/><br/>{{model.Content}}</p><br/><br/>
<p class= "back">{{#link-to 'index'}}Home Page{{/link-to}}</p>
{{outlet}}
I tried to use return this.store.findAll('news-list'); in the pic.js but then all I see was a blank page when I click a picture.
I guess there is something I'm missing. I can't use ember-data properly. How can I fix it?