Identify multiple containers for vuejs components - javascript

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.

Related

injecting dynamic html inside the components from index.html in angular 8

I am working on a web app. which has magnolia as CMS. i am still new here but i will explain the scenario.
magnolia is providing the index.html which has reference of the components.But for some of those components it also provides some additional html content. this additional content has to be shown inside the particular component's template. refer following for better understanding.(remember this is inside the index.html)
<my-component>
<div> this division has to be shown inside the my component template.</div>
</my-component>
I have tried following approaches till now.
trying to use - this apparently does not work as i have learnt recently that the angular is not the master of root template(index.html). so ng-content never works. correct me if i am wrong.
using shadow dom view encapsulation. I am not expert in this, but setting viewencapsulation = shadowdom and defining slot inside the component template fulfills my purpose. The only issue with this approach is the scope of this shadow element. it will be inside the shadow root which has its own scope so no global styles are applied inside it. i had to import all the global css for each such component, which makes the main.js go crazy in size.
can someone please suggest me if there is any better or other solution to this problem?
Have you tried using Input() values for that component?
index.html
<my-comp [myInputs]="'My Input HTML <b>Content</b>'">
your receiving component…
<div [innerHTML]="myInputs"></div>
On my side, I use <ng-content></ng-content> inside the component where I want to put my dynamic content.
Ex:
<app-help-tool>
<span i18n="##pictoTable_helpPictoList">Click on a picto to see its description.</span>
</app-help-tool>
And in HelpToolComponent.ts:
<div
class="help_content"
*ngIf="this.isVisible && this.helpService.isHelpOn()"
[#showHideHelp]
(click)="showHideHelp()"
>
<ng-content></ng-content>
</div>
The result is to put the span content into the div of the component.

VueJS - Load separate component into vue instance

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>

Vue Slot: How to parse then render slot components

Currently building a web page in Vue, and have hit a bit of an issue parsing and then rendering the <slot>'s child components.
I need to be able to take the slot, parse the components into an array, and then render those components for the end-user.
What I've Tried
I've tried many variations of things, most starting with this: this.$slots.default
This is the last version I tried
let slotComponents = [];
this.$slots.default.forEach(vNode => {
slotComponents.push(vNode);
});
But I've also tried selecting the elements within the vNode and using things like $childeren to select the components. No luck so far.
Potential Issues
The cause could be any number of things, but here is what I thought was going on (in order)
I'm not getting the components into the array properly
I'm not rendering them properly or missed something about how they render
Vue isn't supposed to do this?
Edit - Context
Seems like it would be easier if I gave you the full context of my specific problem.
Goal
To create a dynamic tab component. Should look like this.
// Example of component use
<tab-container>
<tab>
<!-- Tab Content -->
</tab>
<tab>
<!-- Tab Content -->
</tab>
<tab>
<!-- Tab Content -->
</tab>
<trash>
<!-- This one won't show up -->
</trash>
</tab-container>
In order to parse through this content, I needed to get the slot data out.
// Inside the <tabs-container> component
computed: {
tabs: function() {
let tabs = []
this.$slots.default.forEach(vNode => {
tabs.push(vNode);
});
return tabs;
}
}
// Inside the <tabs-container> template
<div>
{{tabs[currentTab]}}
</div>
You shouldn't be using template and computed properties if you want to programmatically render out <tab> inside <tab-container>. {{}} in templates are designed to perform basic operations of JS. The most appropriate way will be to use render function.
Render functions - Vue docs
Here is a working example that takes in few tabs components and shows only active tab component: https://jsfiddle.net/ajitid/eywraw8t/403667/

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

Views within non-Backbone templates and rerendering

I have an app that has a single page main.html, and a few major templates admin.html, user.html, etc. This app is not currently backbone, just some sections of it (slow migration).
In the admin page, I have BB views for users, etc. When a user selects a path to /admin/users, e.g. then a master template loader (non-BB) loads admin.html into a standard location in main.html, and then runs reset on the usersView:
// pseudocode
function() {
master.loadTemplate("admin.html").then(function(){usersView.reset();});
}
admin.html contains several elements, only one of which is the template for usersView. So it might look like:
<div id="admin-fragment">
<div id="admin-users">
<!-- template for usersView -->
</div>
<div id="admin-something else">
</div>
</div>
So when I load the script that contains usersView, it looks like:
UsersView = Backbone.View.extend({
el: "#admin-users"
});
All pretty good. Here is the issue.
Sometimes, a user might go somewhere else in this single page app, then go back to /admin/users. So the master template loader loads (and sometimes reloads, depending) the entire admin.html. But the usersView is already attached to the existing (now orphaned) #admin-users.
How do I resolve this? I see two ways, could use some ideas:
instead of usersView.reset() when I select /admin/users, create a new usersView:
// pseudocode
function() {
master.loadTemplate("admin.html").then(function(){usersView = new UsersView();});
}
My concern is the performance and management hit.
somehow tell the existing usersView to "reparent" / "reconnect", i.e. find the element anew.
Any better ideas?
See these posts on good patterns for rendering nested views in Backbone:
Assigning Backbone Subviews Made Even Cleaner
Rendering Views in Backbone.js Isn't Always Simple

Categories