angular 9 candeactivate not working although code seems to be fine and not throwing any error - angular-ui-router

Hello Guys I am new to Angular and learning the things using the following tutorial link. For some reason the canDeactivate route guard seem to not work. Any help would be appreciated as I tried checking many things but none worked. I have the latest angular CLI and there are not errors in my code and for some reason the canDeactivate function is not at all called during the route change.
I am applying the function on the CreateEmployee route so when I fill the form for createEmployee and I try to navigate to different route then it should kick in.
create-employee-component.html: In this, I have few form elements
<form #employeeForm = "ngForm" (ngSubmit)="saveEmployee()" [ngClass]="{'was-validated': employeeForm.submitted}" novalidate>
create-employee-component.ts
import { Component, OnInit, ViewChild } from '#angular/core';
import { NgForm } from '#angular/forms';
import { Department } from '../models/department.model';
import { BsDatepickerConfig } from 'ngx-bootstrap/datepicker';
import { Employee } from '../models/employee.model';
import { EmployeeService } from './employee.service';
import { Router } from '#angular/router';
#Component({
selector: 'app-create-employee',
templateUrl: './create-employee.component.html',
styleUrls: ['./create-employee.component.css']
})
export class CreateEmployeeComponent implements OnInit {
#ViewChild('employeeForm') public createEmployeeForm: NgForm;
datePickerConfig: Partial<BsDatepickerConfig>;
previewPhoto = false;
create-employee-can-deactivate-guard.service.ts
import { Injectable } from '#angular/core';
import { CanDeactivate } from '#angular/router';
import { CreateEmployeeComponent } from './create-employee.component';
#Injectable()
export class CreateEmployeeCanDeactivateGuardService implements CanDeactivate<CreateEmployeeComponent>{
canDeactivate(component: CreateEmployeeComponent): boolean{
alert("HJEJJEJEJ");
if(component.createEmployeeForm.dirty)
{
return confirm('Are you sure you want to discard your changes?');
}
return true;
}
}
app-routing.module.ts
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { ListEmployeesComponent } from './employees/list-employees.component';
import { CreateEmployeeComponent } from './employees/create-employee.component';
import { CreateEmployeeCanDeactivateGuardService } from './employees/create-employee-can-deactivate-guard.service';
const routes: Routes = [
{path: 'list', component: ListEmployeesComponent},
{
path:'create',
component: CreateEmployeeComponent,
canDeactivate: [CreateEmployeeCanDeactivateGuardService]
},
{path: '', redirectTo:'/list', pathMatch:'full'}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers: [CreateEmployeeCanDeactivateGuardService]
})
export class AppRoutingModule { }
I feel everything is correct as per some of the other answers I found on the StackOverflow. Please let me know what am I doing wrong here. I have also posted my code here.

I finally found out what was the issue. After some time spending on the research I found out that in the navbar previously I was using the href element with the anchor tag hence the canDeactivate guard was not kicking in. Now I changed it to [routerLink]="['/list']" and the canDeactivate started working correctly.
Posting the answer as it may be useful for someone who is looking for solution:
Previous Navbar with the anchor tag and href element:
<a class="nav-link" href="list">List <span class="sr-only">(current)</span></a>
Changed Navbar with the anchor tag and routerLink which is working fine with the canDeactivate:
<a class="nav-link" [routerLink]="['/list']">List <span class="sr-only">(current)</span></a>
If in case you are looking for the whole code please check the question where I have mentioned all the code chunks related to canDeactivate guard.

Related

Navigating from single page view to side-menu layout in ionic 4

