How to render different view structures in Aurelia? - javascript

I have a common html structure in my app.html in order to apply for all pages:
<template>
<require from="header"></require>
<require from="side-bar"></require>
<require from="theme-panel"></require>
<require from="footer"></require>
<!-- BEGIN HEADER -->
<js-header></js-header>
<!-- END HEADER -->
<div class="clearfix"></div>
<!-- BEGIN CONTAINER -->
<div class="container">
<div class="page-container">
<!-- BEGIN SIDEBAR -->
<js-side-bar></js-side-bar>
<!-- END SIDEBAR -->
<div class="page-content-wrapper">
<div class="page-content">
<!-- BEGIN STYLE CUSTOMIZER(optional) -->
<js-theme-panel></js-theme-panel>
<!-- END STYLE CUSTOMIZER -->
<!-- BEGIN ACTUAL CONTENT -->
<div class="fade-in-up">
<router-view></router-view>
</div>
<!-- END ACTUAL CONTENT -->
</div>
</div>
</div>
<!-- BEGIN FOOTER -->
<js-footer></js-footer>
<!-- END FOOTER -->
</div>
<!-- END CONTAINER -->
</template>
However, in my case, i have a login page with a totally different structure compared to others. In my app.js, i tried to use getViewStrategy() method to control which views i will render as following:
activate(params, routeConfig, navigationInstruction){
this.navigationInstruction = navigationInstruction;
//console.log("params", params); // undefined
//console.log("routeConfig", routeConfig); // undefined
//console.log("navigationInstruction", navigationInstruction); // undefined
//console.log("router", router); //undefined
}
getViewStrategy(){
if(this.navigationInstruction == 'login'){
return "app_login.html";
} else {
return "app.html";
}
}
in the code above, 'navigationInstruction' is undefined. Therefore, my solution cannot work well. Does anybody have another solution? Thanks so much!

Great question. In fact, when you have two totally sections of the same single page application, the right thing to do is create multiple root view models, or shells.
First, set your app up for custom initialization by adding aurelia-app="main" to your body tag, and creating a new initialization file correspondingly named main.js. It should look like this.
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging();
// notice that we are setting root to 'login'
aurelia.start().then(app => app.setRoot('login'));
}
The app.setRoot('login') line tells Aurelia to load a file called login.js as the app root. This file should look similar to your current app.js. You can do anything you want in this file and it's corresponding view, and it will stay totally separate from your main app.
To navigate back to your main app, you'll need to call app.setRoot('app'), which means you'll need to inject the Aurelia object into your Login view model.
import { inject, Aurelia } from 'aurelia-framework';
#inject(Aurelia)
export class Login {
constructor(aurelia) {
this.aurelia = aurelia;
}
goToApp() {
this.aurelia.setRoot('app');
}
}
For more information, see my full write-up here: Aurelia Login Best Practices

Try adding 'if' bind at the top of the sections select the page style you want based on the view model variable set during 'canActivate()' or 'activate()' process of each page.
<template>
<template if.bind = "normalMain">
....
</template>
<template if.bind = "loginScreen">
....
</template>
</template>

Related

Nuxtjs: How to call component inside another component?

I understand paradigm "Page-component" but what if I have a page that renders component, how do I call another component inside this component? Currently nuxtjs does not allow me do it. I can not stick to standart "page-component" scheme as I am bulding cart which calls cart-items.
Say If a cart component which is called by page looks like this, how would it call cart-item component inside it?
<!---- cart component called from index.vue --->
<template>
<div>
<Cart-item></Cart-item> < ---------- This doesn't work.
</div>
</template>
<script>
export default {
props: ['items']
}
</script>
I managed it the standard way:
<template>
<div>
<CartItem></CartItem>
</div>
</template>
<script>
import CartItem from '../components/Cart-item'
export default {
props: ['items']
}
</script>
Since nuxtjs auto-registers all components wonder if there is more graceful way.
EDIT: as promised, here is an example on how to pass some content to a component from another one thanks to slots. This is totally working in any Nuxt page ofc.
NestedContent.vue
<template>
<div>
<p>Here is the NestedContent component and below is a slot passed to ParentWithSlots' component</p>
<hr />
<parent-with-slots>
<!-- <template #default> // this one can be omit since we do use the default slot here -->
<p>This content is inserted into the component ParentWithSlots</p>
<!-- </template> -->
</parent-with-slots>
</div>
</template>
ParentWithSlots.vue
<template>
<div>
<p>xxxxxxxxxxx ParentWithSlots' content before slot xxxxxxxxxxx</p>
<slot>Default content in case none is provided</slot>
<p>xxxxxxxxxxx ParentWithSlots' content after slot xxxxxxxxxxx</p>
</div>
</template>
Here is how it looks
PS: you may also give a try to layouts, it can be useful for overall positioning of some of your components visually.
If your components are in the components directory, you can set components: true in your nuxt.config.js and have access to it pretty much anywhere without any additional step with the <cart-item></cart-item> syntax.
More details here: https://nuxtjs.org/blog/improve-your-developer-experience-with-nuxt-components/

