rendering different templates in angular2 - node.js

I have just started using angular2 and I am facing some situation to organize my logic. I am actually refactoring backend theme that consists of the two main components i.e login view that is supposed to appear on page load and main dashboard that will appear on successful login. I have no problem with the main dashboard template because i have refactored all the code its working fine. However the main problem is with the login module because dashboard consist of the sidebar, header and maincontent area. My real problem is that how do I exclude sidebar, header on loading the login page which will going to be the startpoint of my app. To be more precise, can i use the layout for the login module which is independent to the dashboard module? Here is my current code for the dashboard.
I would really appreciate if anyone could help me structure this app in proper way.
P.S I am using node as a backend
index.html
<html>
<head>
<title>Angular 2 QuickStart</title>
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<!-- 2. Configure SystemJS -->
<base href="/">
<script src="systemjs.config.js"></script>
<script>
System.import('app/main').catch(function(err){ console.error(err); });
</script>
</head>
<!-- 3. Display the application -->
<body>
<parent>
Loading...
</parent>
<!-- START SCRIPTS -->
<!-- START PLUGINS -->
</body>
</html>
app.component.ts
import { Component, NgZone } from '#angular/core';
import { HeaderComponent } from './header/header.component';
import { SidebarComponent } from './sidebar/sidebar.component';
import { AuthComponent } from './auth/auth.component';
import { ListComponent } from './blogs/list.component';
import {RouteConfig, ROUTER_DIRECTIVES, Router, AuxRoute} from '#angular/router-deprecated';
#Component({
selector: 'parent',
templateUrl:'app/main.html',
directives: [ROUTER_DIRECTIVES, HeaderComponent, SidebarComponent, AuthComponent]
})
#RouteConfig([
{ path: '/list', name: 'BlogList', component: ListComponent}
])
export class AppComponent {
constructor(private _router: Router , private _zone:NgZone){}
}
main.ts
import { bootstrap } from '#angular/platform-browser-dynamic';
import { AppComponent } from './app.component';
import {ROUTER_PROVIDERS} from '#angular/router-deprecated';
bootstrap(AppComponent,[ROUTER_PROVIDERS]);
import {NgZone, enableProdMode} from '#angular/core'
main.html
<div class="page-container">
<div class="page-sidebar">
<mysidebar>
</mysidebar>
</div>
<div class="page-content">
<myheader>
</myheader>
<!-- PAGE CONTENT WRAPPER -->
<div class="page-content-wrap">
<router-outlet></router-outlet>
</div>
</div>
</div>

You can accomplish this by using a Service
export class AppComponentService {
public showSidebar : boolean = true;
public showHeader : boolean = true;
}
If you add this service during bootstrap:
bootstrap(AppComponent,[ROUTER_PROVIDERS, AppComponentService ]);
You can inject this into your AppComponent:
export class AppComponent {
constructor(
private _router: Router,
private _zone:NgZone,
private _appService: AppComponentService
){}
}
And change your template to:
<div class="page-container">
<div class="page-sidebar">
<mysidebar *ngIf="_appService.showSidebar">
</mysidebar>
</div>
<div class="page-content">
<myheader *ngIf="_appService.showHeader">
</myheader>
<!-- PAGE CONTENT WRAPPER -->
<div class="page-content-wrap">
<router-outlet></router-outlet>
</div>
</div>
</div>
Within your LoginComponent you can inject the same singleton service, and play with it on routerOnActivate and routerOnDeactivate:
export class LoginComponent {
constructor(private _appService: AppComponentService){}
routerOnActivate() : void {
this._appService.showSidebar = false;
this._appService.showHeader = false;
}
routerOnDeactivate() : void {
this._appService.showSidebar = true;
this._appService.showHeader = true;
}
}

Related

Laravel jetstream inertia persistent layout

