Rails 6 GlideJS carousel issues (without jquery) - ruby-on-rails-6

I have been looking for a way to remove the need for jQuery in my project. The carousel is my last bastion of jQuery, so I am trying to set up GlideJS for a carousel with does not rely on jQuery. When I run new Glide({}) in rails console I get NoMethodError (undefined method 'Glide' for main:Object)
If I look at the errors in the dev tools in firefox I am seeing [Glide warn]: Root element must be a existing Html node / Uncaught TypeError: this.root is undefined.
application.js
require("#rails/ujs").start()
require("turbolinks").start()
require("channels")
import 'controllers'
import "bootstrap"
import Glide from '#glidejs/glide'
new Glide('.glide').mount()
frontpage.html.erb
<div class="glide">
<div class="glide__track" data-glide-el="track">
<ul class="glide__slides">
<li class="glide__slide">slide1</li>
<li class="glide__slide">slide2</li>
<li class="glide__slide">slide3</li>
<li class="glide__slide">slide4</li>
</ul>
</div>
<div class="glide__arrows" data-glide-el="controls">
<button class="glide__arrow glide__arrow--left" data-glide-dir="<"><</button>
<button class="glide__arrow glide__arrow--right" data-glide-dir=">">></button>
</div>
</div>
I have not worked with much, if any, javascript in my projects to date.

As I am not savy on JS, this was given to me and it worked.
In the application.js
document.addEventListener("turbolinks:load", () => {
new Glide('.glide', {
}).mount()
})

Related

Vue.js 3 - JS from imported Bootstrap 5 does not work, CSS works fine [duplicate]

