Best Practise in Angular2 for displaying buttons based on a users security permissions - security

I’m currently developing my first Angular 2 site. The site contains a Bootstrap NavBar on the left hand side so the user can navigate to different pages on the site. Each user can see different navigation buttons in the NavBar depending on their security permissions.
At the moment I’ve just mocked it to act as if the buttons have been retrieved from the DB into an array. Then each button is displayed in the NavBar by looping round the array. Please see the code below.
Questions
It works, but is this the best way to do it?
Is there a better best practice way to retrieve settings from a DB based on a users security permissions and display those settings?
And is there anything else I need to consider to make it more
security?
navbar.component.heml
<div class="nav-side-menu">
<div class="brand">NavBar</div>
<div class="menu-list">
<ul id="menu-content" class="menu-content collapse out">
<li *ngFor="let appButton of appButtons">
<a href="#">
<i class="{{appButton.buttonClass}}"></i> {{appButton.displayName}}
</a>
</li>
</ul>
</div>
</div>
navbar.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.css']
})
export class NavbarComponent implements OnInit {
constructor() { }
ngOnInit() {
}
// Hard coded in at the moment, but these settings
// will be retrieved from the DB
appButtons = [
{ displayName: "Dashboard", buttonClass: "fa fa-dashboard fa-lg" },
{ displayName: "Gift", buttonClass: "fa fa-gift fa-lg" },
{ displayName: "Globe", buttonClass: "fa fa-globe fa-lg" },
{ displayName: "Search", buttonClass: "fa fa-search fa-lg" }
];
}

Rather than bringing buttons from the database. Categorized your user according to the roles. Add these roles in your DB and show the buttons according to the roles.
You can add other types of security/access level with the help of role.
Which will be much better than fetching the buttons from the database.

Related

Angular RouterLinks broken URL navigation unaffected

