How to generate URL for SPA Page - firebase

I've been doing research on this issue but I cannot find my answer. I am trying to create a group chat with Vue2 and F7, so I use GUID to pass to the chat page:
this.value = 'http://localhost:8088/chat-group/' + this.groupId
However, the link doesn't work if I open it in a new tab. This is the error I am getting:
Failed to load resource: the server responded with a status of 404 (Not Found)
Below is the source file:
<template>
<f7-page :name="name">
<f7-navbar :title="title" :back-link="back" sliding>
<f7-nav-right>
<f7-link href="#">Share</f7-link>
</f7-nav-right>
</f7-navbar>
<div class="content-block-title">Create New Chat Group </div>
<div class="list-block media-list">
<ul>
<li>
<div class="item-inner">
<div class="item-subtitle">Please enter your topic</div>
<div class="item-text">{{groupId}}</div>
</div>
</li>
</ul>
</div>
<div class="item-media"><a :href="chatGroupId"><qrcode-vue :value="value" :size="size" level="H"></qrcode-vue></a></div>
</f7-page>
</template>
<script>
import QrcodeVue from 'qrcode.vue'
export default {
data () {
return {
title: 'Chat Group',
name: 'Chat Group',
back: 'Back',
groupId: 'new',
chatGroupId: '',
responseHTML: '',
joined: false,
username: '',
members: ['abc', 'cba'],
newMessage: '',
messages: [{'username': 'abc', 'message': 'hello'}, {'username': 'cba', 'message': 'world'}],
status: '',
value: '',
size: 250
}
},
mounted: function () {
this.groupId = this.guid()
this.value = 'http://localhost:8088/chat-group/' + this.groupId
this.chatGroupId = '/chat-group/' + this.groupId + '/'
console.log(this.value)
},
methods: {
guid () {
function s4 () {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1)
}
return s4() + s4() + s4() + s4() + s4() + s4() + s4()
}
},
components: {
QrcodeVue
}
}
</script>
I am also using App-Framework, and this is the routes.json file:
{
"path": "/chat-chinese/",
"component": "ChatChinese.vue"
},
{
"path": "/chat-english/",
"component": "ChatEnglish.vue"
},
{
"path": "/chat-group/:groupId/",
"component": "ChatGroup.vue"
},
{
"path": "/chat-groups/",
"component": "ChatGroups.vue"
}

You need to use a web server that supports rewriting URLs to a default (e.g. index.html for most Single-Page Apps). Since you're using Firebase already, you could configure and use firebase serve from the Firebase CLI. Just say Y to the question about rewriting all URLs to index.html when running firebase init.
There are plenty of other options available as well, but the key point is that this is a server-side issue, not something that can be solved with client-side code.

Related

Gatsby-Source-Wordpress: Error when compiling. TypeError: Cannot destructure property 'fields' of 'nodesType' as it is undefined

