How to set up Stimulus Carousel on Rails 7? - ruby-on-rails

I'm a Ruby on Rails newbie trying to use the Stimulus Component "Stimulus Carousel" on a Rails 7 app to have images of a flat displayed as a carousel.
Following the set up instructions from the official documentation triggered issues like the one mentioned on this other question about using Stimulus Components with Importmaps, so I tried adapting the answer to my problem by doing this:
./bin/importmap pin stimulus-carousel
This pinned the following in importmap.rb :
pin "stimulus-carousel", to: "https://ga.jspm.io/npm:stimulus-carousel#4.0.0/dist/stimulus-carousel.es.js"
pin "dom7", to: "https://ga.jspm.io/npm:dom7#4.0.4/dom7.esm.js"
pin "ssr-window", to: "https://ga.jspm.io/npm:ssr-window#4.0.2/ssr-window.esm.js"
pin "swiper/bundle", to: "https://ga.jspm.io/npm:swiper#7.4.1/swiper-bundle.esm.js"
I also generated the Stimulus controller:
rails g stimulus carousel
And wrote this in app/javascript/controllers/carousel_controller.js:
import { Controller } from "#hotwired/stimulus";
import { Application } from "#hotwired/stimulus";
import Carousel from "stimulus-carousel";
const application = Application.start();
application.register("carousel", Carousel);
// Connects to data-controller="carousel"
export default class extends Controller {
connect() {
super.connect();
console.log('Do what you want here.');
// The swiper instance.
this.swiper;
// Default options for every carousels.
this.defaultOptions;
}
}
I could check the stimulus controller was connected, but nothing changed visually on the flats#show page, the first image was still on top of the second. So I looked into Swiper documentation and added this line in the head of app/views/layouts/application.html.erb:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper#8/swiper-bundle.min.css"/>
Now, this did change the visual of the page: I've got the second picture far to the right of the first picture now, but this still isn't a carousel. I have tried adding this in the head too, but it didn't change anything:
<script src="https://cdn.jsdelivr.net/npm/swiper#8/swiper-bundle.min.js"></script>
Could someone please let me know how I should set this up for the carousel to display properly?
Here is what I have on the flats#show page in case it helps:
<div data-controller="carousel" class="swiper-container">
<div class="swiper-wrapper">
<% #flat.photos.each do |photo| %>
<div class="swiper-slide">
<%= cl_image_tag photo.key, class: "flat-image-show" %>
</div>
<% end %>
</div>
</div>
Thanks in advance!

Related

Import googlemaps via importmaps in Rails 7

What is the way to go to load GoogleMaps via importmaps in Rails7?
I've seen folks import it via the script-tag like this:
<script src="https://maps.googleapis.com/maps/api/js?key=<%= Rails.application.credentials[:p10_google_maps_js_api_key] %>&callback=initMap" async defer data-turbolinks-eval="false"></script>
but I would like to take advantage of the new importmap feature of Rails 7. Unfortunately I don't understand how i could trigger the initMap callback without using it as a script.
So there is a Stimulus component that you could use stimulus-places-autocomplete. However, you could easily implement this yourself and save you the trouble of pulling in a dependency.
At the end of the day, the google callback needs to be fired off. The way most have gotten around this is by creating an event and attaching that to the window. You then add a data-action to your views controller div that will look for this even and fire a callback of its own. That callback being an initializer within your Stimulus controller itself.
##########places_controller.rb##########
import { Controller } from "#hotwired/stimulus";
export default class extends Controller {
static targets = ["street", "city", "state", "zip"];
connect() {
// Can also initialize this with a CustomEvent
const event = new Event('google-maps-callback', {
bubbles: true,
cancelable: true,
})
window.dispatchEvent(event);
}
// additional actions
}
##########index.html.erb##########
<div
data-controller="places"
data-action="google-maps-callback#window->places#initMap"
>
<%# view code here %>
</div>
This still is not a solution that I love. However, its the workaround that we have for now.

Rails React Component Renders But Not Visible on Page

I just added the react-rails gem to my project and tried to add a react component to my index.html.erb file. I used the react component generator to make a simple message/text element, but it does not show up on my view after I deploy to Heroku, but it seems to render when I inspect the page. I have no idea why this isn't working.
Please look at the images I have attached. It looks like the javascript doesn't get compiled??
Body inspection part 2
index.html.erb code
<h2>Dashboard</h2>
<%= react_component('Message', text: 'hello') %>
<h3>Products TEST REACT </h3>
Message.jsx code
var React = require("react")
class Message extends React.Component {
render () {
return (
<div>
<div>Text: {this.props.text}</div>
</div>
);
}
}
Message.propTypes = {
text: React.PropTypes.string
};
module.exports = Message
HTML body inspection
I reverted to the git commit before installing react-rails and used the shakacode react-on-rails gem instead and got it to work

Angular 1.5.7 Controller Scope Failing in iOS 9.3+