In a fresh laravel installation i'm trying to make layout persistent following the inertia doc https://inertiajs.com/pages
app.js
require('./bootstrap');
// Import modules...
import { createApp, h } from 'vue';
import { App as InertiaApp, plugin as InertiaPlugin } from '#inertiajs/inertia-vue3';
import { InertiaProgress } from '#inertiajs/progress';
import AppLayout from '#/Layouts/AppLayout';
const el = document.getElementById('app');
createApp({
render: () =>
h(InertiaApp, {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: name => import(`./Pages/${name}`)
.then(({ default: page }) => {
if (page.layout === undefined) {
page.layout = AppLayout
}
return page
}),
}),
})
.mixin({ methods: { route } })
.use(InertiaPlugin)
.mount(el);
InertiaProgress.init({ color: '#4B5563' });
Dashboard.vue (here i replace the default app-layout wrapper by div)
<template>
<div>
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
Dashboard
</h2>
</template>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
<welcome />
</div>
</div>
</div>
</div>
</template>
<script>
import Welcome from '#/Jetstream/Welcome'
export default {
components: {
Welcome,
},
}
</script>
While compiling i get this error :
Error: Codegen node is missing for element/if/for node. Apply
appropriate transforms first.
I can't figure out what that means. Is there a reason why the default laravel app with jetstream and inertia doesn't use persistent layout?
If the header slot is in AppLayout aka the persistent one, you cannot use this way (because there is no slot yet? I don't know but I know persistent layouts do mount after child components, this can be the culprit). As a solution, create another layout, ie PageLayout, with named slots and use that layout to build your dashboard and other pages:
AppLayout.vue
<template>
<div>Persistent stuff...</div>
<slot />
<div>Persistent stuff...</div>
</template>
PageLayout.vue
<template>
<slot name="header" />
<slot name="content" />
<div>PageLayout stuff...</div>
</template>
Dashboard.vue
<template>
<PageLayout>
<template #header>
<h1>Dashboard</h1>
</template>
<template #content>
<p>Welcome user!</p>
</template>
</PageLayout>
</template>
<script>
import AppLayout from 'AppLayout'
import PageLayout from 'PageLayout'
export default {
layout: AppLayout, // Persistent layout
components: {
PageLayout // Regular layout
}
</script>
There is an ongoing discussion here:
https://github.com/inertiajs/inertia/issues/171
You need the app-layout as you need to extend from it <template #header>

Where to find current user info inside the user JSON object?

I am trying to find out how to get certain data belonging to the current logged-in user such as full name, email, etc.
The Firefox console outputs this:
----- header.component.ts -----
this.user:
{}
authService.getUser()
Object { _isScalar: false, source: {…} }
this.authService.me()
Object { _isScalar: false, _subscribe: me() }
-------------------------------
This prints out what the authService can provide using console.log() (see the TypeScript code below).
I look through the Object returned by the authService.getUser() method and also the one returned by the this.authService.me() method, but I cannot manage to find where any of the info is located.
I noticed in the HTML code (in a part that was made by the Mean.io generator and not by me) that it references "user.fullname" to display the user's full name in the header bar, but how? And how would you access other pieces of information such as the user's email, phone number, etc?
My profile component TypeScript file looks like this:
import { Component, OnInit, Input } from '#angular/core';
import { Router } from '#angular/router';
import { AuthService } from '../auth/auth.service';
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit {
#Input() user: any = {};
constructor(
private authService: AuthService,
private router: Router
) { }
ngOnInit() {
console.log("----- header.component.ts -----");
console.log("this.user:");
console.log(this.user);
console.log("-------------------------------");
}
profile(): void {
this.navigate('/profile');
}
logout(): void {
this.authService.signOut();
this.navigate('/auth/login');
}
navigate(link): void {
this.router.navigate([link]);
}
}
And my profile component HTML file looks like this:
<header >
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link href="https://fonts.googleapis.com/css?family=ZCOOL+XiaoWei" rel="stylesheet">
<mat-toolbar color="primary">
<a [routerLink]="['/']" class="logo"></a>
<a [routerLink]="['/']" class="toolbar-buttons"> Home </a>
<a class="toolbar-buttons"> Deals </a>
<a [routerLink]="['/about']" class="toolbar-buttons"> About </a>
<span class="example-spacer"></span>
<a class="links side" [routerLink]="['/auth/login']" *ngIf="!user">Login</a>
<div>
<a class="links side" *ngIf="user" [matMenuTriggerFor]="menu">
<mat-icon>account_circle</mat-icon>{{user.fullname}}
</a>
<mat-menu #menu="matMenu">
<button mat-menu-item *ngIf="user && user.isAdmin" [routerLink]="['/admin']">admin</button>
<button mat-menu-item (click)="logout()">logout</button>
<button mat-menu-item (click)="profile()">profile</button>
</mat-menu>
</div>
</mat-toolbar>
</header>
It turns out the user is not settled instantly, It waits for some ajax response, You could use ngOnChanges event and then access the user object like this
ngOnChanges(){
if(this.user && this.user.fullname){
//Do your work here
}
}

Using reusable components in Vue 2 in comination with vue router

I seem to do something wrong when I'm trying to target child components in nested router-views with click events.
Current situation:
I have a component one and component two. Both have a child component called dialog.
Component one and two are being loaded through a router-view in parent component dashboard. Each view has a button to show their child component "Modal".
The button seems to work fine on the view that gets loaded on pageload. But as soon as I switch routes the showModal function does not know the dialog element from which view to target.
I thought the components would be destroyed and rebuilt upon switching routes but apparently not.
Here is my code, I hope someone is able to help:
App
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
/**
* First we will load all of this project's JavaScript dependencies which
* include Vue and Vue Resource. This gives a great starting point for
* building robust, powerful web applications using Vue and Laravel.
*/
require('./bootstrap')
/**
* Next, we will create a fresh Vue application instance and attach it to
* the body of the page. From here, you may begin adding components to
* the application, or feel free to tweak this setup for your needs.
*/
Vue.component('vuetest', require('./components/vuetest.vue'))
const Dashboard = require('./components/dashboard.vue')
const FirstRoute = require('./components/firstroute.vue')
const Second = require('./components/secondroute.vue')
const routes = [
{
path: '/dashboard',
component: Dashboard,
children: [
{
path: 'firstview',
name: 'firstview',
canReuse: false,
component: FirstRoute
},
{
path: 'secondview',
name: 'secondview',
canReuse: false,
component: Second
}
]
}
]
const router = new VueRouter({
routes // short for routes: routes
})
window.EventHub = new Vue()
const app = new Vue({
el: '#app',
router
});
Vuetest
<template>
<div>
<h1>Vue Test</h1>
<router-view></router-view>
</div>
</template>
<script>
export default {
created() {
},
mounted() {
console.log('Component ready.')
}
}
</script>
Dashboard Route
<template>
<div>
<h1>Dashboard</h1>
<navigation></navigation>
<router-view></router-view>
</div>
</template>
<script>
Vue.component('navigation', require('./navigation.vue'))
</script>
Navigation
<template>
<div>
<router-link :to="{ name: 'firstview' }">first</router-link>
<router-link :to="{ name: 'secondview' }">second</router-link>
</div>
</template>
First Route
<template>
<div class="firstroute">
<h1>First Route</h1>
<button class="showmodal" v-on:click="showModal">Showmodal</button>
<modal></modal>
</div>
</template>
<script>
export default {
methods: {
showModal: function () {
EventHub.$emit('showModal')
}
}
}
Vue.component('modal', require('./modal.vue'));
</script>
Second Route
<template>
<div class="secondroute">
<h1>Second Route</h1>
<button class="showmodal" v-on:click="showModal">Showmodal</button>
<modal></modal>
</div>
</template>
<script>
export default {
methods: {
showModal: function () {
EventHub.$emit('showModal')
}
}
}
Vue.component('modal', require('./modal.vue'));
</script>
Modal
<template>
<div class="dialog hidden">
Dialog
</div>
</template>
<style>
.hidden {
display: none;
}
</style>
<script>
export default{
created() {
EventHub.$on('showModal', this.showModal);
},
methods: {
showModal: function() {
document.querySelector('.dialog').classList.toggle('hidden');
}
}
}
</script>
I really appreciate any help.
tiny recomendations
':class' directive instead of native code:
document.querySelector('.dialog').classList.toggle('hidden');
components:
import Modal from './modal'
export default {
...
components:
Modal
}
...
}
instead of
Vue.component('modal', require('./modal.vue'));
.. also Vuex is a good point for this case
additional:
https://github.com/vuejs/vue-devtools
https://jsfiddle.net/uLaj738k/2/
As it turns out the problem was the moment I called the querySelector method.
Assigning the .dialog element to a const in mounted() solved my problem.

Angular2 not working giving error

I am developing a angular2 app in typescript.
here is my component login.ts
import {Component, View} from 'angular2/core';
import {FormBuilder, FORM_DIRECTIVES } from 'angular2/common';
#Component({
selector: 'login',
injectables: [FormBuilder]
})
#View({
template: '/scripts/src/components/login/login.html',
directives: [FORM_DIRECTIVES]
})
export class login {
}
here is my bootstrap.ts
//import {bootstrap} from 'angular2/angular2';
import {bootstrap} from 'angular2/platform/browser';
import {login} from './components/login/login';
bootstrap(login);
here is my index.html file
<!doctype HTML>
<html>
<head>
<script src="https://github.jspm.io/jmcriffey/bower-traceur-runtime#0.0.87/traceur-runtime.js"></script>
<script src="https://jspm.io/system#0.16.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.1/angular2.dev.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.1/router.dev.js"></script>
</head>
<body>
<div class="container">
<login></login>
</div>
<script>
System.import('scripts/src/bootstrap');
</script>
</body>
</html>
but when i run my app it giving me error
i'm unable to what wrong i have did, please help to run this app.
Since you are loading system.js from jspm.io, the default root path is on the jspm.io domain and your bootstrap is therefore not found.
Further, I do not believe there is such a thing as #View anymore. Put all the parameters in #Component.
You should really check out the latest code samples on http://angular.io. It has changed quite a bit lately. Getting the right libraries in the right order is essential.