When I go and try to run gatsby develop I get the following error:
TypeError: Cannot destructure property 'fields' of 'nodesType' as it is undefined.
at generateNodeQueriesFromIngestibleFields (/Users/repoFolder/Work/gatsby/node_modules/gatsby-source-wordpress/src/steps/ingest-remote-schema/buil
d-queries-from-introspection/generate-queries-from-ingestable-types.js:155:13)
at buildNodeQueries (/Users/repoFolder/Work/gatsby/node_modules/gatsby-source-wordpress/src/steps/ingest-remote-schema/build-queries-from-introspe
ction/build-node-queries.js:25:25)
at runSteps (/Users/repoFolder/Work/gatsby/node_modules/gatsby-source-wordpress/src/utils/run-steps.ts:41:9)
at runSteps (/Users/repoFolder/Work/gatsby/node_modules/gatsby-source-wordpress/src/utils/run-steps.ts:43:9)
at ingestRemoteSchema (/Users/repoFolder/Work/gatsby/node_modules/gatsby-source-wordpress/src/steps/ingest-remote-schema/index.js:49:5)
at runSteps (/Users/repoFolder/Work/gatsby/node_modules/gatsby-source-wordpress/src/utils/run-steps.ts:41:9)
at runAPI (/Users/repoFolder/Work/gatsby/node_modules/gatsby/src/utils/api-runner-node.js:487:16)
ERROR #gatsby-source-wordpress_112003
gatsby-source-wordpress
Encountered a critical error when running the buildNodeQueries build step.
See above for more information.
not finished createSchemaCustomization - 3.831s
not finished [gatsby-source-wordpress] ingest WPGraphQL schema - 2.379s
I haven't made any changes, or upgraded packages and have since been working on other projects. I tried disabling the Related Posts plugin, as well as a few others in addition to upgrading the gatsby-source-wordpress plugin.
I am configuring the plugin like in my gatsby-config.js:
{
resolve: "gatsby-source-wordpress",
options: {
url: `${process.env.WPGRAPHQL_URL}`,
verbose: false,
develop: {
hardCacheData: false,
},
schema: {
perPage: 10, // currently set to 100
requestConcurrency: 3, // currently set to 15
previewRequestConcurrency: 1, // currently set to 5
},
type: {
Page: {
exclude: true,
},
Menu: {
exclude: true,
},
MenuItem: {
exclude: true,
},
},
debug: {
graphql: {
onlyReportCriticalErrors: true,
},
},
searchAndReplace: [
{
search: `${process.env.GATSBY_WORDPRESS_URL_PROTOCOL}://${process.env.GATSBY_WORDPRESS_URL_PATH}`,
replace: `${process.env.GATSBY_SITE_URL_PROTOCOL}://${process.env.GATSBY_SITE_URL_PATH}`,
},
],
html: {
useGatsbyImage: true,
},
},
},
and I am configuring the plugin / constructing the node ingatsby-node.jslike the following:
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
const BlogPostTemplate = path.resolve("./src/templates/BlogPost.tsx")
const BlogTagPostsTemplate = path.resolve(
"./src/templates/BlogTagPosts.tsx"
)
const BlogCategoryPostsTemplate = path.resolve(
"./src/templates/BlogCategoryPosts.tsx"
)
const BlogPostsResult = await graphql(`
{
allWpPost {
edges {
node {
id
slug
uri
link
title
excerpt
date(formatString: "MMMM DD, YYYY")
modified(formatString: "MMMM DD, YYYY")
author {
node {
avatar {
url
}
id
name
uri
slug
}
}
featuredImage {
node {
localFile {
childImageSharp {
gatsbyImageData(
width: 1920
placeholder: BLURRED
formats: [AUTO, WEBP, AVIF]
)
}
}
}
}
categories {
nodes {
id
count
name
slug
}
}
tags {
nodes {
id
count
name
slug
}
}
}
}
}
}
`)
if (BlogPostsResult.errors) {
reporter.panicOnBuild("Error while running GraphQL query.")
return
}
const BlogPosts = BlogPostsResult.data.allWpPost.edges
BlogPosts.forEach((post, index) => {
const date = post.node.date
createPage({
path: `/${post.node.categories.nodes[0].slug}/${moment(date).format(
"YYYY"
)}/${moment(date).format("MM")}/${post.node.slug}.html`,
component: BlogPostTemplate,
context: {
id: post.node.id,
slug: post.node.slug,
uri: post.node.uri,
previous: index === 0 ? null : BlogPosts[index - 1].node,
next:
index === BlogPosts.length - 1
? null
: BlogPosts[index + 1].node,
},
})
})
createPaginatedPages({
edges: BlogPosts,
createPage: createPage,
pageTemplate: "src/templates/BlogPosts.tsx",
pageLength: 10,
pathPrefix: "blog",
})
const BlogTagPosts = new Map()
const BlogCategoryPosts = new Map()
BlogPosts.forEach((post) => {
const tags = post.node.tags.nodes
if (tags && tags.length > 0) {
tags.forEach((tag) => {
if (BlogTagPosts.has(tag.slug)) {
BlogTagPosts.set(tag.slug, [
...BlogTagPosts.get(tag.slug),
post,
])
} else {
BlogTagPosts.set(tag.slug, [post])
}
if (BlogTagPosts.has(tag.title)) {
BlogTagPosts.set(tag.title, [
...BlogTagPosts.get(tag.title),
post,
])
} else {
BlogTagPosts.set(tag.title, [post])
}
})
}
const categories = post.node.categories.nodes
if (categories && categories.length > 0) {
categories.forEach((category) => {
if (BlogCategoryPosts.has(category.slug)) {
BlogCategoryPosts.set(category.slug, [
...BlogCategoryPosts.get(category.slug),
post,
])
} else {
BlogCategoryPosts.set(category.slug, [post])
}
if (BlogCategoryPosts.has(category.title)) {
BlogCategoryPosts.set(category.title, [
...BlogCategoryPosts.get(category.title),
post,
])
} else {
BlogCategoryPosts.set(category.title, [post])
}
})
}
})
const BlogTagSlugs = [...BlogTagPosts.keys()]
const BlogCategorySlugs = [...BlogCategoryPosts.keys()]
//const BlogCategoryTitles = [...BlogCategoryPosts.keys()];
if (BlogTagSlugs.length > 0) {
BlogTagSlugs.forEach((BlogTagSlug) => {
createPage({
path: `/tag/${BlogTagSlug}`,
component: BlogTagPostsTemplate,
context: {
group: BlogTagPosts.get(BlogTagSlug),
slug: BlogTagSlug,
title: BlogTagPosts.get("tag.title"),
},
})
})
}
if (BlogCategorySlugs.length > 0) {
BlogCategorySlugs.forEach((BlogCategorySlug) => {
createPage({
path: `/category/${BlogCategorySlug}`,
component: BlogCategoryPostsTemplate,
context: {
group: BlogCategoryPosts.get(BlogCategorySlug),
slug: BlogCategorySlug,
title: BlogCategoryPosts.get("category.title"),
},
})
})
}
}
Where do I start?
I have tried upgrading packages, stripping out the config option in the config file, and deleting node_modules, package-lock.json and reinstalling the packages. I do run into errors when trying a normal npm i command, but that is fixed by adding the flag -legacy-peer-deps.
I also got this same error, but I got this after upgrading my WordPress plugins. Im pretty sure this is due to the new version 13.x of WP GraphQL. You can fix this by downgrading the WP GraphQL WordPress plugin to version 12.3. You can grab that v12 version here. Also more info on this issue and if a fix is implemented it will probably be updated here.
Had exactly the same issue.
Downgrading WP GraphQL to version 1.12.3 fixed this for me.
Here's the link wp-graphql.1.12.3

Undefined error when setting CSS values with express data

I am running into a bit of an issue, I have a user object which I have an array where a user can set their own styling options when I attempt to load the page where this styling is needed I get a following error
Error
TypeError: Cannot read property 'primaryColor' of undefined
Code
HTML: <div class="container-fluid" style="background-color:<%=user.branding.primaryColor%>;">
Route:
router.get("/:username", function (req, res) {
User.find({ username: req.params.username}, function (err, foundUser) {
if(empty(foundUser)){
res.render("error", {err:"the user " +req.params.username + " does not exist "})
} else {
if (err) {
console.log("Oh no error: ", err)
}
Link.find().where('author.id').equals(foundUser._id).exec(function (err, foundLinks) {
if (err) {
console.log("Oh no error 2: ", err);
}
res.render('dashboard/preview', {
user: foundUser,
links: foundLinks,
});
});
}
});
});
foundUser:
[
{
links: { link: [] },
settings: { accountIsAccountActive: true, darkMode: false },
branding: {
primaryColor: '#D46975',
secondaryColor: '#9646C8',
image: 'https://i.ya-webdesign.com/images/default-image-png-1.png',
linkRadius: '15px',
linkColor: '#FFFFFF'
},
_id: 5eef79490e264c,
firstname: 'testy',
lastname: 'test',
username: 'test',
__v: 0
}
]
I am starting to feel like this may not be possible, thank you in advance for the help!
foundUser is an array. In your HTML code, you need to use
user[0].branding.primaryColor
Or
change mongo query from
User.find()
to
User.findOne()
Or
Assign foundUser[0] to user.
res.render('dashboard/preview', {
user: foundUser[0],
links: foundLinks,
});

paypal not redirecting to the paypal site but inks are getting in the response in Meteor