I've been struggling a bit to successful get the following Angular 1.5.7 app to properly display user data injected into the Scope of my Controller. It works great on all browsers within a Windows Desktop environment but fails to display the data (it merely displays the template bindings) in iOS 9.3.5.
The Router:
routingApp
.config(function ($routeProvider) {
$routeProvider
.when('/user', {
controller: 'userController',
templateUrl: 'app/partials/userPartial.html'
})
.when('/contacts', {
controller: 'contactController',
templateUrl: 'app/partials/contactPartial.html'
})
.otherwise('/');
});
The Controller:
routingApp
.controller('userController', function ($scope, User) {
//Specify accessible controller attributes
$scope.User = new User();
});
The Partial:
<div class="user" id="userWrapper">
<h1 class="header">Contact Information</h1>
<span class="userField">Name: {{User.name}}</span>
<span class="userField">Phone: {{User.phone}}</span>
<span class="userField">Email: {{User.email}}</span>
<h1 class="header">Address Information</h1>
<span class="userField">Street: {{User.address.street}}</span>
<span class="userField">Suite: {{User.address.suite}}</span>
<span class="userField">City: {{User.address.city}}</span>
<span class="userField">Zipcode: {{User.address.zipcode}}</span>
</div>
See the example here: http://changelib.com/routing/
Full code is here: https://github.com/Thoughtscript/demo-angular_routing
Comments: I've heard that ng-App requires some additional configuration for iOS. Not sure if that's true but I've tried a number of things including directly bootstrapping the application (not sure if I did this correctly), switching to another router library (am using angular-route.min.js), and a couple smaller changes that didn't improve the situation. Any help is much appreciated! Thanks!
Was an idiot - some unsupported ES6 code and uncommitted changes to router dependencies fixed the issue. To be clear, the code above was fine (the problem lay in the factory and service). One take away, double-check ES6 support in iOS.

I got jquery-ui sortable working with meteor templates, but is it a bad hack?

I'm trying to use jquery-ui sortable with nested templates in Meteor, as follows. Here are the two templates in question:
<template name="activityEditor">
{{! the main activity editor view }}
<div class="activity-editor">
<input type="text" name="title" class="input-xxlarge" value="{{info.title}}" placeholder="Type a title here...">
<div class="activity-steps">
{{#each info.steps}}
{{>activityStepEditor}}
{{/each}}
</div>
</div>
</template>
<template name="activityStepEditor">
{{! the container view for each step editor }}
<div class="activity-step" data-id="{{_id}}">
<div class="order">{{order}}</div>
{{!....stuff...}}
</div>
</template>
and the template code (using coffeescript):
_.extend Template.activityEditor, {
# ...stuff...
rendered: ->
$(".activity-steps").sortable {
items: '.activity-step'
handle: '.order'
update: ->
stepIds = ($(el).attr('data-id') for el in $('.activity-step'))
$('.activity-steps').empty() #this must be done in order to steps to re-render properly
Lab.Activity.reorderSteps stepIds
}
$(".activity-steps").disableSelection()
}
The only way I can get this code to work and properly rerender the order is by emptying the container of my sortable elements right after they update with $('.activity-steps').empty(). I've tried cancelling the update event and forcing a rerender by changing another variable watched in the context, but any change causes Exception from Meteor.flush(): undefined after which I can't rerender anything until page reload.
This seems to work, and everything rerenders great. So my question is: is there any reason why I shouldn't do this? Is there a better, standard practice way to handle the sortable that I'm not seeing?
In the near future there'll be a better way to do it, as Meteor team is developing its new rendering engine: http://youtube.com/watch?v=ISNEhPG0wnA
(in this video, Avital Oliver shows exactly a way to do it without redrawing the screen: the object in the list is actually moved on all clients)
See this Meteor's Github Wiki entry for more technical info:
http://github.com/meteor/meteor/wiki/New-Template-Engine-Preview
While that's not officially published, if you need it right now, you could try Nazar Leush's approach:
http://github.com/nleush/meteor-todos-sortable-animation
He also published a working example here: http://todos-dnd-animated.meteor.com

Rendering a Gmap through an AJAX call with Gmaps4rails

There are topics that try to cover this here: Gmaps4rails : map not showing when loaded dynamically and especially here: Rendering google map using gmaps4rails through Ajax, also watched the screencast in which a gmap is dynamically updated but I still don't seem to get it work.
I am trying to load the map in a drop-down tab only if a button is clicked displaying the direction between a user and an offer. In my _location.html.erb partial I have:
<%= gmaps({ "direction" => { "data" => { "from" => current_user.location, "to" => #offer.location } } })%>
(the locations are addresses)
Now this works nicely when the partial is rendered immediately. If I however try to render the partial over an AJAX call later after the whole page has already loaded initially, the gmap is not displayed. Is it possible to initialize and render the gmap through an AJAX call and display directions then?
The reason is quite simple: the partial contains much javascript you can't load and execute this way.
So you can't use RJS there.
The proper way to do is UJS: get data with an AJAX call and render the result. In the following code, I use jQuery.
In your view add:
//include google script
<script type="text/javascript" src='http://maps.google.com/maps/api/js?sensor=false&libraries=geometry'></script>
//include gmaps4rails javascript
<%=javascript_include_tag 'gmaps4rails' %>
<script type="text/javascript" charset="utf-8">
//load map when button click (replace with what you want)
$('#ajax_map').click(function(){
//you have to set a size to the div otherwise the map won't display, that's the purpose of these css classes
$('#map_container').addClass('map_container');
$('#gmaps4rails_map').addClass('gmaps4rails_map');
//create the map object
Gmaps4Rails.initialize();
//of course, replace these two with your dynamic data, you'd have to use some $.ajax jQuery method.
Gmaps4Rails.direction_conf.origin = 'toulon, france';
Gmaps4Rails.direction_conf.destination = 'paris, france';
//read the js file, you can customize much more: https://github.com/apneadiving/Google-Maps-for-Rails/blob/master/public/javascripts/gmaps4rails.js
Gmaps4Rails.create_direction();
});
</script>
<div id="map_container">
<div id="gmaps4rails_map"></div>
</div>
<button type="button" id="ajax_map">Ajax Map</button>
Add the following class in your CSS:
#map-container {
width: 800px;
}
#gmaps4rails_map {
width: 800px;
height: 400px;
}

Resources