VueJS - Load separate component into vue instance - javascript

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>

Related

How to use layouts from a folder in nuxtjs 3?

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.

can I "import" React CSS modules from a string?

I have a React project that uses CSS Modules by importing .css files. e.g. import styles from "./styles/MyComponent.css";
I find myself now in a situation where a component is receiving a customized snippet of CSS as a string in response to a dynamic call to the server.
Is it possible to take this string (which is unknown until runtime) and essentially do the same thing to it that import does to the .css file when it is compiled by webpack?
For example:
import styles from "./styles/MyComponent.css";
//later on in component...
moreStyle = "a string containing valid CSS";
//do *something* here to moreStyle string to do whatever importing does to a file.
myJSX = (
<div className={styles.someClass}>
This div content is styled by someClass
</div>
<div className={moreStyle.someOtherClass}>
This div content needs to be styled by someOtherClass, but obviously this isn't working
</div>
);
You can try this:
import styles from "./styles/MyComponent.js";
myJSX = (
<div style={styles.someClass}>
This div content is styled by someClass
</div>
<div style={styles.someOtherClass}>
This div content needs to be styled by someOtherClass, but obviously this isn't working
</div>
);
Consider creating a serialized object, instead.
// Filename: MyComponentStyle.js
//Example styles
export const styles = {
someClass: { height: 10 },
someOtherClass: {
backgroundColor: 'red',
}
};
React doesn't work like your typical HTML/CSS/JS app. The thing to note that JSX may look like HTML but it is not HTML.
In your code, className is being defined as a string, which is expected, however, there's possibly no CSS being referred to in this document. Try to console.log it and see what you get.
...
Another possible solution is to simply have your style within the same component file. A common design choice for component styling is inline styling. This is especially useful for projects of medium-large scale, where managing files can get difficult.
Helpful references:
https://reactjs.org/docs/dom-elements.html#style
https://codeburst.io/4-four-ways-to-style-react-components-ac6f323da822

Meteor optional template

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

Identify multiple containers for vuejs components

I'm working on a website where I have classic multiple pages powered by blade templates, but I want to use vuejs2 components for the most dynamic needs.
It's working fine, but I couldn't find a convenient way to declare containers parsed by VueJs for components.
I want to use many components in many places, but I can't declare a big main container as VueJs container because of conflicts with <script> tags.
If I do that I get errors like : - Templates should only be responsible for mapping the state to the UI. Avoid placing tags with side-effects in your templates, such as <script>, as they will not be parsed.
I currently do something like that in my app.js file :
new Vue({
el: '#need-components-1'
});
new Vue({
el: '#need-components-2'
});
new Vue({
el: '#need-components-3'
});
I really don't like it, I'd like to be able to declare either the whole content as VueJs-able, or at least use a common class or attribute for containers. Here I need to add a new id each time I want to use a new place for components. Moreover, VueJs posts console errors for every time an element is not found because obviously they are not all always loaded.
Is there any elegant / convenient way to do that ?
Thanks
Use some identifier that an element is a root Vue and then, when the page loads, iterate over the elements you find on the page and create Vue's for them.
Here is an example.
console.clear()
const vues = document.querySelectorAll("[data-vue]")
for (let v of vues)
new Vue({el: v, data: {message: "Hello from Vue"}})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div data-vue="true">
<h2>{{message}}</h2>
</div>
<h4>Some other page content</h4>
<div data-vue="true">
<h2>{{message}}</h2>
</div>
<h4>Some other page content</h4>
<div data-vue="true">
<h2>{{message}}</h2>
</div>
Obviously you'll need to figure out a way to marry the appropriate data with the correct Vue if it should be different from Vue to Vue.

Duplicated styles on head and a lot of <style> elements