My angular router is failing to load the latest component after a router change is made within the application. If I call the URL manually it will load the correct content however any use of routerLink or a call to router.navigate has no affect on the router-outlet content.
I have tried binding to router events and recalling the getContent function when there is a change and this fixes the issue when calling programmatically.
The project is pretty bare but the router:
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { ContentpaneComponent } from './contentpane/contentpane.component';
const routes: Routes = [
{ path: '', component: ContentpaneComponent },
{ path: 'post/:app', component: ContentpaneComponent }
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
And the container in app.component.html
<div class="above">
<app-sidebar></app-sidebar>
<div class="contentpane">
<router-outlet></router-outlet>
</div>
</div>
<app-terminal></app-terminal>
If there is any other code segments that would benefit please request in comments.
Here is the code containing the routerLink directives:
<div class="main">
<div class="logo">
O
</div>
<ul class="navbar">
<li *ngFor="let nav of navs">
<a routerLink="/{{nav.href}}" class="navitem">{{nav.title}}</a>
</li>
</ul>
</div>
EDIT: I am also getting a websocket error in the console:
WebSocket connection to 'ws://localhost:4200/sockjs-node/748/g0a4bxsw/websocket' failed: WebSocket is closed before the connection is established.
Not sure if it is a related issue.
So I managed to find the solution. The problem arose because routerLink does not force the reloading of a component when the URL changes, so ngOnInit does not get called. The fix I implemented was to subscribe my content update function to the route.params and queryParams events:
this.route.queryParams.subscribe(queryParams => {this.getContent()});
this.route.params.subscribe(queryParams => {this.getContent()});
This causes these functions to be called whenever the route updates.

Angularjs Material md-datepicker within Formly template will not open calendar pane

I'm trying to create a Formly template using md-datepicker. Unfortunately, when I click on the md-datepicker control within my form the calendar panel does not open.
controller code:
{
className: 'col-xs-6',
key: 'dateCreated',
type: 'materialdatepicker',
templateOptions: {
label: 'Created'
},
expressionProperties: {
'templateOptions.disabled': function () {
return !vm.options.editMode;
},
'templateOptions.required': function () {
return vm.options.editMode;
}
}
}
template:
<script type="text/ng-template" id="materialdatepicker.html">
<div layout="column">
<div flex="100">
<p class="input-group" style="display: block; margin: 0px;">
<md-datepicker id="{{::id}}" name="{{::id}}" ng-model="model[options.key]"></md-datepicker>
</p>
<div class="formlyMessages" ng-messages="fc.$error" ng-if="fc.$touched">
<div class="formlyMessage" ng-message="{{::name}}" ng-repeat="(name, message) in ::options.validation.messages">
{{message(fc.$viewValue, fc.$modelValue, this)}}
</div>
</div>
</div>
</div>
</script>
formly config:
formlyConfigProvider.setType({
name: 'materialdatepicker',
templateUrl: 'materialdatepicker.html',
wrapper: ['bootstrapLabel', 'bootstrapHasError'],
defaultOptions: {
ngModelAttrs: ngModelAttrs
},
controller: ['$scope', function ($scope) {
$scope.materialdatepicker = {};
}]
});
I can't seem to figure out how to get the calendar panel to open. I'm not getting any errors in the console and the control does get populated with my initial value.
Any ideas?
What I forgot to mention in my original post was that this form is contained within a modal window ($uibModal). As such, the calendar pane was popping up behind my modal window.
The solution found here worked for me: Angular Material DatePicker Calendar Shows Behind Angular Modal
You need to tell your calendar pane to open with a high z-index so it renders above the modal. Place this style sheet code into your modal html:
<style>
.md-datepicker-calendar-pane {
z-index: 1200;
}
</style>

Can I Create Nested Angular Component HTML Selectors?

Updated: Per Thierry Templier's response:
Below is essentially what I want to do, but unfortunately the inner components aren't rendering. Is there a way to nest components via their HTML selectors like so?
<custom-menu-bar-component (onCustomEvent)="handleEvent($event)">
<custom-button-component></custom-button-component>
<custom-dropdown-component></custom-dropdown-component>
</custom-menu-bar-component>
In my chrome debugger, I see only the outer component being rendered:
<custom-menu-bar-component>
<div class="row">
** Nothing here, where my two inner components should be :(
</div>
</custom-menu-bar-component>
And my components look like this:
CustomMenuBarComponent.ts:
import {Component} from 'angular2/core'
import {CustomButtonComponent} from './CustomButtonComponent'
import {CustomDropdownComponent} from './CustomDropdownComponent'
#Component({
selector: 'custom-menu-bar-component',
directives: [CustomButtonComponent, CustomDropdownComponent],
template: `
<div class="row"></div>
`
})
export class CustomMenuBarComponent {
}
CustomButtonComponent.ts:
import {Component, EventEmitter} from 'angular2/core'
import {CustomEvent} from './CustomEvent'
#Component({
selector: 'custom-button-component',
outputs: ['onCustomEvent'],
template: `
<button type="button" class="btn btn-light-gray" (click)="onItemClick()">
<i class="glyphicon icon-recent_activity dark-green"></i>Button</button>
`
})
export class CustomButtonComponent {
onCustomEvent: EventEmitter<CustomEvent> = new EventEmitter();
onItemClick(): void {
this.onCustomEvent.emit(new CustomEvent("Button Component Clicked"));
}
}
CustomDropdownComponent is nearly identical to the CustomButtonComponent, but with different text. I'm just trying to get this very simple example working before I start making these components more useful and reusable.
Is this kind of approach possible? I'm trying to make it easy for others to take these components and create more of my custom menu bars with ease and simplicity.
Not sure what your question is about but
<custom-menu-bar-component (onCustomEvent)="handleEvent($event)">
<custom-button-component></custom-button-component>
<custom-dropdown-component></custom-dropdown-component>
</custom-menu-bar-component>
requires <ng-content></ng-content> in the template of CustomMenuBarComponent
A bit of documentation can be found in https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#aftercontent I had expected a bit more this was all I found.
http://blog.thoughtram.io/angular/2015/06/29/shadow-dom-strategies-in-angular2.html might contain some helpful information as well.
Update
Moving (onCustomEvent)="handleEvent($event)" to the <custom-button-component></custom-button-component> element should do what you want. Events from EventEmitter don't bubble.
In fact you have the error because you don't instantiate your EventEmitter in the CustomButtonComponent component:
#Component({
(...)
})
export class CustomButtonComponent {
onCustomEvent: EventEmitter<CustomEvent> = new EventEmitter(); // <-----
(...)
}
Otherwise your code seems correct.
Update
You need to use ng-content to include your sub components into the CustomMenuBarComponent one.
#Component({
selector: 'custom-menu-bar-component',
directives: [CustomButtonComponent, CustomDropdownComponent],
template: `
<div class="row">
<ng-content></ng-content>
</div>
`
})
export class CustomMenuBarComponent {
}

When should we use Vue.js's component

