How to SSR nested components or slotted content with vueonrails? - ruby-on-rails

I am trying to add Vue.js components to a Ruby-on-Rails app with SSR support by using Hypernova and vueonrails helpers.
I am able to render a registered component with Hypernova from a view (.html.erb) by using this helper:
<%= render_vue_component('HelloWorld', message: 'Hello World from SSR!') %>
Let's say my HelloWorld component has a <slot> in its template:
<template>
<div class="helloworld">
<h1>{{ message }}</div>
<slot></slot>
</div>
</template>
I want to replicate something like the following in the .html.erb using render_vue_component:
<hello-world message="Hello World from SSR!">
<hello-world>SSR Nested component</hello-world>
</hello-world>
How could I pass child elements to the render_vue_component helper so they get rendered by Hypernova?

Well, I analyzed the repo and by looking at the it, I think you should use the render_vue_component only for rendering the root component, like the main.js when you start a project with #vue/cli:
<%= render_vue_component('App', { router }) %>
I came to this conclusion by analyzing the vueonrails source code.
If you look to the render_vue_component function, it only takes the identifier and a data object, which internally calls render_react_component.
By this signature, it simply don't fit with Vue's render function signatures, which are (element, children) or (element, attributes, children).
Maybe you find a better support by opening an issue on the repo.
But I'd think twice before using this project because there isn't any documentation so far and checking the website, seems like they are more focused in selling books about the project than creating a documentation.

Related

How can I pass children to a component in a slim template using react_rails

I'm using react_rails to render react components inside of a slim template in a Ruby on Rails application. Is there a way to pass children to a React component, where the children are rendered server-side in slim?
Here is what I would ideally like to have:
<MyComponent>
<p>my custom content</p>
</MyComponent>
Where the content comes from the template itself, something like this:
= react_component 'MyComponent' do
p my custom content
Is this possible? If so what's the best way to pass children to a react_component?
On page load, the react_ujs driver will scan the page and mount components using data-react-class and data-react-props.
The view helper's signature is:
react_component(component_class_name, props={}, html_options={})

Parametrized nested erb components in Rails

I'm quite new with Rails, but have React experience. I'd like to manage with erb files sth I could do in React that way:
class MyElement extends Component {
render() {
return <div>
some text
{this.props.myFirstParam + this.props.mySecondParam}
some another text
</div>
}
}
class MyBody extends Component {
render() {
return <body>
<MyElement myFirstParam={1} mySecondParam={2} />
<MyElement myFirstParam={3} mySecondParam={5} />
<MyElement myFirstParam={7} mySecondParam={3} />
</body>
}
}
Of course I know that Rails process files on the server side, but I'd like just to show the idea of component nesting. I'd like to avoid imperative string concatenation in methods too. Is there some good way to do this?
Okay, I found the way. We can do this with render function. I created file named _my_element.erb which looks like this:
<div>
some text
<%= myFirstParam + mySecondParam %>
some another text
</div>
It's located in views folder, which seems to be important.
Next in my_body file I inserted:
<%= render partial: "my_element.erb",
locals: {myFirstParam: 1, mySecondParam: 2} %>
For some reason name of file should begins with _ char, while path in partial parameter should't include it.
But after all, that works.
You've given your own solution already, but let me add a cleaner apporach to what you might be after.
If you've followed the Rails pattern your MyBodyController#show-method sets #my_body and your MyBody has a has_many :my_elements line. You can now type:
<%= render #my_body.my_elements %>
You can now create a 'my_elements/_my_element.html.erb'-file (yep indeed with that underscore) that can contain the HTML for rendering the comment block (if you do this step wrong, Rails will explain you what to do). Since it is a set of records the code will repeated for all comments.
As a more general note: In Rails you typically pass objects (with values, but also methods), instead of just the limited set of separate values needed for rendering that element.
See also: http://guides.rubyonrails.org/layouts_and_rendering.html#passing-local-variables

Variable dustjs partial name

I am setting up some heavy split testing using dust.js on my single page app.
The base template looks like (simplified):
{>"layouts/master" /}
{<body}
<div id="container">
{?home}{>homeWelcome/}{/home}
</div>
{/body}
What I'm trying to do is have a folder containing N versions of the homeWelcome partial, and send N through the model to select the right template, like so :
{<body}
<div id="container">
{?home}{>/splits/homeWelcome_{partialNumber}/}{/home}
</div>
{/body}
But it (unsurprisingly) doesn't compile.
I could send params to the one homeWelcome template, and have all my splits in there but some are radically different from the others and it'd make for one hell of a long file.
In addition to that, I want to be able to add/remove partials in the splits directory dynamically (partialNumber is a rand from 1 to the number of files in the dir).
Any ideas how to achieve that?
Just add double quotes around the partial name and dust will happily parse the string before including a partial.
Note that partial names don't necessarily relate to folder structure, but I'm assuming you are compiling your templates with the appropriate names.

How to avoid embedding javascript in views/show

I have a Rails app that uses javascript (Backbone) to show user specific data on each users profile page /views/users/show.html.erb. I do this by passing <%= #user.id %> as a data parameter to Backbone's fetch function, however, the only way I know how to get the <%= #user.id %> into Backbone's fetch function is by embedding the javascript in each views/users/show.html.erb page, which therefore allows Backbone to load different user specific info for each views/users/show.html.erb page. Although this works, it seems like the wrong way to do it, as I have read that I should not embed javascript like this. Furthermore, I am going to have to do it a lot, because I wish to display a lot of different kinds of data, more than you see below. So the show.html.erb page will be filled with javascript to make the app work the way I wish.
Question: how might I get #user.id into Backbone's fetch function for each user's show page without embedding javascript in the way that I've done. In the comments, someone suggest I use app/assets/javascripts/user.js, but I don't know how to get <%= #user.id %> into that file. #user.id is readily available in show.html.erb
<script type='text/javascript'>
$(document).ready(function() {
app.collections.awardCollection.fetch({ data: $.param({ user_id: <%= #user.id %> }) }).complete(function(){
app.views.awardCollection = new app.Views.awardCollection({ collection : app.collections.awardCollection});
app.views.awardCollection.render()
});
});
</script>
In order to understand how the views works, is that you can add as many extensions to a view as you want, and they will be parsed by the right library.
If you create a view like
my_view.haml.erb
It will be first parsed with ruby (erb), and then with haml, and will end in a html page.
You can create many views for js, usually you want to archive that when you do ajax, so you can end having a js view like:
my_view.js.erb
First ruby will be parsed (all the <% %> tags), that will end as plain text, and then the server will serve the .js file. But that's usually a common task for ajax.
If you have to render a html page where you want to put some js and you need some ruby code on it, what I usually do is to put the data in the html content with a hidden div.
You can put in any view (even on your layout if you want it to be globally available), something like:
<div id="user_id" style="display: none;"><%= #user.id %></div>
And then on a js (or coffeescript or whatever) you can just check the content of that div:
<script type="text/javascript">
var user_id = $("#user_id").html();
</div>
that's really useful when you want to debug or create tests for your js files, since its plain js and won't throw syntax errors.
I see the comment of Luís Ramalho and Gon is a good option, but I recommend use the following approaches:
If the from the variable is not going to change, print it with <%= %> under .js.erb files located in app/assets/javascripts (note that it will be cached until you restart your app)
If you need server variables the best way is to use Ajax
You can define functions on .js files on app/assets/javascripts and call those functions from the views
If you really don't want any Javascript code in the view, you can create the functions on a .js on app/assets/javascripts (corresponding to the view, for order), and use events and/or store the variables in hidden fields (or even use the data attribute from HTML5)

Emberjs and jQuery UI integration questions

I am trying to understand how the integration between jQueryUI and Emberjs should be done. I am new to both libs and to javascript so this might be a newbie question.
I have this jsfiddle set up: http://jsfiddle.net/pSKgV/1/ and it renders this resulting document:
<body class="ember-application">
<div id="ember129" class="ember-view">
<div id="ember163" class="ember-view ui-draggable"></div>
</div>
</body>
The code is mostly taken from this blog post: http://www.lukemelia.com/blog/archives/2012/03/10/using-ember-js-with-jquery-ui/
Questions: How do I put something inside the inner div? I want to put some content that i can bind to something.
I have tried the following:
{{view App.Draggable}}Drag Me{{/view}} but that gives an error. I’ve also tried
adding this to the App.Draggable object:
didInsertElement: function() {
this.$().html(“Drag Me”)
}
but that did not give the expected results. How is the best way to use/access the jquery/jqueryui functions such as .html() in this situation?
Also, is the outer div necessary or can I make this view only render one div element?
regards
Oskar
http://jsfiddle.net/ud3323/XMgwV/
You forget the # symbol {{#view App.Draggable}}Drag me{{/view}}. You should also create namespaces in Ember using Ember.Namespace.create() instead of just using an empty {}

Resources