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>
Related
i am working on preact app and i have different components imported in a single page, i want to click on button in header and scroll to particular component.
this is my parent component
<div class={style.root}>
<Header />
<Landing />
<HowItWorks />
<BrowserCatalogue />
<ContactUs />
<Footer />
</div>
and in my header i have 3 buttons
<div class={styles.headerItems}>
<span style={styles.pointer}>Working</span>
<span style={styles.pointer}>Catalogue</span>
<span style={styles.pointer}>Contact</span>
</div>
</div>
like when i click on working my page should scroll to HowItWorks component.any help?
Let me help you friend. You should introduce refs in your parent component.
We will wrap each section in a div and give it a ref prop.
Here is sandbox for your reference: https://codesandbox.io/s/navbar-click-scroll-into-section-us8y7
Parent Component
import React from "react";
import ReactDOM from "react-dom";
import Header from "./Header";
import HowItWorks from "./HowItWorks";
import BrowserCatalogue from "./BrowserCatalogue";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
selected: null
};
}
//refs
howItWorks = React.createRef();
browserCatalogue = React.createRef();
changeSelection = index => {
this.setState({
selected: index
});
};
componentDidUpdate() {
this.scrollToSection(this.state.selected);
}
scrollToSection = index => {
let refs = [this.howItWorks, this.browserCatalogue];
if (refs[index].current) {
refs[index].current.scrollIntoView({
behavior: "smooth",
block: "nearest"
});
}
};
render() {
return (
<div className="App">
<div>
<Header changeSelection={this.changeSelection} />
</div>
<div ref={this.howItWorks}>
<HowItWorks />
</div>
<div ref={this.browserCatalogue}>
<BrowserCatalogue />
</div>
</div>
);
}
}
Header
const Header = (props) => {
const { changeSelection } = props;
return (
<div style={{ background: "green" }}>
<span onClick={() => changeSelection(0)}>Working</span>{" "}
<span onClick={() => changeSelection(1)}>Catalogue</span>{" "}
<span>Contact</span>
</div>
);
}
Workflow:
Each component gets a ref, and we keep that in memory for when we
need to scroll.
Header, we defined a handler in parent called changeSelection()
and we pass it as prop. It takes an index and we use that index to
update the parent state.
Each link, "Working", "Catalogue", etc, will correspond to an index
that matches with a ref in our parent, so setting up an onClick() handler for each span will allow us to pass in that index to changeSelection()
parent state is updated, triggers componentDidUpdate(), in there
we run scrollToSection() which you guessed it takes in an index (stored in our state as "selected"). Create an array of our refs, and simply use the matching index to locate that ref and scroll to that component.
I am slowly teaching myself NodeJs, Express, React, monogoDB and Typescript.. (coming from a MVC C# SQL DB Background)
My very simple Hello world program just needs to communicate with the Express server to display a list of Users. My Express server is on Port 3001 and my Create-React-App Front end is on Port 3000.
my App Component is as follows:
import * as React from 'react';
import './App.css';
const logo = require('./logo.svg');
class App extends React.Component {
state = {users: []}
componentDidMount(){
console.log("Fetching Users");
fetch('/users')
.then(res=> res.json())
.then(users=> this.setState({users}));
}
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React test</h2>
{this.state.users.map(user =>
<div key={user.id}>{user.username}</div>
)}
</div>
<p className="App-intro">
To get started, edit <code>src/App.tsx</code> and save to reload.
</p>
</div>
);
}
}
export default App;
The error:
(21,28): error TS2339: Property 'id' does not exist on type 'never'.
I can see the problem is that I havent defined users to include properties users.id and users.username.. But I am unsure how to do this?
I may have posted this question a bit to quickly.
but I solved my answer
import * as React from 'react';
import './App.css';
const logo = require('./logo.svg');
interface Iuser {
id: number,
username: string
}
class App extends React.Component {
state = {users: Array<Iuser>()}
componentDidMount(){
console.log("Fetching Users");
fetch('/users')
.then(res=> res.json())
.then(users=> this.setState({users}));
}
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React test</h2>
{this.state.users.map(user =>
<div key={user.id}>{user.username}</div>
)}
</div>
<p className="App-intro">
To get started, edit <code>src/App.tsx</code> and save to reload.
</p>
</div>
);
}
}
export default App;
(Creating an interface for the array object)
I did try this previously but had the syntax wrong.
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.
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;
}
}
So I am using Meteor/React, but I used Blaze's login template for its convenience. It works great on the homepage, but when I try to login from any other page on the site, the page reloads and the login appears to have been unsuccessful.
This is my implementation.
AccountsUI.jsx
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
export class AccountsUI extends React.Component {
componentDidMount(){
this.view = Blaze.render(Template.loginButtons, this.refs.loginContainer);
}
componentWillUnmount(){
Blaze.remove(this.view);
}
render(){
return(
<span ref="loginContainer" />
)
}
}
mainLayout.jsx
<div className="container-fluid">
<a className="navbar-btn pull-left panel-body"><b>FAQ</b></a>
<a className="navbar-btn pull-right panel-body"><b>Category</b></a>
<a className="navbar-btn pull-right panel-body"><b>Notifications</b></a>
<a className="navbar-btn pull-right panel-body"><b><AccountsUI /></b></a>
</div>
</div>
Why would this work only on certain pages?
Blaze
Your code looks ok, are you importing all components correctly?
Try: https://atmospherejs.com/gadicc/blaze-react-component
and do:
import Blaze from 'meteor/gadicc:blaze-react-component';
....
<a className="navbar-btn pull-right panel-body"><b><Blaze template="loginButtons" /></b></a>
....
Without trying to change your choice of tools too much, I have been exploring React, Meteor and Authentication for a little while, often getting stuck in state management and other dark holes. Below is a overview of some options:
React Accounts-UI package
Personally as a quick tool I am a big fan of the React Accounts-UI package https://atmospherejs.com/std/accounts-ui
It's easy to implement and has many React specific config options.
Check out 'Create your own styled version' to implement in Navbar at https://github.com/studiointeract/accounts-ui/blob/master/README.md
React with Kadira FlowRouter and ReactLayout
For something within the Navbar, here is a stab with flow router.
From the Meteor Guide User/Authentication section:
While a router is optional and the basic functionality will work without it, it’s also a good idea to pick a router integration:
For Navbar login (Not React Accounts-UI).
You need Flowrouter and Reactlayout
Routes
We create 2 route groups which allow us to build auth logic into Flow router easily:
const publicRoutes = FlowRouter.group( { name: 'public' } );
publicRoutes.route( '/login', {
name: 'login',
action() {
ReactLayout.render( App, {
yield: <Login /> }
);
}
}
);
const authenticatedRoutes = FlowRouter.group( { name: 'authenticated' } );
authenticatedRoutes.route( '/hidden', {
name: 'hidden',
action() {
ReactLayout.render( App, {
yield: <Hidden /> }
);
}
}
);
App:
You can modify this to suit your own setup. The approach here is to grab the reactmeteordata mixing which allows us to test if the user is logged or logging in. The isPublic function allows us to test if the user should be allowed on the current route. The rest should be self explanatory.
App = React.createClass({
mixins: [ ReactMeteorData ],
getMeteorData() {
return {
loggingIn: Meteor.loggingIn(),
hasUser: !!Meteor.user(),
isPublic( route ) {
let publicRoutes = [
'login'
];
return publicRoutes.indexOf( route ) > -1;
},
canView() {
return this.isPublic( FlowRouter.current().route.name ) || !!Meteor.user();
}
};
},
loading() {
return <div className="loading"></div>;
},
getView() {
return this.data.canView() ? this.props.yield : <Login />;
},
render() {
return <div className="app-root">
<AppHeader hasUser={this.data.hasUser} />
<div className="container">
{this.data.loggingIn ? this.loading() : this.getView()}
</div>
</div>;
}
}
);
Header:
Nothing cosmic, we change the brandLink depending on user state. We then check hasUser (passed as a prop from the App component) to change which nav component to display.
AppHeader = React.createClass({
mixins: [ ReactMeteorData ],
getMeteorData() {
return { brandLink: !!Meteor.user() ? '/hidden' : '/login' }; },
render() {
return ( <nav className="navbar navbar-default" role="navigation">
<div className="container">
<div className="navbar-header">
<button type="button" className="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse"><span className="sr-only">Toggle navigation</span><span className="icon-bar"></span> <span className="icon-bar"></span><span className="icon-bar"></span>
</button>
<a className="navbar-brand" href={this.data.brandLink}>AuthExample</a>
</div>
{this.props.hasUser ? <AuthenticatedNavigation /> : <PublicNavigation />}
</div>
</nav> );
}
});
AuthenticatedNavigation component :
AuthenticatedNavigation = React.createClass({
currentUserEmail() {
return Meteor.user().emails[0].address;
},
logout( event ) {
event.preventDefault();
return Meteor.logout( () =>
FlowRouter.go( '/login' ) );
},
render() {
return <div id="navbar-collapse" className="collapse navbar-collapse">
<ul className="nav navbar-nav">
<li className={FlowHelpers.currentRoute( 'hidden' )}>Hidden
</li>
</ul>
<ul className="nav navbar-nav navbar-right">
<li className="dropdown">
<a href="#" className="user-profile-toggle dropdown-toggle clearfix" data-toggle="dropdown">{this.currentUserEmail()} <span className="caret"></span>
</a>
<ul className="dropdown-menu" role="menu">
<li>Account Preferences</li>
<li className="logout" onClick={this.logout}>Logout</li>
</ul>
</li>
</ul>
</div>;
}
});
PublicNavigation Component:
PublicNavigation = React.createClass({
render() {
return (
<div id="navbar-collapse" className="collapse navbar-collapse">
<ul className="nav navbar-nav navbar-right">
<li className={FlowHelpers.currentRoute( 'login' )}>
<a href={FlowHelpers.pathFor( 'login' )}>Login</a>
</li>
</ul>
</div> );
}
}
);
Look at https://themeteorchef.com/snippets/authentication-with-react-and-flow-router/ for more details.