I am developing an app using NuxtJs 3. In nuxt 3, layout feature has been changed. We can use layout via <NuxtLayout name="layoutName"> and this layoutName is in layouts folder. My problem is,layout is in another folder inside layouts directory. How can i use that?
Not sure to fully understand what you want to have at the end, but let's say that you want to change the path of the layouts directory with the dir property.
You can do that with the following in nuxt.config.ts
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
dir: {
layouts: 'fancyLayouts/nested'
}
})
Then, all of your layouts will be available in that directory
Now, we have a very simple other.vue layout with the following (custom.vue is almost the same)
<template>
<div>
<slot />
<br/>
<p>other layout</p>
</div>
</template>
We could have the following in /pages/index.vue
<script>
definePageMeta({
layout: false,
});
export default {
data: () => ({
layout: "custom",
}),
};
</script>
<template>
<NuxtLayout :name="layout">
<button class="border p-1 rounded" #click="layout === 'other' ? layout = 'custom' : layout = 'other'">
Switch layout
</button>
</NuxtLayout>
</template>
This will allow us simply switch from one layout to another successfully aka custom or other.
If what you want is to have 2 directories like:
/layouts/custom.vue
/layouts/nested/other.vue (nested 1 level more)
Then, I did not achieved to have a simple way of managing the import regarding a layout.
I've never seen this kind of configuration and I'd even say that if you have a lot of various layouts (to the point of nesting them), then I recommend wrapping your content inside of components rather than layouts anyway.
Because layouts are meant to be quite simple global outside wrappers for the pages, nothing regarding a complex structure. It's more of a component's purpose.
This feature was added in Nuxt 2 long ago.
Adds support for folders in /layouts #1865
Nuxt.js does not respect layouts in separate folders. #1854
It does not seem to work the same way in nuxt 3 because I'm facing the same issue.
Layouts inside folders Nuxt js 3
update...
I checked the source code for Nuxt 3 and definitely is not supported what you are trying to do.
example: layouts/folder/custom.vue
and then use it in pages like follows
<NuxtLayout name="folder/custom">some code ...</NuxtLayout>
I just sent a PR for this feature.
Adds support for folders in /layouts #5796
Hopefully it gets accepted or at least it gives someone the idea to add this.
Related
I'm often in doubt when I want a new behaviour of a component.
Let's make a simple example, I have <app-title> component:
<div>
<h1>{{title}}</h1>
</div>
Some time after, inside another page I need to put a button alongside the title. The problem is, should I create a new title component or should I parametrize the existing one?
I can edit <app-title> to look like this:
export class AppTitleComponent implements OnInit {
#Input() showButton: boolean;
title = 'App title';
constructor() {}
ngOnInit() {}
}
<div>
<h1>{{title}}</h1>
<button *ngIf="showButton">{{buttonTitle}}</button>
</div>
This is a simple example and may be obvious but using Angular I always have this problem, think about complex components: the #Input() would become many using this method, but creating a new component would increase files and complexity.
From this example you could say to create two components, one for title and another for button but that's only because this is a very simple case. Think about changing a component from "compact" mode to "expanded" and viceversa. On the one hand you may need to have the large component and on the other hand have it smaller in size and showing less information
Is there some guideline about this?
Thanks
I think it's important thinking about behavior within the context of your component. Is the button core to behavior of the title component? Does it make sense to not only display the button, but also handle its events within the context of the title component? If the answer is no, then at some granular level I'd split the components.
Here are some other things you can consider:
Anticipating that your title component may need some content wrapped with the title, you can use transclusion:
<div>
<h1>{{title}}</h1>
<ng-content></ng-content>
</div>
Then, in the parent, you'd do something like this:
<div>
<app-title-component title='title'>
<button>Some Button Text</button>
</app-title-component>
</div>
You could write a wrapper component that packages the title and the button together... ie:
<div>
<app-title-component></app-title-component>
<button>Some Button Text</button>
</div>
You could, as suggested, parameterize the configuration. I recommend thinking about if the behavior that you are parameterizing is part of the core behavior of the component. For example, if you want to paramaterize whether or not a legend shows on a chart, that makes sense because the legend is a core feature of the chart. But I probably wouldn't parameterize whether or not the chart should be followed by a raw data sheet. Instead I would create a new component for that and render them in sequence, because the data sheet is not part of the core behavior of the chart, even though sometimes I'd want to put them next to each other.
At the end of the day, you have to think about the decision in terms of your app, your app's future usability, and developer ease (e.g.- will it make sense to a future developer that this button is packaged with title).
I hope you find this helpful.
I'm trying to make a react component that changes the width from a parameter but it's doesn't work and I don't know why.
function Bar() {
const p =80
const style = `bg-slate-500 h-8 w-[${p.toFixed(1)}%]`
console.log(style)
return (
<div className=' h-8 w-full'>
<div className={`bg-slate-500 h-8 w-[${p}%]`}>
</div>
</div>
)
}
export default Bar
With this code I get a full-size bar, but if I use a strict String with 80.0 it works fine
The other answers are wrong. In Tailwind V3 JIT is included and you won't need to set mode to jit anymore. And even in Tailwind V2 the posted code wouldn't work.
Following the docs, you must avoid string interpolation and use complete class names.
This is wrong:
<div class="text-{{ error ? 'red' : 'green' }}-600"></div>
Instead use Complete class names:
<div class="{{ error ? 'text-red-600' : 'text-green-600' }}"></div>
What about using inline styles?
You may be tempted to use inline styles, but remember Content Security Policies are increasingly under scrutiny to disallow unsafe inlines. Any inline style or script could in theory be injected with unintended content. Furthermore, the whole point to Tailwind is to generate CSS at build time, so you'd be working around it and might as well not even be using it in this case.
in tailwind.config.js add mode: 'jit'.
I would suggest https://v2.tailwindcss.com/docs/just-in-time-mode to learn more.
e.g:
module.exports = {
//other options
mode: 'jit',
}
I ran into a similar issue building a site with Astro & Tailwind.
The solution I discovered was to use the 'safelist' feature that allows you to define certain classes that will be force-compiled, regardless of whether or not they are found when scanning the specified content locations in the tailwind.config.cjs file.
An example of the code I used to fix my situation:
module.exports = {
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
Adding a safelist and classes to it:
safelist: ["lg:grid-cols-[1fr_4fr]", "lg:grid-cols-[1fr_3fr_1fr]"],
...
Here's a link to the relevant area in the Tailwind documentation.
If your p's value is a constant that will be used across the app, you can instead define it in your tailwind.config.js file like this.
theme: {
extend: {
spacing: {
"p-value": "80%",
},
},
}
Then you can modify your code to this.
<div className="bg-slate-500 h-8 w-p-value">
Include mode: 'jit' in tailwind config file
Recently, I've been learning Gatsby.js & GraphQL and have been working on a small project. My goal with this project is to have simply one page that displays all posts on the page, meaning no slugs, etc referencing a specific page, just having all the markdown files converted to the template I have, then all on one page. An example of what I mean is below:
Looking through the Gatsby documentation, I can see how to make a list of markdown "blogs" but they're just links that you essentially click to take you to the actual post page which isn't what I want
I tried doing something similar to that, but my problem is I can't really pass the HTML to my component because it's made up of many UI components (Material UI card components). For example, I had something like this in my index.js file that displays all my posts
const Posts = edges
.filter(edge => !!edge.node.frontmatter.question)
.map(edge => <Grid item xs={8}><PostCard key={edge.node.id} question={edge.node.frontmatter.postTitle} postContents={}/></Grid>)
Obviously the problem there is, I can't pass dangerouslySetInnerHTML into postContents. I could pass edge.node.html but then the HTML tags themselves would be showing in the content. I know there's a better way to do this, which utilizes the template file in my templates folder but I'm not exactly sure how to, I had no luck finding much regarding this on stackoverflow or the Gatsby documentation.
Would appreciate any insight on how to achieve this result
Thanks!
If you cannot pass dangerouslySetInnerHTML into postContents, then you can instead pass the edge or node itself in, and then update that component to set the inner html of a div instead.
So your PostCard would look something like:
export default function PostCard({ node }) {
return (
<div key={node.id}>
<h1>{node.frontmatter.postTitle}</h1>
<div dangerouslySetInnerHTML={{ __html: node.html }} />
</div>
);
}
And your index.js could be simplified to:
const Posts = edges
.filter(edge => !!edge.node.frontmatter.question)
.map(edge => <Grid item xs={8}><PostCard node={edge.node} /></Grid>)
At my job I'm currently in the progress of a redesign of our web platform, including moving a lot of old javascript / jquery into VueJS.
I have a global.js file which holds our Vue components and we have a vendor.js which holds Vue, Axios, Vuex,...
Now we have a text editor on our site and this editor has a Vue version. The problem is that this text editor is pretty big, almost 500kb minified for production. So I've created a separate component in a separate file for this editor, since we only need it on two pages.
Now, since my global Vue takes up the whole page I cannot insert the text editor into it because you can't put a Vue instance inside another Vue instance.
Is there a way that I can keep the editor as a totally separate file but somehow use the global Vue instance when it gets loaded on a page?
I've searched for quite a bit but haven't come across this problem anywhere. Maybe it's not possible to do this.
Try loading TextEditor.vue asynchronously.
new Vue({
// ...
components: {
TextEditor: () => import("./TextEditor.vue")
}
})
Reference.
You can modify the CSS for the editor to position:fixed or position:absolute and put it inside your app component. Then use a v-if to toggle visibility.
You can also wrap your editor using a 3rd party dialog component to wrap it into a modal popup window.
Another unrelated suggestion is to use lazy loading if the component has a large size.
You can merge multiple Vue files together through imports and the components property within a .vue file.
<template>
<div>
<TextEditor></TextEditor>
</div>
</template>
<script>
import TextEditor from 'path/to/TextEditor.vue';
export default {
name: 'Main',
components: {
TextEditor
}
}
</script>
Furthermore, you can set this to be a dynamic import. If your project was set up with vue-cli, you should already have webpack installed. If that's the case then you can also set the dynamic import to have one of two types: prefetch or preload. Essentially prefetch downloads files when the app is idle and preload downloads it in parallel to the main component. Implementing either of those aspects ends up looking like this:
export default {
name: 'Main',
components: {
TextEditor: import(/* webpackPrefetch: true */ 'path/to/TextEditor.vue')
/* OR */
// TextEditor: import(/* webpackPreload: true */ 'path/to/TextEditor.vue')
}
}
You should take a look at splitting your bundle into chunks: https://webpack.js.org/guides/code-splitting/
Only load your text editor chunk on pages that require it.
You can put a Vue instance inside another Vue instance.
Let's say I have the following HTML
<div id="main-app"></div>
new Vue({
el: "#main-app",
template: `<div>
This is my main application which has an editor div
<div id="editor"></div>
</div>`
});
new Vue({
el: "#editor",
template: `<div>
This is my editor component
</div>`
});
The final resulting HTML would be
<div>
This is my main application which has an editor div
<div>
This is my editor component
</div>
</div>
So I have started to learn Meteor and trying to get my head around how I should format the template correctly. This is how I set up my project:
A/view.html - html file for template A
A/script.js - import A/view.html. Potentially be the "controller" to work with the interaction
B/view.html - html file for template B
B/script.js - import A/view.html. Potentially be the "controller" to work with the interaction
routes.js - route file, include all script.js for template A and B
So I have two questions:
First, I want to make A as the base template, meaning it will have style and javascript tags as well as a "styles" and "scripts" optional blocks in case the child template wants to add extra files. How can I do this? I have tried normal ways:
Creating 2 blocks named styles and scripts in each child templates. This doesn't work since routes.js imports everything, meteor complains there are 2 templates having the same name
Using Template.dynamic. This work but I have to declare what template I want to render in the block of "styles" and "scripts", which is a bit untidy, in my opinion, when the project goes big.
The second question, as I described what I am currently doing with my routes, what is the best way to localize(?) the block to the current file?. Would it be possible to have 2 blocks called "scripts" in 2 different child templates and meteor not complaining?
Thanks guys :)
I'm not 100% sure what you mean by "blocks", but I assume you want to have A be the template that everything else fits into, and then have other pages feed into it?
If so, it sounds like you would want to use dynamic templates, and have A be a layout.
Here is an example of a layout, which imports other templates from one of my projects (It actually imports two constant templates (loginNavbar and modalWindow) as well as dynamic ones depending on what I call to it. You could add as many styles and other things as you want to the layout itself):
<template name="loginLayout">
<div class="loginNavbarTemplateDiv">
{{> loginNavbar}}
</div>
<div class="loginContentTemplateDiv">
{{> Template.dynamic template=content}}
</div>
{{> modalWindow}}
</template>
So then my routes.js looks like this (renders the outside loginLayout with the inner content of login or register):
FlowRouter.route('/login', {
name: 'login',
action: function() {
BlazeLayout.render("loginLayout", { content: 'login' });
}
});
FlowRouter.route('/register', {
name: 'register',
action: function() {
BlazeLayout.render("loginLayout", { content: 'register' });
}
});
Overall, I wouldn't have two templates named the same thing, and if you structure your files/app properly, it shouldn't be too untidy.
Please let me know if this helps and if you have any other questions, I'd be glad to help.
-
Here is a great tutorial on dynamic templates if you want more:
https://themeteorchef.com/tutorials/using-dynamic-templates
And a guide on how to structure your Meteor app files (helps a ton and makes everything better):
https://guide.meteor.com/structure.html