I want to use Bootstrap 5 with Vue 3. As Bootstrap 5 uses vanilla JS (no JQuery), can I use Bootstrap 5 directly in a Vue 3 project (without using Bootstrap-Vue)? Can someone guide me how to use Bootstrap 5 with Vue 3?
Bootstrap 5 no longer needs jQuery so it's easier to use with Vue, and no longer requires a library like bootstrap-vue.
Install bootstrap as you would any other JS module in the Vue project using npm install or by adding it to the package.json...
npm install --save bootstrap
npm install --save #popperjs/core
Next, add the Bootstrap CSS and JS components to the Vue project entrypoint (ie: src/main.js)...
import "bootstrap/dist/css/bootstrap.min.css"
import "bootstrap"
Then, the simplest way to use Bootstrap components is via the data-bs- attributes. For example here's the Bootstrap Collapse component...
<button
class="btn btn-primary"
data-bs-target="#collapseTarget"
data-bs-toggle="collapse">
Bootstrap collapse
</button>
<div class="collapse py-2" id="collapseTarget">
This is the toggle-able content!
</div>
Demo with Navbar component
Or, you can import any Bootstrap components and "wrap" them as Vue components. For example here's the Popover component...
import { Popover } from bootstrap;
const popover = Vue.component('bsPopover', {
template: `
<slot/>
`,
props: {
content: {
required: false,
default: '',
},
title: {
default: 'My Popover',
},
trigger: {
default: 'click',
},
delay: {
default: 0,
},
html: {
default: false,
},
},
mounted() {
// pass bootstrap popover options from props
var options = this.$props
var ele = this.$slots.default[0].elm
new Popover(ele,options)
},
})
<bs-popover
title="Hello Popover"
content="This is my content for the popover!"
trigger="hover">
<button class="btn btn-danger">
Hover for popover
</button>
</bs-popover>
Demo |
Read more
Yes, you can use Bootstrap without Bootstrap-Vue.
Install these two packages with npm:
npm install --save #popperjs/core bootstrap#next
Import Bootstrap to src/main.js:
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap";
Example usage for Vue Template:
<div class="dropdown">
<button
class="btn btn-secondary dropdown-toggle"
type="button"
id="dropdownMenuButton1"
data-bs-toggle="dropdown"
aria-expanded="false"
>
Check Bootstrap
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
</ul>
</div>
Result:
While the Bootstrap CSS can be used with any framework (React, Vue, Angular, etc), the Bootstrap JavaScript is not fully compatible with them.
Here's the official reasoning from the Bootstrap 5 docs:
While the Bootstrap CSS can be used with any framework, the Bootstrap JavaScript is not fully compatible with JavaScript frameworks like React, Vue, and Angular which assume full knowledge of the DOM. Both Bootstrap and the framework may attempt to mutate the same DOM element, resulting in bugs like dropdowns that are stuck in the “open” position.
The docs state to use an alternative framework-specific package instead of the Bootstrap JavaScript such as React Bootstrap, BootstrapVue, and ng-bootstrap.
Unfortunately, BootstrapVue is only compatible with Vue2/Nuxt2 and there is no version available for Vue3/Nuxt3 yet.
It's easy to implement this once you understand how Bootstrap modals work. Bootstrap modals have a div element with a class of modal fade. When it is triggered, this element gets the show and d-block class as well. In addition, the body tag gets an additional class of modal-open. When the modal is closed, this process is reversed. Understanding this, we can easily implement Bootstrap 5 modals in one's code:
Import Bootstrap 5's CDN in your code. Add both the CSS and JS to your code.
Our sample Single Page Component will look like this:
<template>
<div>
<p>Test modalnow</p>
<div>
<button type="button" class="btn btn-primary" #click="modalToggle">My Modal</button>
<div
ref="modal"
class="modal fade"
:class="{ show: active, 'd-block': active }"
tabindex="-1"
role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
#click="modalToggle">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
</div>
</div>
</div>
<div v-if="active" class="modal-backdrop fade show"></div>
</div>
</div>
</template>
Here we are using the basic Bootstrap 5 modal.
<script>
export default {
data() {
return {
active: false,
}
},
methods: {
modalToggle() {
const body = document.querySelector("body")
this.active = !this.active
this.active ? body.classList.add("modal-open") : body.classList.remove("modal-open")
},
},
}
</script>
Here, we have a variable active which is initially set false. So modal will not show up on page load. On clicking a link, we use a method to toggle this variable. This will remove the show attribute and the d-block class from our modalm and remove the modal-open property from the body tag.
bootstrap 5 must have popper for run , try with this npm :
npm install --save bootstrap
npm i #popperjs/core
please add this package :
npm install --save #popperjs/core
To make bootstrap work with SSR you can not:
import "bootstrap";
as others have suggested since it will give you an error:
document is not defined
This is not an optimal solution but it will work
npm install bootstrap
And only import the bootstrap scss in your styles tag so you have access to the bootstrap variables etc.
<style lang="scss">
#import 'bootstrap/scss/bootstrap';
.sticky-sidebar {
z-index: $zindex-sticky;
...
}
</style>
And then just add the bootstrap bundle to your header. Note: you don't have to add the css now that it is imported in your component.

Using Bootstrap 5 with Vue 3

I want to use Bootstrap 5 with Vue 3. As Bootstrap 5 uses vanilla JS (no JQuery), can I use Bootstrap 5 directly in a Vue 3 project (without using Bootstrap-Vue)? Can someone guide me how to use Bootstrap 5 with Vue 3?
Bootstrap 5 no longer needs jQuery so it's easier to use with Vue, and no longer requires a library like bootstrap-vue.
Install bootstrap as you would any other JS module in the Vue project using npm install or by adding it to the package.json...
npm install --save bootstrap
npm install --save #popperjs/core
Next, add the Bootstrap CSS and JS components to the Vue project entrypoint (ie: src/main.js)...
import "bootstrap/dist/css/bootstrap.min.css"
import "bootstrap"
Then, the simplest way to use Bootstrap components is via the data-bs- attributes. For example here's the Bootstrap Collapse component...
<button
class="btn btn-primary"
data-bs-target="#collapseTarget"
data-bs-toggle="collapse">
Bootstrap collapse
</button>
<div class="collapse py-2" id="collapseTarget">
This is the toggle-able content!
</div>
Demo with Navbar component
Or, you can import any Bootstrap components and "wrap" them as Vue components. For example here's the Popover component...
import { Popover } from bootstrap;
const popover = Vue.component('bsPopover', {
template: `
<slot/>
`,
props: {
content: {
required: false,
default: '',
},
title: {
default: 'My Popover',
},
trigger: {
default: 'click',
},
delay: {
default: 0,
},
html: {
default: false,
},
},
mounted() {
// pass bootstrap popover options from props
var options = this.$props
var ele = this.$slots.default[0].elm
new Popover(ele,options)
},
})
<bs-popover
title="Hello Popover"
content="This is my content for the popover!"
trigger="hover">
<button class="btn btn-danger">
Hover for popover
</button>
</bs-popover>
Demo |
Read more
Yes, you can use Bootstrap without Bootstrap-Vue.
Install these two packages with npm:
npm install --save #popperjs/core bootstrap#next
Import Bootstrap to src/main.js:
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap";
Example usage for Vue Template:
<div class="dropdown">
<button
class="btn btn-secondary dropdown-toggle"
type="button"
id="dropdownMenuButton1"
data-bs-toggle="dropdown"
aria-expanded="false"
>
Check Bootstrap
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
</ul>
</div>
Result:
While the Bootstrap CSS can be used with any framework (React, Vue, Angular, etc), the Bootstrap JavaScript is not fully compatible with them.
Here's the official reasoning from the Bootstrap 5 docs:
While the Bootstrap CSS can be used with any framework, the Bootstrap JavaScript is not fully compatible with JavaScript frameworks like React, Vue, and Angular which assume full knowledge of the DOM. Both Bootstrap and the framework may attempt to mutate the same DOM element, resulting in bugs like dropdowns that are stuck in the “open” position.
The docs state to use an alternative framework-specific package instead of the Bootstrap JavaScript such as React Bootstrap, BootstrapVue, and ng-bootstrap.
Unfortunately, BootstrapVue is only compatible with Vue2/Nuxt2 and there is no version available for Vue3/Nuxt3 yet.
It's easy to implement this once you understand how Bootstrap modals work. Bootstrap modals have a div element with a class of modal fade. When it is triggered, this element gets the show and d-block class as well. In addition, the body tag gets an additional class of modal-open. When the modal is closed, this process is reversed. Understanding this, we can easily implement Bootstrap 5 modals in one's code:
Import Bootstrap 5's CDN in your code. Add both the CSS and JS to your code.
Our sample Single Page Component will look like this:
<template>
<div>
<p>Test modalnow</p>
<div>
<button type="button" class="btn btn-primary" #click="modalToggle">My Modal</button>
<div
ref="modal"
class="modal fade"
:class="{ show: active, 'd-block': active }"
tabindex="-1"
role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
#click="modalToggle">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
</div>
</div>
</div>
<div v-if="active" class="modal-backdrop fade show"></div>
</div>
</div>
</template>
Here we are using the basic Bootstrap 5 modal.
<script>
export default {
data() {
return {
active: false,
}
},
methods: {
modalToggle() {
const body = document.querySelector("body")
this.active = !this.active
this.active ? body.classList.add("modal-open") : body.classList.remove("modal-open")
},
},
}
</script>
Here, we have a variable active which is initially set false. So modal will not show up on page load. On clicking a link, we use a method to toggle this variable. This will remove the show attribute and the d-block class from our modalm and remove the modal-open property from the body tag.
bootstrap 5 must have popper for run , try with this npm :
npm install --save bootstrap
npm i #popperjs/core
please add this package :
npm install --save #popperjs/core
To make bootstrap work with SSR you can not:
import "bootstrap";
as others have suggested since it will give you an error:
document is not defined
This is not an optimal solution but it will work
npm install bootstrap
And only import the bootstrap scss in your styles tag so you have access to the bootstrap variables etc.
<style lang="scss">
#import 'bootstrap/scss/bootstrap';
.sticky-sidebar {
z-index: $zindex-sticky;
...
}
</style>
And then just add the bootstrap bundle to your header. Note: you don't have to add the css now that it is imported in your component.

VueJS component won't render on front end (possibly Drupal issue)

The site I'm currently working on is built in Drupal 7. I have one form that requires user input so I'm attempting to build it with VueJS. The form is contained all within one template file (.tpl.php) and all the content is provided in this template file or via the VueJS Javascript (nothing is coming from the CMS).
The issue I have is that the Vue components are not rendering on the front-end, but when I copy the code into a JSFiddle they do, so I'm guessing it is an issue with the interaction between VueJS and Drupal. Here is a screenshot of my markup when inspecting...
Here is the code from the .tpl.php file...
<div id="app">
<form>
<div>
<label for="year">Per Year</label>
<input type="radio" name="frequency" id="year" value="year" v-model="frequency" checked>
<label for="month">Per Month</label>
<input type="radio" name="frequency" id="month" value="month" v-model="frequency">
</div>
</form>
<ul class="plans">
<template id="plan-component">
<h2 class="plan-name">{{ name }}</h2>
<h2 class="plan-cost">{{ price }}</h2>
<h2 class="plan-tagline">{{ tagline }}</h2>
Choose this plan
</template>
<li>
<plan-component :frequency="frequency"
name="Basic"
tagline="Basic tagline"
price-yearly="Free"
price-monthly="Free"
></plan-component>
</li>
<li>
<plan-component :frequency="frequency"
name="Rec"
tagline="Rec tagline"
price-yearly="3"
price-monthly="4"
></plan-component>
</li>
<li>
<plan-component :frequency="frequency"
name="Team"
tagline="Team tagline"
price-yearly="4"
price-monthly="5"
></plan-component>
</li>
<li>
<plan-component :frequency="frequency"
name="Club"
tagline="Club tagline"
price-yearly="5"
price-monthly="6"
></plan-component>
</li>
</ul>
</div>
..and the code from my JS file...
Vue.component('plan-component', {
template: '#plan-component',
props: ['frequency', 'name', 'tagline', 'priceYearly', 'priceMonthly'],
computed: {
'price': function() {
if (this.frequency === 'year') {
return this.priceYearly;
} else {
return this.priceMonthly;
}
}
},
methods: {
makeActivePlan() {
// We dispatch an event setting this to become the active plan
this.$dispatch('set-active-plan', this);
}
}
});
new Vue({
el: '#app',
data: {
frequency: 'year',
activePlan: {name: 'no', price: 'You must select a plan!' }
},
events: {
'set-active-plan': function(plan) {
this.activePlan = plan;
}
},
});
And here is the JSFiddle which outputs the components correctly - https://jsfiddle.net/2xgrpLm6/
What browser are you using? <template> tags are not supported in IE.
Another idea is to make sure you are never using fragment components (meaning wrap everything inside your template with a div like so:
<template id="foobar">
<div>
CONTENT HERE
</div>
</template>
Lastly, have you turned on Vue debug mode? Before you instantiate your Vue instance, set Vue.config.debug = true and see if you get console errors then.
Try moving the <template id="plan-component">...</template> code outside of the Vue instance. I.e., such that it is not contained within <div id="app">...</div>.
This has solved a similar problem for me in the past, though I'm not sure if it applies here.
For anyone having a similar issue, the solution was simple. After Jeff suggested turning on Vue debug mode (and downloading the Dev version of Vue JS instead of minified - https://vuejs.org/guide/installation.html) the console gave the error [Vue warn]: Cannot find element: #app.
The issue was that Drupal was loading my scripts in the <head>, before <div id="app"> was loaded in the DOM. As such #app couldn't be found. After outputting the scripts before the closing <body> tag all was sorted. See here for more information [Vue warn]: Cannot find element

How do I load templates from my routes file with Flow Router

I'm struggling to load templates when using Flow Router. If I include my templates in /client/app.html -
<body>
{{> applicationLayout}}
</body>
<template name="applicationLayout">
<header>
{{> Template.dynamic template=header}}
</header>
</template>
<template name="mainMenu">
<ul>
<li>who we are </li>
<li>what we do</li>
<li>where we work</li>
</ul>
</template>
And my routes in /lib/routes.js -
FlowRouter.route( '/terms', {
action: function() {
BlazeLayout.render( 'applicationLayout', {
header: 'mainMenu',
} );
},
name: 'termsOfService', // Optional route name.
});
Then everything works fine. But I want to split out my templates. So I create /imports/ui/components/main-menu.html
<template name="mainMenu">
<h2> {{currentPage}} </h2>
<ul>
{{#each pages }}
<li> {{ page.headerTitle }} </li>
{{/each}}
</ul>
</template>
I remove the template from /client/app.html and then add the following import to the top of my /lib/routes.js
import '/imports/ui/components/main-menu.js';
Now I get the following error -
/Users/aidan/.meteor/packages/meteor-tool/.1.3.2_4.1s3pnfi++os.osx.x86_64+web.browser+web.cordova/mt-os.osx.x86_64/dev_bundle/server-lib/node_modules/fibers/future.js:267
throw(ex);
^
Error: Cannot find module './main-menu.html'
at require (packages/modules-runtime/.npm/package/node_modules/install/install.js:78:1)
at meteorInstall.imports.ui.components.main-menu.js (imports/ui/components/main-menu.js:5:1)
at fileEvaluate (packages/modules-runtime/.npm/package/node_modules/install/install.js:141:1)
at require (packages/modules-runtime/.npm/package/node_modules/install/install.js:75:1)
at meteorInstall.lib.routes.js (lib/routes.js:1:1)
at fileEvaluate (packages/modules-runtime/.npm/package/node_modules/install/install.js:141:1)
at require (packages/modules-runtime/.npm/package/node_modules/install/install.js:75:1)
at /Users/aidan/Code/edu-finance/.meteor/local/build/programs/server/app/app.js:239:1
at /Users/aidan/Code/edu-finance/.meteor/local/build/programs/server/boot.js:283:10
at Array.forEach (native)
Exited with code: 8
Your application is crashing. Waiting for file change.
I can't work out what's going on here. How do I split out my templates?
UPDATE
So I've managed to get somewhere by putting my includes in /client/app.js. This is displaying things as I expected. I'm not sure this is the best way to structure everything. And I'd still love to know what the problem was trying to do the imports in /lib/routes.js

FullpageJs and Meteor : Capture event from template

I'm struggling with fullpage.js in Meteor.
When I add a button inside a template, the Template.templatename.events function does not fire on event.
For example:
Template HTML (messages.html)
<template name="messages">
<ul>
{{#each mess}}
<li>{{ messContent}}</li>
{{/each}}
<button class="addMessage">Add Message!</button>
</ul>
</template>
Template JavaScript (messages.js)
Template.messages.helpers({
mess: function(){
return Messages.find();
}
});
Template.messages.events({
'click .addMessage' : function(event){
console.log("clicked")
}
})
Main HTML
<head>
<title>fulltest</title>
</head>
<body>
{{> full}}
</body>
<template name="full">
<div id="fullpage">
<div class="section active">
<h1>First slide</h1>
{{> messages}}
</div>
<div class="section">
<h1>Second slide</h1>
</div>
</div>
</template>
My fullpage initialisation:
Template.full.rendered = function(){
$('#fullpage').fullpage();
}
If I remove the fullpage initialisation then the click event gets logged. Still new at Meteor, I didn't manage to grasp what's going wrong here.
All help much appreciated,
Joris
Use delegation or use verticalCentered:false and scrollOverflow:false.
From the fullPage.js FAQs:
My javascript/jQuery events don't work anymore when using fullPage.js
Short answer: if you are using options such as verticalCentered:true or overflowScroll:true in fullPage.js initialization, then you will have to treat your selectors as dynamic elements and use delegation. (by using things such as on from jQuery). Another option is to add your code in the afterRender callback of fullpage.js
Explanation: if you are using options such as verticalCentered:true or overflowScroll:true of fullPage.js, your content will be wrapped inside other elements changing its position in the DOM structure of the site. This way, your content would be consider as "dynamically added content" and most plugins need the content to be originally on the site to perform their tasks. Using the afterRender callback to initialize your plugins, fullPage.js makes sure to initialize them only when fullPage.js has stopped changing the DOM structure of the site.

Resources