Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

My take on view_component_reflex with --sidecar nested. #24

Open
sebyx07 opened this issue Oct 14, 2020 · 2 comments
Open

My take on view_component_reflex with --sidecar nested. #24

sebyx07 opened this issue Oct 14, 2020 · 2 comments

Comments

@sebyx07
Copy link
Contributor

sebyx07 commented Oct 14, 2020

This is how I use view_component_reflex with the --sidecar layout from view_component.

first the dir structure

screen21

Before putting the .js & .scss you must change the application.js to load controllers properly. Here is mine:

import { Application } from "stimulus";
require("@rails/ujs").start();
require("turbolinks").start();
require("@rails/activestorage").start();
require("channels");
import StimulusReflex from 'stimulus_reflex';
import consumer from '../channels/consumer';
import controller from '../controllers/application_controller';

const application = Application.start()
const context = require.context("../../components", true, /_controller\.js$/)
function getDefinitionsFromContext(ctx){
  return ctx.keys().map((path) => {
    const splitPath = path.split("/");
    const identifier = splitPath.slice(1, splitPath.length - 1).join("/").replace("_component", "");
    const obj = ctx(path);

    const klassKey = Object.keys(obj)[0];
    const controllerConstructor = obj[klassKey];

    return {
      identifier,
      controllerConstructor
    };
  });
}

application.load(getDefinitionsFromContext(context));
StimulusReflex.initialize(application, { consumer, controller, debug: false });

My application_controller.js, which is pretty standard

import { Controller } from 'stimulus'
import StimulusReflex from 'stimulus_reflex'

export default class ApplicationController extends Controller {
  connect () {
    StimulusReflex.register(this)
  }

  beforeReflex (element, reflex, noop, reflexId) {
  }

  reflexSuccess (element, reflex, noop, reflexId) {
  }

  reflexError (element, reflex, error, reflexId) {
  }

  afterReflex (element, reflex, noop, reflexId) {
  }
}

I'll skip example_component.rb, example_component.html.erb because they are standard

Here is the example_component_controller.js, which also links the .scss file

import "./example_component.scss"
import ApplicationController from "../../../javascript/controllers/application_controller";

import "./example_component.scss"
import ApplicationController from "../../../javascript/controllers/application_controller";

export class ExampleComponentController extends ApplicationController {
  connect() {
    super.connect();
    console.log('ok');
  }
}

finally the .scss which is scoped to this component, nothing fancy, but usefull

div[data-controller="my/example"]{
  background-color: red;
}

Would be cool if someone will write a generator for this so we could just

rails g component my_awesome/nested_component

@sebyx07
Copy link
Contributor Author

sebyx07 commented Oct 14, 2020

I'll leave application_component.rb

class ApplicationComponent < ViewComponentReflex::Component
end

& the example_component.rb just in case

module My
  class ExampleComponent < ApplicationComponent
    attr_accessor :count

    def initialize
      @count = 1
    end

    def increment
      self.count += 1
    end
  end
end

& the html

<%= component_controller do %>
  <p><%= count %></p>
  <%= reflex_tag :increment, :button, "Click" %>
<% end %>

@olhor
Copy link

olhor commented Mar 16, 2021

Following @sebyx07 approach I modified stimulus controller naming resolvers to match following "sidecar" folder structure.
Assuming we have a parent_component with nested first_child_component I wanted to have:

app/components/
├- parent_component.rb
└- parent_component/
    ├- parent_controller.js
    ├- parent_component.html.erb
    ├- first_child_component.rb
    └- first_child_component/
        ├- first_child_controller.js
        └- first_child_component.html.erb 

I had to modify getDefinitionsFromContext function in order to remove all _component parts from filenames and to follow stimulus naming conventions ("--" for namespace separation and "-" for word separation):

const cvContext = require.context("../../components", true, /_controller\.js$/)

function getDefinitionsFromContext(ctx){
  return ctx.keys().map((path) => {
    const splitPath = path.split("/");
    const identifier = splitPath.slice(1, splitPath.length - 1).join("--").replace(/_component/g, "").replace('_', '-');
    const obj = ctx(path);
    const klassKey = Object.keys(obj)[0];
    const controllerConstructor = obj[klassKey];

    return {
      identifier,
      controllerConstructor
    };
  });
}

application.load(getDefinitionsFromContext(cvContext))

and override self.stimulus_controller method from ViewComponentReflex::Component module to:

ViewComponentReflex::Component.module_eval do
  def self.stimulus_controller
    name.gsub('Component', '').underscore.dasherize.gsub("/", "--")
  end
end

This approach allowed component_controller helper method to generate following controllers:

...
<div data-controller="parent" key="123"></div>
...
<div data-controller="parent--first-child" key="456"></div>
...

and to be properly initialized by Stimulus framework.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants