Nuxt3 navigateTo gives Uncaught (in promise) TypeError - vuejs3

I am trying to programmatically route to a detail page from within a list in Nuuxt3 app:
#/pages/items/index.vue
<script setup lang="ts">
const gotoDetail = async (itemId) => {
await navigateTo(`/items/${itemId}`)
}
</script>
<template>
<div>
<NuxtLayout name="main-standard">
<template #main-content>
<div v-for='item in items' :key=item>
<div #click='gotoDetail(item.id)'
</div>
</template>
<template #main-content>
<!-- aside content -->
</template>
<NuxtLayout>
</div>
</template>
And I am getting this error message:
ERROR: Uncaught (in promise) TypeError: Cannot read properties of null (reading 'parentNode')
I have searched for an answer and most solutions mention to wrap the <NuxtLayout> with a div. But that did'nt solve my issue.
I am using a default Layout. So the<NuxtLayout name="main-standard"> is inside this default layout. Both pages: index.vue and [itemId].vue are in the pages/items/ folder.
I am doing something wrong but just can't find it. Does anyone see whats going on?

A few things here are going to break your code.
One, the middle div with the click handler is missing a closing >, contents, and a closing </div>. (Of course you may have omitted that for brevity)
items isn't defined, so there's nothing to iterate over.
You're using the same v-slot name #main-content for multiple templates, but each slot name should be unique. The # attributes (shorthand for v-slot) should match the slot names you write in your layout, and those must also be unique within the component.
The main problem looks to be related to the way you're using layouts. To mess with layout on a page using the component, you have to add this into the setup script:
definePageMeta({
layout: false,
});
A different way of applying your custom layout to this page is to replace the false boolean with the name of the layout, and omit the tag from this page altogether. For that to work, app.vue should have a <NuxtLayout> tag wrapping the <NuxtPage>
Not a breaking change, but it may also simplify things to write
<NuxtLink :to="`/items/${itemId}`">{{ whatever you wanted inside that div }} </NuxtLink>
If you need to run code before navigating to that page, you can add it into the top-level middleware folder, and call that named middleware on the page before which you want it to run.

Related

Storybook Error: Couldn't find story matching 'components-button--page'

I can't seem to resolve this error I am getting in Storybook. I have the following file called Button.stories.mdx:
import { Meta, Story, ArgsTable, Canvas } from '#storybook/addon-docs/blocks';
import Button from './Button';
import ButtonStory from './Button.stories.tsx'
<Meta title="Components/Button" component={Button} />
export const Template = (args) => <Button {...args } />
# Button Component
---
This Button component is supposed to handle all states for a button. It extends off of the HTML button type so that you should have all native HTML features that a button would provide.
We may make some of these optional props required if we deam that it is important. This is usually an accessibility call.
This button should handle actions that occur on the page and if you want to use a href to make it a link you should surround it within an a tag.
Types supported by aero-design-system:
- Primary
- Secondary
## Quick Start
To create a button, use import the `Button` and give it the following props `props`.
<Canvas>
</Canvas>
<ArgsTable of={Button} args={{
backgroundColor: { control: 'color' }
}} />
And I am getting the following error:
Couldn't find story matching 'components-button--page'.
I have tried placing a blank story in there with that ID but that didn't seem to fix anything. I just got a new error
Uncaught Error: Docs-only story
I haven't been able to see anything related to this on here yet.
In my case, the problem was simple, I was trying to load unexisting story:
http://localhost:6006/?path=/story/spoiler--primary
instead, I should've loaded this :)
http://localhost:6006/?path=/story/testcomponent--primary
Something that storybook's documentation doesn't seem to mention is a few important bits:
<Story/> component must have a name property with at least ONE character
<Story/> component must have at least a single child within it
Meaning, the minimum requirement to get the mdx file to render when using the <Story/> component is this:
<Story name="default">
<Button/>
</Story>
This is regardless of whether the <Story/> component is wrapped around the <Canvas/> component or not.
The second half of the problem is <Canvas/> component, and it has just one condition:
It must have at least a single child within it
so the mimimum requirement for <Canvas/> to render is this:
<Canvas>
<Button/>
</Canvas>
Combining everything together for your case scenario, what you need to do with <Canvas/> is this:
<Canvas>
<Story name="default">
<Button/>
</Story>
</Canvas>
Try setting it that way, then refresh the page.
If you are using storybook v6. Try to check stories property at your .storybook/main.js. Make sure the path/file type is correct.
module.exports={
stories:[
'../src/components/Button.stories.mdx', // default page
'../src/**/*.stories.#(js|jsx|ts|tsx|mdx)'
]
}

Calling holder.js in Meteor

I'm new to Meteor and trying to get holder.js to work in the framework. It works on refresh, but when moving from one route to another, it breaks.
The documentation just says "Because Meteor includes scripts at the top of the document by default, the DOM may not be fully available when Holder is called. For this reason, place Holder-related code in a "DOM ready" event listener."
I assume I need a Template.foo.onRendered callback, but unsure how to format it. Here's the HTML:
<img class="holder" src="holder.js/120x120">
And here's the callback I've added in a .js file:
Template.contactSingle.onRendered(function() {
this.$('.holder').Holder.run();
});
Again, the holder.js images appear on refresh, but I can't get them to render when going from one page to another. I'm using FlowRouter for routing.
I'm sure it's something simple. Any help is greatly appreciated!
Change your code from:
Template.contactSingle.onRendered(function() {
this.$('.holder').Holder.run();
});
to:
Template.contactSingle.onRendered(function() {
Holder.run({images: document.querySelectorAll('.holder')});
});
Obviously you do not want to do the costly document.querySelectorAll('.holder'). If you can reduce that to you template using a class from its wrapper.
For example:
Template:
<template name="singlePost">
<div class="single-post">
<h2>This is the singlePost area.</h2>
<img class='holder' src="holder.js/300x200">
</div>
</template>
and onRendered
Template.singlePost.onRendered(function() {
Holder.run({
images: document.querySelectorAll('.single-post .holder')
});
});

Iron router sublayouts - getting data context on yield

Consider the following templates:
layout
pageLayoutStandard
aboutUs
'layout' is my top-level template, which I specify using:
Router.configure({
layoutTemplate: 'layout',
});
Inside layout.html I have the following:
<main id="site-main" role="main">
{{>Template.dynamic template=page.pageLayoutTemplate }}
</main>
I pass some data in from the route: a page object which has a property 'pageLayoutTemplate', having the value 'pageLayoutStandard'.
Inside 'pageLayoutStandard' template, I have:
{{> yield }}
If I visit the '/about-us' route, I render the 'aboutUs' template into 'pageLayoutStandard' - no worries.
And now to my problem...
In my 'aboutUs' template, I expect the 'data' property of the instance to contain the data I passed down from iron-router. However, I find that my the data property contains a 'Template' object; specifically, it contains 'pageLayoutStandard'.
So it looks like 'yield' doesn't like living in a sub-layout - it wants to live at the top level layout for it to get the data from the route. This I validated by moving my yield to the top level layout - the 'aboutUs' template then gets the right data.
Is there any way I can get 'yield' to get the correct data context when it exists in a sublayout?
One solution is to access the data using
Router.current().data()
I my self am fairly new to Iron-Router, but I believe it may be a similar issue that I cam across in another way. There are also several ambiguities in your question, such as where and how are you specifically defining the data context.
Basically I discovered that {{> yield}} creates it's own <body> tags. This also means that things like Template.body.events(); don't propagate into this new Iron-router <body>. This is a known "bug" with Iron-Router.
There is a workaround that has been developed to solve that particular issue, but I'm not sure it's relevant to your case, or at least may not solve the problem since you are not looking to propagate the main body template.
In the end it may be that your routing logic is somewhat inverted, as you mentioned, with the intended usage of Iron-Router.
I believe a better way to perform what you want would be to have:
<main id="site-main" role="main">
{{> yield}}
</main>
With something like this in your router definition
Router.map(function (){
this.route("/about-us", {
template: "pageLayoutStandard"
}
});
You should then be able to set your data context and rendering as per usual.

Polymer 1.0 data binding on custom element for pagination

I am learning Polymer to make a small single page app.
I would like to manage pagination as it is done in the Polymer starter kit, but without using the template dom-bind (because I need one later in the page, and we cannot have one nested in another).
So, I tried to make my own custom element in order to bind its attribute "route" to the iron-pages element. But, as you can imagine, it did not work.
So I tried to make data binding work in a small example. As shown in the "Quick Tour of Polymer", I have made a custom element in "pagination-element.html" :
<link rel="import" href="../bower_components/polymer/polymer.html">
<script>
Polymer({
is: "pagination-element",
properties:{
route : {type:String, value:"/", notify : true}
}
});
</script>
and using it in my page (I have checked that pagination-element.html is imported) :
<pagination-element id="app" route="/">
<paper-input label="{{ route }}"></paper-input>
</pagination-element>
Then, I tried to get the "route" value with an other Polymer custom element, but it only displays me "{{route}}" on my page.
I think I have not understood how data binding works...
Can anyone help me ?
Thanks a lot !
Have a good day
On your page you will need to wrap your pagination-element in an auto binding template using (unless you are using this inside another element):
<template is="dom-bind">
In your paper-input you are setting the label to be the value of route but you have not given route a value. I'm guessing you want the value of it to be that of the route property on the pagination-element:
So in your index.html or other page:
<body unresolved>
<template is="dom-bind">
<pagination-element id="app" route="{{route}}">
<paper-input label="{{route}}"></paper-input>
</pagination-element>
</template>
</body>

Template.body vs Template.myTemplate

I am currently going through the Meteor tutorial (https://www.meteor.com/try), and have come across something about Templates that puzzles me.
In the tutorial, a simple "Todo List" application gets created. In this app, the following HTML is placed into the simple-todos.html file:
<!-- simple-todos.html -->
<head>
<title>Todo List</title>
</head>
<body>
<div class="container">
<header>
<h1>Todo List</h1>
</header>
<ul>
{{#each tasks}}
{{> task}}
{{/each}}
</ul>
</div>
</body>
<template name="task">
<li>{{text}}</li>
</template>
Then, the following JavaScript is placed into the simple-todos.js file:
// simple-todos.js
Tasks = new Mongo.Collection("tasks");
if (Meteor.isClient) {
// This code only runs on the client
Template.body.helpers({
tasks: function () {
return Tasks.find({});
}
});
}
At this point, the example works exactly as intended. However, as I poke around in the documentation, as well as look at other examples on the web, I have noticed slightly different syntax: using Template.myTemplate instead of Template.body.
So, out of curiosity, I altered my JavaScript file to read:
Template.task.helpers({ ...
instead of:
Template.body.helpers({ ...
However, when I run the application now, the client does not display the data from the collection. I don't get any errors about undefined types, like I do if I misspell the template name in the JavaScript, so it seems that it is resolving the template correctly. But why isn't it getting used or rendered?
And to go a little further: when is it appropriate to use Template.myTemplate and when is it appropriate to use Template.body?
The helpers code only works for the template it's attached too.
So, code that works for Template.task will only apply to templates named "task".
Template.body is like the one-off that exists because it would be weird if it didn't. It's a way for you to specifically target the <body>, even though technically, there's no template named "body".
So, what is going on:
Parent template = body
Child template = task
Your logic says:
In the parent template, for each task that we find, render an instance of the child template "task".
If you change your helper from body to task, you won't get any output at all, unless you mimic the pattern that's already happening:
<template name="task">
{{#each tasks}}
do something
{{/each}}
</template>
That's because <body> is the parent template, and you should treat it as such:
<template="body>stuff that normally goes in an HTML body here</template>
When you remove the helpers for the body, then no data gets displayed at all because helpers pass data into the template. And with no helper for the template i.e. the body, you get no data.

Resources