What is the Vue.js equivalent of Express-Handlebars sections?

I started a project using Express and Handlebars and then was encouraged to look at Vue.js. I am still at the stage of reading the docs but so far can't understand how to have layouts, partials and sections in Vue.js. I think a partial would be a component, but I'm lost how to have a layout with partials and sections that I can inject content into.
This is what I do using npm express-handlebars in a file called baselayout.hbs:
<!doctype html>
<html lang="en">
<head>
{{> global/headcode }} <!-- partial view with code for the head tag. it has stuff like favicon links --->
{{{_sections.pagemeta}}} <!-- page specific metadata injected here. this would be meta keywords description etc for a page/view --->
</head>
<body>
<div>
{{> global/siteheader }} <!--- partial view for the site's header --->
<div id="base-Container">
<main id="base-Content" role="main">
{{{ body }}} <!--- a page's main body content goes here --->
</main>
</div>
</div>
{{> sitefooter }}
{{{_sections.pagescripts}}} <!-- section for page-specific scripts injected here --->
</body>
</html>
How could I setup something like the above in Vue.js that would also work with server-side rendering? I just need a base layout with header/footer components included but also sections into which page-specific content can go.
For SSR, you should look at Nuxt.js, Vapper, or one of the other SSR Vue frameworks.
That said, yes, you would use components for everything. Generally, you would have one component for your main layout, then one for each view, then individual components for each partial/section that you would then import into your views and/or main layout. So, for example, based on the above code:
Your main app layout:
// AppLayout.vue
<template>
<div id="app-layout">
<site-header />
<router-view />
<site-footer />
</div>
</template>
<script>
import SiteHeader from './components/global/SiteHeader.vue'
import SiteFooter from './components/global/SiteFooter.vue'
export default {
name: 'AppLayout',
components: {
SiteHeader,
SiteFooter
},
meta: {
// metatags and other head content can be modified using vue-meta or similar
}
}
</script>
Example 'partial' component:
// BaseContainer.vue
<template>
<main id="base-container" role="main">
<h1 class="title">{{ content.title }}</h1>
<img :src="image" alt="" />
<base-content v-html="content.body" />
</main>
</template>
<script>
import BaseContent from './components/content/BaseContent.vue'
export default {
name: 'BaseContainer',
components: {
BaseContent
},
props: {
content: {
type: Object,
default() {
return {}
}
},
image: {
type: String,
default: ''
}
}
}
</script>
Example view component:
// MyView.vue
<template>
<div id="my-view">
<base-container :content="content" :image="image" />
</div>
</template>
<script>
import BaseContainer from './components/BaseContainer.vue'
import content from './data/myContent.json'
import image from './assets/myImage.jpg'
export default {
name: 'MyView',
components: {
BaseContainer
},
data() {
return {
content,
image
}
}
}
</script>
You would then use vue-router to specify which view component to load based on the current URL.
You probably need to use components and slots within your components.
Yes you need to create a component for each of your partials. Each component would have a template.
Then you will have a main component that will put all of this together. Using the more granular components you already have (your partials).
Now if the template structure (html for each component) is coming from the server then you can prob use slots https://v2.vuejs.org/v2/guide/components-slots.html which is a way VueJs allow components to receive custom markup when instantiating the components (see the example in the docs)
For the general layout and UI components of your app you may want to look at https://element.eleme.io/#/en-US/component/layout which is a nice alternative to the more popular Vuetify.