When I study the feature of Vue.js's component system. I feel confused when and where should we use this? In Vue.js's doc they said
Vue.js allows you to treat extended Vue subclasses as reusable
components that are conceptually similar to Web Components, without
requiring any polyfills.
But based on their example it doesn't clear to me how does it help to reuse. I even think it complex the logic flow.
For example, you use "alerts" a lot in your app. If you have experienced bootstrap, the alert would be like:
<div class="alert alert-danger">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<strong>Title!</strong> Alert body ...
</div>
Instead of writing it over and over again, you can actually make it into a component in Vue:
Vue.component('alert', {
props: ['type','bold','msg'],
data : function() { return { isShown: true }; },
methods : {
closeAlert : function() {
this.isShown = false;
}
}
});
And the HTML template (just to make it clear, I separate this from the Vue Comp above):
<div class="alert alert-{{ type }}" v-show="isShown">
<button type="button" class="close" v-on="click: closeAlert()">×</button>
<strong>{{ bold }}</strong> {{ msg }}
</div>
Then you can just call it like this:
<alert type="success|danger|warning|success" bold="Oops!" msg="This is the message"></alert>
Note that this is just a 4-lines of template code, imagine when your app uses lot of "widgets" with 100++ lines of code
Hope this answers..

Ionic nav-back-button and menu button showing together

I try to make the menu button not to show when the back button is showing. is there a way to let Ionic take care of that? or it's up to me?
for example if i use ui-sref to go from app.users to app.users.add or app.users.details i expect the menu button to be hidden and the back button to show, but they're both showing when i go to nested views. example:
<button class="button button-positive" ui-sref="app.users.details({id:user.id})"> User details </button>
app.js
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app', {
url: '/app',
abstract: true,
templateUrl: 'templates/menu.html'
//controller: 'AppCtrl'
})
.state('app.users', {
url: '/users',
views: {
'menuContent#app' : {
controller: 'UsersCtrl',
templateUrl: 'templates/users.html'
}
}
})
.state('app.users.add', {
url: '/addUsers',
views: {
'menuContent#app' : {
controller: 'AddUserCtrl',
templateUrl: 'templates/add_user.html'
}
}
})
.state('app.users.details', {
url: '/userDetails/:id',
views: {
'menuContent#app' : {
controller: 'UserDetailsCtrl',
templateUrl: 'templates/details_user.html'
}
}
})
}
menu.html
<ion-side-menus>
<ion-pane ion-side-menu-content>
<ion-nav-bar class="bar-stable">
<ion-nav-back-button class="button-clear">
<i class="icon ion-ios7-arrow-forward"></i> back
</ion-nav-back-button>
</ion-nav-bar>
<ion-nav-view name="menuContent" animation="slide-right-left"></ion-nav-view>
</ion-pane>
<ion-side-menu side="right">
<header class="bar bar-header bar-stable">
<h1 class="title">Title</h1>
</header>
<ion-content class="has-header">
<ion-list>
<ion-item nav-clear menu-close ui-sref="app.users">
Users
</ion-item>
<ion-item nav-clear menu-close ui-sref="app.users.add">
New user
</ion-item>
</ion-list>
</ion-content>
</ion-side-menu>
</ion-side-menus>
My views structure is as such:
<ion-view title="Title">
<ion-nav-buttons side="right">
<button menu-toggle="right"class="button button-icon icon ion-navicon"></button>
</ion-nav-buttons>
<ion-content class="has-header">
...
View Content
...
</ion-content>
</ion-view>
This is done by ionic by default now in beta 14. You can also toggle this by this attribute.
<ion-side-menus enable-menu-with-back-views="false">
Relative Codepen
Sidemenu Starter Project
Sidemenu Docs
Is also possible to override that from a child page just adding the ion-side-menus directive inside the child template:
<ion-side-menus enable-menu-with-back-views="true"></ion-side-menus>
<ion-view view-title="My Child page">
<ion-content>
<h1>HEY</h1>
</ion-content>
</ion-view>
This will add the complete navigation bar (ion-nav-bar) inside your child page that was added into menu.html template (according with the example above)
Place the navbar with menu button on the html page on which you need menu button and place navbar with back button on the page where you need back button.
Like this I need Menu On home page so place your navbar on homepage with menu button
<ion-view title="home">
<ion-nav-bar class="bar-stable main-header-nav home-page">
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" menu- toggle="left"></button>
</ion-nav-buttons>
</ion-nav-bar>
<ion-content></ion-content>
</ion-view>
And I need back button on Inbox page so use navbar with backbutton on inbox page
<ion-view title="">
<ion-nav-bar class="bar-stable main-header-nav home-page">
<ion-nav-back-button class="button-clear go-back">
</ion-nav-back-button>
</ion-nav-bar>
<ion-content></ion-content>
</ion-view>
I solved in 2019 adding the property "menuToggle" to the Button. That tells Ionic/Angular that the function of that button is to be the "Hamburguer Button" so, Angular understand and hide it when the "Back Arrow Button" is shown.
<button menuToggle ion-button icon-only (click)="btnHamburger()">
<ion-icon name="menu"></ion-icon>
</button>

Resources