I'm quite happy with angular-material2 components but there are some weird behaviors that I don't understand and they don't have a proper documentation especially for teeming and customizing their components.
My project looks like:
.src
--app
--components
--login-component
login-component.html
login-component.scss
login-component.js
--login-component
home-component.html
home-component.scss
home-component.js
--and so on ...
app.component.html
app.component.scss
app.component.ts
app.module.ts
app.routing.ts
--assets
--environments
--scss
styles.scss
_material2-theme.scss
_variables-scss
_utilities.scss
_reset.scss
favicon
index.html
and so on ....
In angular-cli.json I have modified the styles to look at scss/style.scss
...
"styles": [
"scss/styles.scss"
]
...
the _material2-theme.scss looks like:
//////////////////////* THEMES */ Custom Blue Theme*/
#import '~#angular/material/theming';
#include mat-core();
$app-primary: mat-palette($mat-light-blue);
$app-accent: mat-palette($mat-light-blue, A200, A100, A400);
$app-theme: mat-light-theme($app-primary, $app-accent);
#include angular-material-theme($app-theme);
/*default palette forground/background*/
$light-foreground-palette: map-get($app-theme, foreground);
$light-background-palette: map-get($app-theme, background);
$primary: map-get($app-theme, primary);
$accent: map-get($app-theme, accent);
and inside of style.scss I am importing everything to be compiled with scss cli compiler
//////////////////////* CUSTOM */
#import "_material2-theme.scss";
#import "_reset.scss";
#import "_utilities.scss";
//////////////////////* COMPONENTS */
#import "~app/components/login/login.component.scss";
My question is after the scss is compiled we have in html head many style tags some of them duplicated and look like:
Everything seems to be compiled in one style that is added in head(tha one that has type attribute) and and after that each scss component splited in each css component with its separate style in head, and that is very weird. I am doing something wrong or is just tha way is working material2?
The behavior you're seeing is caused by ViewEncapsulation.Emulated defined for material components.
First, you don't need to add styles for components to the global styles "scss/styles.scss":
//////////////////////* COMPONENTS */
#import "~app/components/login/login.component.scss";
If you do that, besides your styles getting duplicated you will lose style encapsulation because styles added for components/login/login.component.scss will become global.
Now, to the question why there are many style elements. When you use ViewEncapsulation.Emulated, and it's a default view encapsulation mode, Angular will put each component's style into its own style tag and add attribute to elements inside a component template. In material all components use emulated encapsulation mode so for each component the style tag is added.
If you add #Component({..., encapsulation: ViewEncapsulation.Native }) for your components, you will see the style for your components will be removed.
Here is the logic that adds the style tag:
export class DomSharedStylesHost extends SharedStylesHost implements OnDestroy {
...
private _addStylesToHost(styles: Set<string>, host: Node): void {
styles.forEach((style: string) => {
const styleEl = this._doc.createElement('style'); <--- creates a style element
styleEl.textContent = style;
this._styleNodes.add(host.appendChild(styleEl));
});
}
In Material design for Angular the stylesheet is split into:
style - margins, paddings etcetera that is linked from the component
theme - mostly colors that is build using the theme scss
typography - fonts & font properties that is built using the typography scss
This causes similar selectors, but with different properties inside.
I think it is the live development server that comes with angular cli or webpack that loads css dynamically which causes duplication of style tags. I believe this is not happening in a production build.
PS. Strange of you to add _reset.scss after anything else.
I do not think it is due to having separate styles for each component.
This issue is similar to what's there in a ngularjs-material as well.
Angular-material includes some default theme css as a const variable in JavaScript.
You can read a more on this issue ,
https://stackoverflow.com/questions/33466779/how-to-get-rid-off-angular-material-extra-styles-and-css-linked-by-it-forcefull
In development, the angular cli or webpack compiles every css/scss files from each component to a style tag in the HTML page.
ie, in your case: login-component.scss, home-component.scss each will be loaded inside seperate style tags in the HTML page.
All these will be compiled into a single css file and will be linked in index.html in production conf.
So I too think its the way angular works!
Currently I see that there is only one file generated in production build ng build --prod .
There are multiple style tag in your head may be your every component is linked with some external different css Or you have written internal css at related HTML page.
If you want that there is only one style tag throughout application then in that case you have to write all style in single file.

Categories