So before this config use to work with react-router. But now I got a error message saying that; But I saw you need to use , but how to render the routes config insideReactDom.render( < div >
<Router history = {hashHistory}routes = {routes} > < /Router> </div>,
document.getElementById('react-app'));
?
routes.map is not a function
Can someone help me please.
const routes = {
component: Base,
childRoutes: [{
path: '/home',
getComponent: (location, callback) => {
if (Auth.isUserAuthenticated()) {
callback(null, Home);
} else {
callback(null, HomePage);
}
}
},
{
path: '/login',
component: LoginPage
},
]
}
Related
I am developing Ionic4 App. So first I have Welcome page, that has one button "Log in" that when clicked navigates to Log In Page (using this.navCtrl.navigateRoot('/login')). When the user logs in, the Dashboard shows up using NavController in the login.ts
login() {
this.loginService.login().subscribe(user => {
this.navCtrl.navigateRoot('/dashboard');
});
}
On dashboard I register the back button in the constructor:
this.platform.backButton.subscribe( () => {
if(this.router.url=='/dashboard')
this.presentBackButtonAlert();
}
);
When the button is clicked an alert shows up to confirm if the user wants to exit the app:
presentBackButtonAlert() {
this.alertCtrl.presentAlert({
header: 'Exit?',
buttons: [
{
text: 'No',
role: 'cancel',
handler: () => {
}
}, {
text: 'Yes',
handler: () => {
console.log('Confirm Okay');
navigator['app'].exitApp();
}
}
]
});
}
The problem is when I tap the hardware back button, the alert shows up but the Dashboard Page navigates back to Welcome Page.
I expect that when I am logged in (on Dashboard Page), and tap the back button, only the alert to appear without navigating back.
Here's my AuthGuard:
import { Injectable } from '#angular/core';
import {AuthService} from '../authentication/auth.service';
import {CanActivate} from '#angular/router';
#Injectable({
providedIn: 'root'
})
export class AuthGuardService implements CanActivate{
constructor(private authService: AuthService) { }
canActivate(): boolean {
return this.authService.isAuthenticated();
}
}
And this is how I use it on the router module:
{path: 'login', loadChildren: './pages/Auth/login/login.module#LoginPageModule', pathMatch: 'full'},
{path: 'dashboard', loadChildren: './pages/company/dashboard/dashboard.module#DashboardPageModule', canActivate: [AuthGuardService]},
My authState is in the AuthService and it looks like this:
public authState = new BehaviorSubject(false);
public isAuthenticated() {
return this.authState.value;
}
The first is that simply returning the Boolean value isn't enough in a Guard. You should also tell to which page you want to go to if the condition held by the Guard isn't met
so you canActivate Method
import { Injectable } from '#angular/core';
import {AuthService} from '../authentication/auth.service';
import {CanActivate} from '#angular/router';
import { Router, ActivatedRouteSnapshot } from '#angular/router';
#Injectable({
providedIn: 'root'
})
export class AuthGuardService implements CanActivate{
constructor(private router: Router, private authService: AuthService) { }
canActivate(): boolean {
if(this.authService.isAuthenticated()){
this.router.navigate(['/whateverpage'])
return false;
}
return true;
}
}
You should also add the gaurd to the page you are about to go to. Since you want to prevent access to the login page after a user has logged in the guard should be added to the loginpage
{path: 'login', loadChildren: './pages/Auth/login/login.module#LoginPageModule' canActivate: [AuthGuardService]},
{path: 'dashboard', loadChildren: './pages/company/dashboard/dashboard.module#DashboardPageModule'},
For Ionic4, I tried this method and it is working fine for me.
Back button after login page means the user wants to exit the application or navigate to other paths.
When the view is entering I am subscribing it variable and when it leaves then unsubscribe. So that the exiting app on the back button will execute for that particular page only(e.g. dashboard page).
constructor(private platform: Platform){}
backButtonSubscription;
ionViewWillEnter() {
this.backButtonSubscription = this.platform.backButton.subscribe(async () => {
navigator['app'].exitApp();
// edit your code here
// either you exit or navigate to the desired path
});
}
}
ionViewDidLeave() {
this.backButtonSubscription.unsubscribe();
}
thanks for the question and answers, they guided me in the right way because I was facing a similar challenge, in my case, I fixed this doing something as follows on the canActivate() method on my Guard class, then I added this canActivate only to the auth page config on the app-routing.module.ts, (the rest of the pages use the canLoad instead):
canLoad(
route: Route,
segments: UrlSegment[]): Observable<boolean> | Promise<boolean> | boolean {
return this.authService.UserIsAuthenticated.pipe(
take(1),
switchMap(isAuthenticated => {
if (!isAuthenticated) {
return this.authService.autoLogin();
} else {
return of(isAuthenticated);
}
}),
tap(isAuthenticated => {
if (!isAuthenticated) {
this.router.navigateByUrl('/auth');
}
}));
}
canActivate(
route: import("#angular/router").ActivatedRouteSnapshot,
state: import("#angular/router").RouterStateSnapshot): boolean | import("#angular/router").UrlTree | Observable<boolean |
import("#angular/router").UrlTree> | Promise<boolean | import("#angular/router").UrlTree> {
return this.authService.UserIsAuthenticated.pipe(
take(1),
switchMap(isAuthenticated => {
if (!isAuthenticated) {
return this.authService.autoLogin();
} else {
return of(isAuthenticated);
}
}),
map(isAuthenticated => {
if (!isAuthenticated) {
return true;
} else {
this.router.navigateByUrl('/dashboard');
return false;
}
}));
}
So on the routing config, I only used the canActivate for the auth page...
const routes: Routes = [
{
path: '', redirectTo: 'auth', pathMatch: 'full',
canActivate: [AuthGuard]
},
{
path: 'auth',
loadChildren: () => import('./auth/auth.module').then(m => m.AuthPageModule),
canActivate: [AuthGuard]
},
{
path: 'products',
children: [
{
path: '',
loadChildren: './products/products.module#ProductsPageModule',
canLoad: [AuthGuard]
},
{
path: 'new',
loadChildren: './products/new-product/new-product.module#NewProductPageModule',
canLoad: [AuthGuard]
},
.....
I'm trying to include a local JSON file from the static directory called blogs.json which has a load of blogs inside it.
I'm currently loading the blogs via Vue Axios which is a module I'm including in Nuxt JS.
Currently, the blogs are being loaded from the json file perfectly fine, however there is a noticeable few ms delay before the blogs are loaded, I'm trying to figure out a better approach to load the json file and populate the blogs array listed inside data()
This is my current code:
<script>
import PageBanner from '~/components/PageBanner';
export default {
head: {
title: 'Site Title: Blog',
meta: [
{ hid: 'description', name: 'description', content: 'Site description' }
]
},
components: {
PageBanner
},
data () {
return {
blogs: [],
isLoading: true
}
},
created () {
this.axios.get("/articles/blogs.json").then((response) => {
this.blogs = response.data
this.isLoading = false
})
}
}
</script>
This works just fine, but how could I modify this to load the json more quickly?
Just import it, do this and it should work God willing:
<template>
<div>
<!-- There should be no delay -->
{{blogs}}
</div>
<template>
<script>
import PageBanner from '~/components/PageBanner';
import blogsFromJson from '~/articles/blogs.json'; // Or wherever it is found
export default {
head: {
title: 'Site Title: Blog',
meta: [
{ hid: 'description', name: 'description', content: 'Site description' }
]
},
components: {
PageBanner
},
data () {
return {
blogs: blogsFromJson, // Just set it here
isLoading: true
}
},
/* No need for this anymore
created () {
this.axios.get("/articles/blogs.json").then((response) => {
this.blogs = response.data
this.isLoading = false
})
}
*/
}
</script>
I have this URL that is feched succefully by Axios
const URL_INTERIORES = 'http://localhost:3001/interiores';
I installed the react-image-lightbox from npm and it gave to me default images configured in array.
const images = [
'//placekitten.com/1500/500',
'//placekitten.com/4000/3000',
'//placekitten.com/800/1200',
'//placekitten.com/1500/1500',
];
I would like to change the default array to get the images from the db.json file to come into images's lightbox. How can I solve it?
Here is the rest of the code, with the 'react-image-lightbox' configuration:
class Interiores extends Component {
constructor(props) {
super(props)
this.state = {
interiores: [],
photoIndex: 0,
isOpen: false
}
}
componentDidMount() {
axios.get(URL_INTERIORES)
.then(res => {
this.setState({ interiores: res.data })
})
}
render() {
const { photoIndex, isOpen } = this.state;
return (
<div>
<button type="button" onClick={() => this.setState({ isOpen: true })}>
Open Lightbox
</button>
{isOpen && (
<Lightbox
mainSrc={images[photoIndex]}
nextSrc={images[(photoIndex + 1) % images.length]}
prevSrc={images[(photoIndex + images.length - 1) % images.length]}
onCloseRequest={() => this.setState({ isOpen: false })}
onMovePrevRequest={() =>
this.setState({
photoIndex: (photoIndex + images.length - 1) % images.length,
})
}
onMoveNextRequest={() =>
this.setState({
photoIndex: (photoIndex + 1) % images.length,
})
}
/>
)}
</div>
)
}
}
export default Interiores;
And here is my db.json file.
"interiores": [
{
"text": "introduction text here",
"images": [
"int_01_thumb.jpg", "int_02_thumb.jpg", "int_03_thumb.jpg",
"int_04_thumb.jpg", "int_05_thumb.jpg", "int_06_thumb.jpg",
"int_07_thumb.jpg", "int_08_thumb.jpg", "int_09_thumb.jpg"
]
}
],
I've never worked with such library, so I might be missing something, but would an alternative like this work?
render() {
const { interiores, photoIndex, isOpen } = this.state; // Added 'interiores'
// Link to static root and make a relative path for each iamge
const staticRoot = '//localhost:3001/interiores/'
const images = interiores[0].images.map(i => staticRoot + i)
// Rest of your code
}
Once you have the file names, just link them to your static files/images path and map over the image array.
Have the following code:
export default new Router({
routes: [
{
path: '/noticia/:id',
name: 'Noticia',
component: Noticia,
props: true
}
]
})
export default {
name: 'Noticia',
data () {
return {}
},
props: ['id'],
computed: {
noticia () {
return this.$store.getters.noticia
}
},
mounted: function () {
this.$nextTick(function () {
console.log(id)
// Code that will run only after the
// entire view has been rendered
})
}
}
<div>{{id}}</div>
The problem is {{id}} is showed by html div, but it isn't passed to 'mounted', so, i cannot run my 'console.log(id)' (as it will run a code to bring data and put it into the computed).
I have other code running with the same data, running wheel, cannot understand the error
mounted() {
console.log( this.id )
}
To get it working on mounted, just did:
this.id instead of just id
I caught this error when testing a component with templateUrl. I don't know how to fix it.
After reading a post I added TestBed.resetTestEnvironment() and TestBed.initTestEnvironment() in the test file, but it doesn't solve the problem.
It seems like something is missing in the config file and the html file cannot be loaded.
Here are the karma logs:
Here is my karma.config.js file:
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', 'karma-typescript'],
files: [
// Zone:
'./node_modules/zone.js/dist/zone.js', // 'Uncaught ReferenceError: Zone is not defined'
'./node_modules/zone.js/dist/proxy.js', // 'TypeError: Cannot read property 'assertPresent' of undefined'
'./node_modules/zone.js/dist/sync-test.js',
'./node_modules/zone.js/dist/async-test.js', // 'TypeError: Cannot read property 'assertPresent' of undefined'
'./node_modules/zone.js/dist/fake-async-test.js',
'./node_modules/zone.js/dist/jasmine-patch.js', // 'TypeError: Cannot read property 'assertPresent' of undefined'
// Angular:
'./node_modules/angular/angular.js',
'./node_modules/#uirouter/angularjs/release/angular-ui-router.js',
'./node_modules/angular-mocks/angular-mocks.js',
// ANY OTHER FILES TO LOAD FOR YOUR TESTS
// App:
'./assets/app/app.component.ts',
'./assets/app/app.component.html',
'./assets/app/app.component.spec.ts',
],
exclude: [
'./assets/app/main.aot.ts'
],
preprocessors: {
"**/*.ts": "karma-typescript"
},
reporters: ['spec', 'karma-typescript'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
concurrency: Infinity,
mime: {
'text/x-typescript': ['ts', 'tsx']
}})}
In the following you can find the app.component.ts file:
import { Component } from '#angular/core';
import { Constants } from './utils/constants';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styles:[
`
#status {
background:#f8f9fa;
bottom:0;
left:0;
margin:0;
padding:0;
position:fixed;
width:100%;
}
#status div {
margin:0;
padding:5px;
text-align:center;
color:#c8c9ca;
}
`
]})
export class AppComponent {
appTitle = "App Title";
demoMode=(Constants.DEMO_MODE)?"Demo":"DefaultMode";
}
Finally, I attach the app.component.spec.ts file:
import { AppComponent } from "./app.component";
import { ComponentFixture, TestBed } from "#angular/core/testing";
import { BrowserDynamicTestingModule,
platformBrowserDynamicTesting } from '#angular/platform-browser-dynamic/testing';
import { Constants } from "./utils/constants";
describe('AppComponent', () => {
let component: AppComponent;
let fixture: ComponentFixture<AppComponent>;
let h1: HTMLElement;
beforeEach(() => {
TestBed.resetTestEnvironment();
TestBed.initTestEnvironment(BrowserDynamicTestingModule,
platformBrowserDynamicTesting());
TestBed.configureTestingModule({
declarations: [AppComponent],
})
.compileComponents().then(()=>{
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
h1 = fixture.nativeElement.querySelector('h1');
}).catch((error)=>{
console.log(error);
});
});
it('should display original title', () => {
expect(h1.textContent).toContain("App Title");
});
it('status bar text should correspond to the working mode', () =>{
let text=(Constants.DEMO_MODE)?"Demo":"DefaultMode";
expect(document.getElementById("status-text").textContent).toEqual(text);
});
});
Thanks in advance!
You should be taking the nativeElement of the the text box as below,
h1 = fixture.nativeElement.querySelector('h1').nativeElement;
Also, please query the fixture inside the it statement and add the fixture.detectChanges(); inside the it to trigger the change detection