Meteor optional template - javascript

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

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.

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>

Jade Includes conditionals to check the actual page

Is there a way to check which page are you in Jade and include something (a partial page or a specific CSS Style) based in that page? For example:
If I am in homepage, then include just the HOMEPAGE-head.jade
Else - include the NORMAL-head.jade
Here is an in context example:
doctype html
html
body
if HOMEPAGE
include ./includes/HOMEPAGE-head.jade
else
include ./includes/NORMAL-head.jade
h1 My Site
p Welcome to my super lame site.
include ./includes/foot.jade
Thank you!
Alternatively you can structure your Jade to use inheritance to achieve what you want.
E.g.,
layout.jade:
doctype html
html
body
block header
block content
h1 My Site
p Welcome to my super lame site.
block footer
include ./includes/foot.jade
homepage.jade:
extends ./layout.jade
block header
include ./includes/HOMEPAGE-head.jade
normal.jade:
extends ./layout.jade
block header
include ./includes/NORMAL-head.jade
And then have all your normal pages use normal.jade and your homepage to use homepage.jade.
There are two approaches I know of.
Option A: 2 layouts and extends
Make two layouts: layout.jade and layout-homepage.jade, changing the include line accordingly. Most of your pages will extends layout, but index.jade will extends layout-homepage.
Option B: variables block
in layout.jade:
- var HOMEPAGE = false;
block variables
doctype html
html
body
if HOMEPAGE
include ./includes/HOMEPAGE-head.jade
else
include ./includes/NORMAL-head.jade
h1 My Site
p Welcome to my super lame site.
include ./includes/foot.jade
Then in index.jade:
block variables
- HOMEPAGE = true;
h1 This is your home page template...
All the rest of your pages will default to HOMEPAGE = false so they don't need any changes to make this approach work.
An option that I'd suggest is using separate layouts. It takes advantage of the system that Harp already has in place, and helps maintain the concept of "different content, different file." Using Harp, you can specify an explicit layout for any given page in the _data.json or _harp.json files.
From http://harpjs.com/docs/development/layout
Layouts other than _layout can be specified in a _data.json. This is
useful if you need even finer control of Layouts, or if you want to
name your Layout something other than _layout.
myapp.harp.io/
|- _layout.ejs
|- index.ejs
|- about.md
+- articles/
|- _data.json
|- _an-example-layout.ejs
|- _another-one.jade
|- article-one.md
+- article-two.md
Here, it’s possible to make article-one.md use _an-example-layout.ejs by specifying layout in the _data.json file in that folder:
{
"article-one": {
"layout": "_an-example-layout",
"title": "Example Title"
},
"article-two": {
"layout": "_another-one",
"title": "Another Example Title"
}
}
Now, each article will use the specified Layout.
If it's a minor difference you want to make and you can't justify having a completely separate template, you can also pass data through to the include if you use Harp's partial() function instead of Jade's include keyword. Then the logic for handling the different variable value would be in a single head.jade file. Your example might look like this.
foo.jade:
doctype html
html
body
!= partial("./includes/head.jade", { page: bar })
h1 My Site
p Welcome to my super lame site.
include ./includes/foot.jade
head.jade should now have access to the page variable.
A third option can take advantage of the current object that Harp injects into the templates. If you're accessing //site.com/foo/bar, current.source === "bar" and current.path === ["foo", "bar"]. You can use this object to dynamically set class names, etc. You can read more about it here: http://harpjs.com/docs/development/current

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

ASP.net MVC - Views and jQuery Best Practices

I'm trying to figure out what the best practice is for using jQuery in an MVC app. Specifically, I would like to know what I should do so that I don't clutter all my views with individual document.ready statements.
As an example:
I have the following Views:
/Views/Shared/_Layout.cshtml
/Views/Home/Index.cshtml
/Views/Home/_Dialog.cshtml
/Views/Home/_AnotherDialog.cshtml
I have a controller action that will render the Home/Index View, which uses the Layout and renders two partial views (or editor templates, display templates, etc.). This one controller action has rendered 4 or more views. Each view is using some jquery document.ready code.
Currently, I have the code at the bottom of each view:
// In Index
<script type="text/javascript">
$(function() {
$('#tabs').tabs()
});
</script>
// In _Dialog
<script type="text/javascript">
$(function() {
$('#some-dialog').dialog( ... );
});
</script>
I know this isn't a very good practice because it is already getting unmanageable in my small project. What are some good practices to follow when I have tons of pages that all need some jQuery / javascript initialization code separated across dozens of views?
You could do something along the lines of what Telerik do with their javascript registrar. Basically, make this registrar available in your view model. At the simplest level, all it has to do is keep track of strings added to it:
public class JavascriptRegistrar
{
private StringBuilder jsBuilder_ = new StringBuilder();
public Add(string js)
{
builder.Append(js).Append('\n');
}
public string ToString()
{
return "<script type=\"text/javascript\">" + jsBuilder_.ToString() + "\n</script>";
}
}
Your partial views will then add to this when rendering:
<h1>In my view!</h1>
#Model.Registrar.Add("$function() { /* ... */ }")
Finally, at the bottom of your main view, when you're done:
#Model.Registrar.ToString()
Which will write out all the javascript it has collected during rendering.
If the initialisation is specific to a view and you know it definitely won't be used outside that view, for example some page specific behaviour, then just leave it in the view!
There is nothing wrong with having script tags in all your views, as long as you aren't replicating js between views. I think people tend to misunderstand 'separation of concerns' in this case and think that simply means 'keep different languages away from each other at all costs'...that is wrong, clearly if some page initialisation logic/behaviour is specific to a page, then the html and js intrinsically 'concern' each other, therefore moving the js into a separate file is not really 'good practice', if anything it makes your code more difficult to understand.
I personally like to open up a View, and be able to see all the js and css that is specific to that page as soon as I open it, makes it nice and readable. However, obviously if code needs to be shared then you need to bust it out your view and get in your scripts folder whwere it can be referenced by anything!
EDIT
In your example above I see in your Index view you initialise your tabs. This is fine as it is, however, if you added tabs somewhere else in the project then it might be better to create your tabs using a .tabs class rather than #tabs id, and then in an external js file initialise all your tabs at once by calling $('.tabs').

Categories