I am new to Meteor and integrating Paypal(Which i never had done).
from Client side in meteor -
I am calling method on button click.
<MDBBtn onClick={(e) => callPaypal(e)} color="primary" type="submit">
Add and Continue to PayPal
</MDBBtn>
And this callpaypal() method ->
import { Link as ReactRouterLink } from 'react-router-dom'
const callPaypal = (e) => {
e.preventDefault();
Meteor.call('createPayalPayment', (err, res) => {
console.log(res[1].href) **FIRST CONSOLE**
if (res) {
let link = res[1];
if (link.href) {
return <ReactRouterLink to={`${link.href}`} />
}
}
})
}
Calling createPayalPayment method from server ->
import { Config } from "./paypal_config";
createPayalPayment() {
var data = {
"intent": "sale",
"payer": {
"payment_method": "paypal"
},
"redirect_urls": {
// "return_url": `${Meteor.absoluteUrl('/execute'), { "replaceLocalhost": "true" }}`,
"return_url": "http://127.0.0.1:3000/execute",
"cancel_url": "http://172.20.10.5:3000/cancel"
},
"transactions": [{
"amount": {
"currency": "USD",
"total": "1.00"
},
"description": "This is the payment description."
}]
};
paypal.configure(Config);
var ppCreate = Meteor.wrapAsync(paypal.payment.create.bind(paypal.payment));
var ppExecute = Meteor.wrapAsync(paypal.payment.execute.bind(paypal.payment));
var response = ppCreate(data);
if (response.state !== 'created') {
console.log('not created!!!!!!!!!!!!!!!!')
}
else {
console.log(response); **SECOND CONSOLE**
return response.links;
}
}
And here is my Paypal config ->
export const Config = {
'mode': 'sandbox',
'client_id': 'client_Id',
'client_secret': 'secret'
};
FIRST CONSOLE --> 'https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-1HR12649X9688931M'
SECOND CONSOLE -->>
{ id: 'PAYID-L2IJO4I8GE24787GF168351L',
intent: 'sale',
state: 'created',
payer: { payment_method: 'paypal' },
transactions:
[ { amount: [Object],
description: 'This is the payment description.',
related_resources: [] } ],
create_time: '2020-04-10T15:57:37Z',
links:
[ { href: 'https://api.sandbox.paypal.com/v1/payments/payment/PAYID-L2IJO4I8GE24787GF168351L',
rel: 'self',
method: 'GET' },
{ href: 'https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-1HR12649X9688931M',
rel: 'approval_url',
method: 'REDIRECT' },
{ href: 'https://api.sandbox.paypal.com/v1/payments/payment/PAYID-L2IJO4I8GE24787GF168351L/execute',
rel: 'execute',
method: 'POST' } ],
httpStatusCode: 201
}
As the links[2].href is the URL, where the paypal should be redirect here and user can login to the account. But It is not redirecting. So I am manually redirecting to this link in callPaypal() method just below the First console.
But Still the router is unable to redirect to the link maybe Outer Domain Issue or whatever even it's not showing error.
Please Is there any way that the paypal redirect itself to paypal login? I have already wasted my 2 days on this and still have nothing.
Thanks.
I added the Redirect URL in my paypal developer account for this project.
It looks like you're using an old, redirect-based PayPal integration, so my recommendation is trying the new in-context experience: https://developer.paypal.com/demo/checkout/#/pattern/server
Notice the two fetch calls to '/demo/..' placeholders, which would need to be replaced with actual routes on your server. The first should return a PayID (or newer v2/orders ID), and the second should execute/capture that ID.
This integration is superior because your site stays loaded in the background, and the buyer is able to checkout and pay without 'leaving' it.
On the server side, it looks like you may be using the old deprecated v1 PayPal-node-SDK, which there is no reason to do for a new integration. Instead, use the v2 Checkout-NodeJS-SDK

Is there a way to show related model ids without sideloading or embedding data

My understanding is that using serializeIds: 'always' will give me this data, but it does not.
Here's what I'm expecting:
{
id="1"
title="some title"
customerId="2"
}
Instead the output I'm receiving is:
{
id="1"
title="some title"
}
My code looks something like this:
import {
Server,
Serializer,
Model,
belongsTo,
hasMany,
Factory
} from "miragejs";
import faker from "faker";
const ApplicationSerializer = Serializer.extend({
// don't want a root prop
root: false,
// true required to have root:false
embed: true,
// will always serialize the ids of all relationships for the model or collection in the response
serializeIds: "always"
});
export function makeServer() {
let server = newServer({
models: {
invoice: Model.extend({
customer: belongsTo()
}),
customer: Model.extend({
invoices: hasMany()
})
},
factories: {
invoice: Factory.extend({
title(i) {
return `Invoice ${i}`;
},
afterCreate(invoice, server) {
if (!invoice.customer) {
invoice.update({
customer: server.create("customer")
});
}
}
}),
customer: Factory.extend({
name() {
let fullName = () =>
`${faker.name.firstName()} ${faker.name.lastName()}`;
return fullName;
}
})
},
seeds(server) {
server.createList("invoice", 10);
},
serializers: {
application: ApplicationSerializer,
invoice: ApplicationSerializer.extend({
include: ["customer"]
})
},
routes() {
this.namespace = "api";
this.get("/auth");
}
});
}
Changing the config to root: true, embed: false, provides the correct output in the invoice models, but adds the root and sideloads the customer, which I don't want.
You've run into some strange behavior with how how serializeIds interacts with embed.
First, it's confusing why you need to set embed: true when you're just trying to disable the root. The reason is because embed defaults to false, so if you remove the root and try to include related resources, Mirage doesn't know where to put them. This is a confusing mix of options and Mirage should really have different "modes" that take this into account.
Second, it seems that when embed is true, Mirage basically ignores the serializeIds option, since it thinks your resources will always be embedded. (The idea here is that a foreign key is used to fetch related resources separately, but when they're embedded they always come over together.) This is also confusing and doesn't need to be the case. I've opened a tracking issue in Mirage to help address these points.
As for you today, the best way to solve this is to leave root to true and embed false, which are both the defaults, so that serializeIds works properly, and then just write your own serialize() function to remove the key for you:
const ApplicationSerializer = Serializer.extend({
// will always serialize the ids of all relationships for the model or collection in the response
serializeIds: "always",
serialize(resource, request) {
let json = Serializer.prototype.serialize.apply(this, arguments);
let root = resource.models ? this.keyForCollection(resource.modelName) : this.keyForModel(resource.modelName)
return json[root];
}
});
You should be able to test this out on both /invoices and /invoices/1.
Check out this REPL example and try making a request to each URL.
Here's the config from the example:
import {
Server,
Serializer,
Model,
belongsTo,
hasMany,
Factory,
} from "miragejs";
import faker from "faker";
const ApplicationSerializer = Serializer.extend({
// will always serialize the ids of all relationships for the model or collection in the response
serializeIds: "always",
serialize(resource, request) {
let json = Serializer.prototype.serialize.apply(this, arguments);
let root = resource.models ? this.keyForCollection(resource.modelName) : this.keyForModel(resource.modelName)
return json[root];
}
});
export default new Server({
models: {
invoice: Model.extend({
customer: belongsTo(),
}),
customer: Model.extend({
invoices: hasMany(),
}),
},
factories: {
invoice: Factory.extend({
title(i) {
return "Invoice " + i;
},
afterCreate(invoice, server) {
if (!invoice.customer) {
invoice.update({
customer: server.create("customer"),
});
}
},
}),
customer: Factory.extend({
name() {
return faker.name.firstName() + " " + faker.name.lastName();
},
}),
},
seeds(server) {
server.createList("invoice", 10);
},
serializers: {
application: ApplicationSerializer,
},
routes() {
this.resource("invoice");
},
});
Hopefully that clears things up + sorry for the confusing APIs!

Durandal 2 upgrade redirect issue

Hello and thanks for taking a look at my issue.
I have been migrating my SPA application to use the Durandal 2.0 library, following the sage advice from my oft savior, John Papa. And now that I have completed the upgrade process, I find a strange behavior (or a lack of behavior) when I try to navigate using my menu buttons. Specifically what isn't happening is the browser doesn't redirect to the new page. The interesting thing is that the browser address bar is populated properly and if I simply click in the address bar and press enter (hard reload), I am redirected as expected.
I've looked around and this is not caused due to any security check/redirect which I have seen other discussing elsewhere. Durandal code is unmodified.
js on pages can be quite trivial:
define([], function () {
console.log("welcome loaded");
var vm = {
title: 'Welcome'
};
return vm;
});
So my guess is its something in my configuration of durandal.
main.js:
require.config({
paths: {
'text': '../Scripts/text',
'durandal': '../Scripts/durandal',
'plugins': '../Scripts/durandal/plugins',
'transitions': '../Scripts/durandal/transitions',
'knockout': '../Scripts/knockout-2.3.0',
'bootstrap': '../Scripts/bootstrap',
'jquery': '../Scripts/jquery-1.9.1'
},
shim: {
'bootstrap': {
deps: ['jquery'],
exports: 'jQuery'
}
}
});
define('jquery', function () { return jQuery; });
define('knockout', ko);
define(['durandal/system', 'durandal/app', 'durandal/viewLocator'],
function (system, app, viewLocator) {
// Enable debug message to show in the console
system.debug(true);
app.configurePlugins({
router: true,
dialog: true,
widget: true
});
app.start().then(function () {
toastr.options.positionClass = 'toast-bottom-right';
toastr.options.backgroundpositionClass = 'toast-bottom-right';
// When finding a viewmodel module, replace the viewmodel string
// with view to find it partner view.
viewLocator.useConvention();
// Adapt to touch devices
// app.adaptToDevice();
//Show the app by setting the root view model for our application.
app.setRoot('viewmodels/shell', 'entrance');
});
});
shell.js:
define(['../../Scripts/durandal/plugins/router', 'viewmodels/config', 'services/datacontext'], function (router, config, datacontext) {
function addSession(item) {
router.navigate(item.hash);
}
function boot() {
// $(".page-splash-message").text("Configuring routes...");
router.makeRelative({ moduleId: 'viewmodels' });
router.map(config.routes);
router.buildNavigationModel();
$(".page-splash-message").text("Let's make traxx..!");
return router.activate();
}
function failedInitialization(error) {
var msg = 'App initialization failed: ' + error.message;
}
return {
addSession: addSession,
adminRoutes: adminRoutes,
profileRoutes: profileRoutes,
visitorRoutes: visitorRoutes,
router: router,
activate: function () {
datacontext.primeEditData().then(boot).fail(failedInitialization);
}
};
});
routes in config.js
define(['../../Scripts/durandal/plugins/router'], function (router) {
toastr.options.timeOut = 4000;
toastr.options.positionClass = 'toast-bottom-right';
var startModule = 'Welcome';
var serviceName = 'api/Zepher';
var imageSettings = {
imageBasePath: '../content/images/photos/',
unknownPersonImageSource: 'unknown_person.jpg'
};
var routes = [
{ route: '', moduleId: 'home/welcome', title: 'Welcome', nav: false, },
{ route: 'Welcome', moduleId: 'home/welcome', title: 'Welcome', nav: false, },
{ route: 'NotFound', moduleId: 'home/notFound', title: 'Not Found', nav: false, },
{ route: 'Roadmap', moduleId: 'home/roadmap', title: 'Roadmap', nav: false, },
{ route: 'Register', moduleId: 'account/register', title: 'Register', nav: true, caption: '<i class="fa fa-user"></i> Register' },
{ route: 'RegisterAccounts', moduleId: 'account/registerAccounts', title: 'Register Accounts', nav: false, caption: '<i class="fa fa-key"></i> Register Accounts', },
];
return {
debugEnabled: ko.observable(true),
imageSettings: imageSettings,
servicetitle: serviceName,
startModule: startModule,
router: router,
routes: routes,
activate: function () {
console.log("config activate called");
router.makeRelative({moduleId: 'viewmodels'});
router.map(routes);
router.buildNavigationModel();
//sets up conventional mapping for
//unrecognized routes
router.mapUnknownRoutes('home/nontFound', 'not-found');
//activates the router
return router.activate();
// no longer needs a start module
}
};
});
found what I was missing in my upgrade, so I thought I'd share what I've learned.
Seems I forgot to update my Shell.html file.
From this:
<div>
<header>
<!--ko compose: {view: 'shared/nav', afterCompose: router.afterLogging, transition: 'entrance' } --><!--/ko-->
</header>
<section id="content" class="main">
<!--ko compose: {model: router.activeItem, afterCompose: router.afterCompose, transition: 'entrance', cacheViews: true } --><!--/ko-->
</section>
<footer>
<!--ko compose: {view: 'shared/footer'} --><!--/ko-->
</footer>
</div>
to This:
<div>
<header>
<!--ko compose: {view: 'shared/nav', afterCompose: router.afterLogging, transition: 'entrance' } --><!--/ko-->
</header>
<section id="content" class="main container-fluid page-host" data-bind="router: { transition: 'entrance', cacheViews: true }">
</section>
<footer>
<!--ko compose: {view: 'shared/footer'} --><!--/ko-->
</footer>
</div>

Resources