I'd like to build an app using Ionic 4 that simplistically works the following way:
On-boarding page is shown with some images/text
After user clicked 'start' button, some flag onboarded=true is written into local-storage
User redirected to the main app view which is a ion-split-pane containing side-menu layout
Next time when user launches the app, I check if he/she already viewed on-boarding screen (by checking presence of onboarded var in storage) and if it is - I immediately redirect the user to the main app having side-menu layout as I mentioned, omitting on-boarding screen.
I started the project using ionic cli, based on side-menu template and to fulfill logic described above, I modified it the following way:
app.component.ts
import { Component } from '#angular/core';
import { Platform } from '#ionic/angular';
import { SplashScreen } from '#ionic-native/splash-screen/ngx';
import { StatusBar } from '#ionic-native/status-bar/ngx';
import { Storage } from '#ionic/storage';
import { Router } from '#angular/router';
#Component({
selector: 'app-root',
template: '<router-outlet></router-outlet>',
})
export class AppComponent {
constructor(
private platform: Platform,
private splashScreen: SplashScreen,
private statusBar: StatusBar,
private storage: Storage,
private router: Router
) {
this.initializeApp();
}
async initializeApp() {
await this.platform.ready();
this.statusBar.styleDefault();
this.splashScreen.hide();
const onboarded = await this.storage.get('onboarded');
if (onboarded) {
this.router.navigate(['main-app']);
} else {
this.router.navigate(['onboarding']);
}
}
}
onboarding.page.html
<ion-header>
<ion-toolbar>
<ion-title>onboarding</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
Welcome aboard!
<ion-button (click)="start()">Start app!</ion-button>
</ion-content>
onboarding.page.ts
import { Component } from '#angular/core';
import { Storage } from '#ionic/storage';
import { Router } from '#angular/router';
#Component({
selector: 'app-onboarding',
templateUrl: './onboarding.page.html',
styleUrls: ['./onboarding.page.scss'],
})
export class OnboardingPage {
constructor(
private storage: Storage,
private router: Router
) { }
start() {
this.storage.set('onboarded', true);
this.router.navigate(['main-app']);
}
}
main-app.page.html
<ion-app>
<ion-split-pane>
<ion-menu>
<ion-header>
<ion-toolbar>
<ion-title>Menu</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-menu-toggle auto-hide="false" *ngFor="let p of appPages">
<ion-item [routerDirection]="'root'" [routerLink]="[p.url]">
<ion-icon slot="start" [name]="p.icon"></ion-icon>
<ion-label>
{{p.title}}
</ion-label>
</ion-item>
</ion-menu-toggle>
</ion-list>
</ion-content>
</ion-menu>
<ion-router-outlet main></ion-router-outlet>
</ion-split-pane>
</ion-app>
main-app.page.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-main-app',
templateUrl: './main-app.page.html',
styleUrls: ['./main-app.page.scss'],
})
export class MainAppPage {
public appPages = [
{
title: 'Home',
url: '/home',
icon: 'home'
},
{
title: 'List',
url: '/list',
icon: 'list'
}
];
constructor() { }
}
List and Home pages are just empty pages and their content is not important for this example
Problem is that main-app containing side menu and content is not working properly. I know that there is a problem in <ion-router-outlet main></ion-router-outlet>'s usage but couldn't find the right solution.
Does anyone have idea how to make it work?
Thanks!
Andrew.
try adding contentId="content" to your <ion-menu>
ie: <ion-menu contentId="content">
and also add id="content" to your <ion-router-outlet>
ie: <ion-router-outlet id="content" main>
i seem to remember something about the router-outlet not working without that.

ngModel not working in Nativescript

I have been developing a simple application on Nativescript playground recently, where i found that ngModel was not working on input type TextField. I have made sure that i import NativescriptFormModule in the module of the app. but still it is not working.
You the extended syntax instead of ngModel (text and textChange) as the TextFuield is a complex layout with multiple bindable properties (hint, text, returnType, etc.) - example Playground here and documentation article here
HTML
<TextField [hint]="hint" [text]="name" (textChange)="onTextChange($event)"></TextField>
TypeScript
export class SignupComponent implements OnInit {
name: string;
hint: string = "enter name";
tf: TextField;
onTextChange(args: EventData) {
console.log("onTextChange");
this.tf = <TextField>args.object;
console.log("tf.text: ", this.tf.text);
}
}
I had the same issue with the NativeScript Playground tutorial today. Could'nt have my [(ngModel)]="user.email" (or password) to work. I fixed it by importing the NativeScriptFormsModule and adding it to #NgModule imports in the app.module.ts
import { NativeScriptFormsModule } from "nativescript-angular/forms";
then
#NgModule({
imports: [NativeScriptFormsModule]
})
app.module.ts file:
import { NgModule } from "#angular/core";
import { NativeScriptModule } from "nativescript-angular/nativescript.module";
import { NativeScriptUIChartModule } from "nativescript-ui-chart/angular";
import { NativeScriptHttpClientModule } from "nativescript-angular/http-client";
import { NativeScriptFormsModule } from "nativescript-angular/forms";
import { AppComponent } from "./app.component";
#NgModule({
imports: [
NativeScriptModule,
NativeScriptUIChartModule,
NativeScriptHttpClientModule,
NativeScriptFormsModule
],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule { }

.NET Core + Angular-CLI App - can't pass data from api to components [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I'm building an ASP .NET Core App with Angular-CLI using this tutorial.
App works great, I got pre-build event to ng build before starting the app in VS, it works OK. I also can successfully get to my API over localhost:port/api/[controller] (in this case it would be Contacts as it's supposed to be a contact book).
Now, I'm trying to have Angular get the data directly from API. For this reason, I created a IContact interface, a ContactService and I pass it onto ContactComponent which is supposed to display it.
I might be making a silly mistake here (my skills are very basic), but for some reason I don't even see the object .json coming in through the network logs when I run the app (before trying to pass it to the view I'm trying to ensure I'm getting the data from the API first).
I might be doing something wrong (so I encourage you to reply even if you think you might be saying silly-obvious stuff) but here's my questions:
Should my code below work (not attaching imports though, I think I got all of them but check me ;-))? I'm not talking super-efficient or stuff, just basic to get the job done.
What is the best way to see if service to getAPI is working? Would that be network logging in your browser if you just import the service into the component and try to call the get method? Or is there another way?
Is my logic and approach towards the general architecture of the app OK or am I getting something wrong? :-)
contact.service.ts
const API_URL = environment.apiUrl;
#Injectable()
export class ContactService {
constructor(private http: Http) { }
public getContacts(): Observable<IContact[]> {
return this.http.get(API_URL)
.map((response: Response) => <IContact[]>response.json())
.catch(this.handleError);
}
private handleError(error: Response) {
console.error(error);
return Observable.throw(error.json().error || 'Server error')
}
}
icontact.ts
export interface IContact {
id: number;
firstName: string,
lastName: string,
address: string,
telephone: string
}
contact.component.ts
#Component({
selector: 'app-contact',
templateUrl: './contact.component.html',
styleUrls: ['./contact.component.css'],
providers: [ContactService]
})
export class ContactComponent implements OnInit {
private _contactService: ContactService;
private contactlist: IContact[];
constructor() {
}
public ngOnInit() {
this._contactService.getContacts()
.subscribe((contacts) => { this.contactlist = contacts });
}
}
Any other code requirements or anything - let me know. All feedback is appreciated. Thanks!
I have slightly changed your code.
File icontact.ts. I have made IContact a simple type. You can make it an interface back to support your object shape.
export type IContact = string;
File contact.service.ts. We call the /api/values API that is created by default in a new ASP.NET Core Web API project if you follow the tutorial you mentioned.
import { Injectable } from "#angular/core";
import { Http } from "#angular/http";
import { Observable } from "rxjs/Observable";
import 'rxjs/add/operator/map'
import { IContact } from "./icontact";
const API_URL = '/api/values'; // environment.apiUrl;
#Injectable()
export class ContactService {
constructor(private http: Http) { }
public getContacts(): Observable<IContact[]> {
return this.http.get(API_URL)
.map(response => <IContact[]>response.json());
}
}
File contact.component.ts. The template simply displays the list.
import { Component, OnInit } from "#angular/core";
import { ContactService } from "./contact.service";
import { IContact } from "./icontact";
#Component({
selector: 'app-contact',
template: '<div *ngFor="let contact of contactList">{{contact}}</div>',
providers: [ContactService]
})
export class ContactComponent implements OnInit {
private contactList: IContact[];
constructor(private contactService: ContactService) { }
public ngOnInit() {
this.contactService.getContacts().subscribe(
(contacts) => { this.contactList = contacts; },
(error) => { console.log(error); }
);
}
}
File app.component.html. Display your component on the application's page.
<app-contact>Wait...</app-contact>
File app.module.ts. Import HttpModule. That fixes one of the error messages on your screenshot.
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { HttpModule } from '#angular/http';
import { AppComponent } from './app.component';
import { ContactComponent } from "./contact.component";
#NgModule({
declarations: [AppComponent, ContactComponent],
imports: [BrowserModule, HttpModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
In the Configure method in Startup.cs, make sure you put the app.Use(...) call before app.UseStaticFiles().
Since you serve your Angular app from wwwroot by a ASP.NET Core middleware, the host is the same for the Angular app and the Web API, so you don't need to configure CORS for that setup.
By the way, you may be interested in taking a look at the Angular CLI Templates on Visual Studio Marketplace. (Disclaimer: I'm the author.) The Item Template supports your project setup out-of-the-box.

Mdl-Select won't expand contract

so I'm stumped! I decided to try the Mdl-Select but the select stays permanently expanded and refuses to contract. My first thought was that something was installed incorrectly specifically the popover which I assume manages that part.
here is what I am doing.
app.module.ts
import { MdlModule } from '#angular-mdl/core'
import { MdlPopoverModule } from '#angular-mdl/popover';
import { MdlSelectModule } from '#angular-mdl/select';
systemjs.config.ts
'#angular-mdl/core': 'npm:#angular-mdl/core/bundle/core.js',
'#angular-mdl/popover': 'npm:#angular-mdl/popover/index.umd.js',
'#angular-mdl/select': 'npm:#angular-mdl/select/index.umd.js'
app.module.ts (which is consumed by main.ts)
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import { MdlModule } from '#angular-mdl/core'
import { MdlPopoverModule } from '#angular-mdl/popover';
import { MdlSelectModule } from '#angular-mdl/select';
#NgModule({
imports: [
BrowserModule,
FormsModule,
ReactiveFormsModule,
HttpModule,
routing,
MdlModule,
MdlPopoverModule,
MdlSelectModule
]
app.component.html
<div>
<mdl-select [(ngModel)]="user.SelectedFeature" placeholder="Feature">
<mdl-option [value]><em>-- Select a Feature --</em></mdl-option>
<mdl-option *ngFor="let feature of features" [value]="feature">{{feature}}</mdl-option>
</mdl-select>
</div>
features is a string[]
I am pretty much stumped here on what else to do... obviously I have other modules imported but nothing that isn't generic.
no errors are thrown.
The issue was I missed adding the extra imports in my style sheet
issue resolved by adding the following to style.css
#import '~#angular-mdl/popover/popover';
#import '~#angular-mdl/select/select';

angular2 and nativescript does not work - assertion failed: 15G1004 14A345: libxpc.dylib

I am been having a terrible time getting nativescript to work with angular 2 with the official release. I am zero issues with good ole faction web angular but native script is a different story. NativeScript why will my hello world work? I see nothing.
Thanks
Home:
import {Component} from "#angular/core";
import { Router } from "#angular/router";
#Component({
selector: "home",
template: "hello",
})
export class HomeComponent {
constructor(private router: Router){
}
}
app
import {Component} from "#angular/core";
#Component({
selector: "main",
template: "<page-router-outlet></page-router-outlet>",
})
export class AppComponent {
}
Routes:
import { HomeComponent } from "./pages/home/home.component";
export const routes = [
{ path: "", component: HomeComponent }
];
export const navigatableComponents = [
HomeComponent
];
Modules:
import { NgModule } from "#angular/core";
import { NativeScriptFormsModule } from "nativescript-angular/forms";
import { NativeScriptHttpModule } from "nativescript-angular/http";
import { NativeScriptModule } from "nativescript-angular/platform";
import { NativeScriptRouterModule } from "nativescript-angular/router";
import { AppComponent } from "./app.component";
import { HomeComponent } from "./pages/home/home.component";
import { routes, navigatableComponents } from "./app.routes";
#NgModule({
imports: [
NativeScriptModule,
NativeScriptFormsModule,
NativeScriptHttpModule,
NativeScriptRouterModule,
NativeScriptRouterModule.forRoot(routes)
],
declarations: [AppComponent,
...navigatableComponents ],
bootstrap: [AppComponent]
})
export class AppModule {}
Executing before-prepare hook from /Users/Documents/frontend/bootops-mobile-v1/hooks/before-prepare/nativescript-dev-typescript.js
Project successfully prepared (ios)
Transferring project files...
Successfully transferred all files.
Applying changes...
Sep 17 23:05:18 Davids-iMac com.apple.CoreSimulator.SimDevice.D28D29C3-07B6-4B60-B4B0-711475C505DE.launchd_sim[19187] (UIKitApplication:org.nativescript.bootopsmobilev1[0x7351][20309]): Service exited due to Terminated: 15
Successfully synced application org.nativescript.bootopsmobilev1 on device D28D29C3-07B6-4B60-B4B0-711475C505DE.
Sep 17 23:05:19 Davids-iMac bootopsmobilev1[20349]: objc[20349]: Class PLBuildVersion is implemented in both /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/PrivateFrameworks/AssetsLibraryServices.framework/AssetsLibraryServices (0x124c81910) and /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/PrivateFrameworks/PhotoLibraryServices.framework/PhotoLibraryServices (0x124aab210). One of the two will be used. Which one is undefined.
Sep 17 23:05:19 Davids-iMac bootopsmobilev1[20349]: assertion failed: 15G1004 14A345: libxpc.dylib + 62597 [37A9DF49-35C1-3D93-B854-B35CACF0100F]: 0x7d
Sep 17 23:05:19 Davids-iMac bootopsmobilev1[20349]: CONSOLE LOG file:///app/tns_modules/#angular/core/bundles/core.umd.js:210:20: Angular 2 is running in the development mode. Call enableProdMode() to enable the production mode.
Everything in your code snippets looks good except the part with the HomeComponent template, that Component declaration should look like this:
#Component({
selector: "home",
template: `<Label text="hello"></Label>`,
})
export class HomeComponent {
constructor(private router: Router){
}
}
The reason behind this is the fact that in NativeScript there is not webview it is all native components. So in order for your "hello" text to be rendered you need to use the NativeScript Label. This is all true for NativeScript + Angular 2. Your code snippets work correctly on Angular 2 web because the web browser knows how to render that text, on the other hand NativeScripts Angular 2 renderer does not know how to render a text directly because on iOS and Android there is not such rendering and you need to tell it how via the Label tag.
In the index.html, you have to put <selector-for-the-page></selector-for-the-page> You might have different selector.

Resources