Integrating WinJS and Angular2

I'm trying to wrap a WinJS rating control in an Angular2 component. When I debug, I can see that WinJS.UI.processAll(); is being called and executed. But I don't see the rating control.
How can I make it work?
Dashboard.cshtml:
#{
ViewBag.Title = "Dashboard";
}
<my-app></my-app>
<script src="/jspm_packages/system.js"></script>
<script src="/config.js"></script>
<script>
System.import('reflect-metadata')
.then(function () {
System.import('angular2')
.then(function () {
System.import("/js/app");
});
});
</script>
app.js:
import { Component, View, bootstrap } from 'angular2';
import 'reflect-metadata';
import 'winjs';
#Component({
selector: 'my-app'
})
#View({
template: '<div data-win-control=\'WinJS.UI.Rating\' data-win-options=\'{averageRating: 3.4}\'></div>'
})
class MyAppComponent {
constructor() {
}
onInit() {
WinJS.UI.processAll();
}
}
bootstrap(MyAppComponent);
As requested by #wonderfulworld
First of all, according to Adding the Windows Library for JavaScript to your page you must add the css file to your html.
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/winjs/4.3.0/css/ui-light.min.css">
Second thing, from alpha37+ you must import and implement the lifecycle hook you're using, onInit in this case (see remove LifecycleEvent).
import { Component, View, bootstrap, OnInit} from 'angular2/angular2';
import 'reflect-metadata';
import 'winjs';
#Component({
selector: 'my-app'
})
#View({
template: '<div data-win-control=\'WinJS.UI.Rating\' data-win-options=\'{averageRating: 3.4}\'></div>'
})
class MyAppComponent implements OnInit {
onInit() {
WinJS.UI.processAll();
}
}
And that would be all. Here's the plnkr.
Glad it helped ;)

Resources