Vuejs router-link changes dynamically

I have multiple templates in my project, with a fairly simple url structure.
/startpage/
/startpage/test_1
/startpage/test_2
In my 'App.vue' I made a template, which is shown on every page in my project. That template includes a button, which is supposed to act like a 'Home' button, named 'Projects'.
App.vue
<template>
<div>
<div>
<router-link :to="/startpage/"><button class="Project">Projects</button></router-link>
</div>
<router-view/>
</div>
</template>
When I'm on the startpage (localhost:4545/#/startpage/), the button has the target localhost:4545/#/startpage/.
But when I'm on another page, for example localhost:4545/#/startpage/test_1, the button suddenly has the same url as the page I'm on.
Why does the router change the link dynamically and not keep the target /startpage/?
As described in the documentation, you either need to use binding or not:
<!-- literal string -->
<router-link to="home">Home</router-link>
<!-- renders to -->
Home
<!-- javascript expression using `v-bind` -->
<router-link v-bind:to="'home'">Home</router-link>
<!-- Omitting `v-bind` is fine, just as binding any other prop -->
<router-link :to="'home'">Home</router-link>
So it should be that you don't need to use :, or use a string literal. Currently it tries to use it as a variable, which of course it not present.

vue js with vue router and lazy loading custom tags/components

I'd like to have Vue custom components loaded only after the route has been clicked.
Let's say my app's stripped down html structure looks like this:
<div id="admin">
<admin-menu><!-- component with routes --></admin-menu>
<div id="content-container">
<!-- want dynamically loaded Single Page Components here -->
</div>
</div>
I'd like content container to be the target, where the dynamically loaded Single Page Components should be placed.
The one thing I don't want is to predefine the custom components in the content container right from the start like this:
<div id="admin">
<admin-menu><!-- component with routes --></admin-menu>
<div id="content-container">
<my-component-1></my-component-1>
<my-component-2></my-component-2>
<my-component-3></my-component-3>
<my-component-N></my-component-N>
</div>
</div>
If I do that, they must be registered i.e. loaded when vue hits them on startup of the app and but I'd like the components to be lazy loading.
So how can I initialize and place a single file component in the target content-container only after the respective router link has been clicked?

emberjs - Conditionally rendering templates within a larger template

Ember Community Assemble!
I want to conditionally {{render ''}} small templates inside of the application.hbs sidebar but the content of that sidebar depends on which model's hbs we are routed to. For instance, the contents of the 'permit' sidebar would be different than that of the 'profile' sidebar.
Right now I am only able to render all of the sidebar contents at once regardless of what model.hbs is chosen.
<!-- Right Sidebar in application.hbs START -->
<div id="sidebar-wrapper" class="super-super-float-right-col">
<div id="sidebar-wrapper" class="super-float-right-col">
<div id="sidebar-wrapper" class="float-right-col">
{{render 'applicant'}} <!-- only available to '/person/#' -->
{{render 'location'}} <!-- only available to '/permit/#' -->
</div>
</div>
</div>
<!-- Right Sidebar END -->
<div class="The rest of the content">
{{outlet}} <!--inserts the rest of the html into the main content container -->
</div>
I don't want both 'applicant' and 'location' to be rendered at the same time as they are above, and I want to data inside of 'applicant' to change depending on the id # of 'person'. The same relationship applies to 'location' inside of 'permit.hbs'
VpcYeoman.PermitRoute = Ember.Route.extend({
renderTemplate: function() {
this.render({ render:'location'});
}
});
Application_route.js is blank for now
Although 1.2.0 introduced the ability to use properties for template name in {{view}} it does not work for {{render}} yet.
So your options are to use {{view}} if you can, or a series of {{#if}} in the template, or a component/view to wrap the choice of what to render (one way to do this would be to have a template for each render, and a choice view that binds templateName property to the parentController property that determines which should be displayed)
Here is a jsbin that I used to experiment.

Categories