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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Targets creation #41

Closed
savroff opened this issue Jan 5, 2018 · 13 comments
Closed

New Targets creation #41

savroff opened this issue Jan 5, 2018 · 13 comments

Comments

@savroff
Copy link

savroff commented Jan 5, 2018

This time I will try to act smarter and ask before write code 馃槀

Sometimes I need to create a DOM elements, that Stimulus controller should controller after.
For example in this interface, when I create a new item, I need a way to add it to DOM.
vr9svsvjo7

Right now I need to write this

    // create tag element
    const item = document.createElement('div')
    item.setAttribute('data-title', 'aa')
    item.setAttribute('data-id', 123)
    item.setAttribute('data-target', 'categories.selected_item')
    item.setAttribute('data-action', 'click->categories#removeItem')
    item.setAttribute('class', 'categories--selected-item')
    item.innerHTML = 'aa'

    this.targets.find('selected_items').appendChild(item)

Can we present some sort of function to simply create new dom elements inside. Like:

// element can be a 'div' by default
const target = this.targets.create('selected_item', { 
  element: 'div', 
  data: {}, 
  htmlOptions: { class: 'categories--selected-item' }, 
  innerHTML: '' 
})
this.targets.find('selected_items').appendChild(target)

Probably we can just create a simple standard way to adding mix-ins to controllers.
This case we can keep things like that outside of Stimulus

@mdesantis
Copy link

mdesantis commented Jan 6, 2018

What about using template tag? https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template

@savroff savroff closed this as completed Jan 6, 2018
@savroff
Copy link
Author

savroff commented Jan 6, 2018

@mdesantis thank you! 馃帀

@javan
Copy link
Contributor

javan commented Jan 6, 2018

You can access a controller's own identifier with this.identifier. So you can avoid hard coding and repeating it by doing:

item.setAttribute('data-target', `${this.identifier}.selected_item`)
item.setAttribute('data-action', `click->${this.identifier}#removeItem`)

Instead of:

 item.setAttribute('data-target', 'categories.selected_item')
 item.setAttribute('data-action', 'click->categories#removeItem')

@savroff
Copy link
Author

savroff commented Jan 6, 2018

@javan what I actually did.
I created own Conrollerclass with wrapping original Stimulus one and added functions there.

Can we think of a simple way to do a mix-in? Like for example in Vue

@savroff
Copy link
Author

savroff commented Jan 6, 2018

@mdesantis one problem with templates, seems like they not supported in IE 馃挬
But a lot of our users still use last version of IE

@mdesantis
Copy link

Yes, but there are many working polyfills in the wild :-)

@mdesantis
Copy link

mdesantis commented Jan 6, 2018

Anyway, I feel the needing of managing potentially existing HTML components, in a library like this one. Something that solves the requirement "How to display a new incoming message on my instant messaging app?"

@savroff
Copy link
Author

savroff commented Jan 6, 2018

Agree, this can be placed in examples and in the handbook. Seems like I closed this issue too early :)

@mdesantis idea with templates is really nice, this case you can keep HTML related things in Rails view, but we need to include polyfill in a framework (could increase library size a little).

Or we can create new elements and insert them like I showed, plus wrap it in nicer helper. But I see a downside here, that new element markup fully created in the .js file.

Or introduce data-template.
@javan what you think about all these?

@savroff savroff reopened this Jan 6, 2018
@rainerborene
Copy link

rainerborene commented Jan 7, 2018

Another alternative would be to provide facilities for cloning existing elements, so you can change any attribute you want before appending to the DOM. That way you don't have to deal with template strings (or rendering engine?!) which I'm not in favor of adding an unnecessary layer of complexity. I was thinking of data-bind-* attributes which allows you to expose an interface to your JS instead.

<ul class="tasks">
  <li data-controller="task_item" data-bind-text="task_item.name">Todo item</li>
</ul>

Or you could define this interface in your controller to avoid duplicated code if you're not using <template>:

interface: {
  name: '@text' // xpath-like expressions
}

Then somewhere in your JS you would do something like this:

this.clone().feed({ name: "New todo item" }).appendTo(this.element.parentNode)

Just my 2 cents.

+1 for <template> and pollyfills.

@javan
Copy link
Contributor

javan commented Jan 8, 2018

Remember:

Stimulus is a JavaScript framework with modest ambitions. It doesn't seek to take over your entire front-end鈥攊n fact, it's not concerned with rendering HTML at all.

We don't plan to add our own way to do rendering, and there are a number of great options for you to chose from. I'd start small with document.createElement() or element.cloneNode(), and then reach for a templating library if needed. I'm a fan of hyperHTML and good ol' EJS.

@javan javan closed this as completed Jan 8, 2018
@pacMakaveli
Copy link

@savroff on top of using template, you can also combine the template functionality with lodash's template and create this:

javascript: https://github.com/games-directory/games.directory/blob/v2.0-beta1/app/javascript/packs/controllers/identity_verification_process_controller.js

view: https://github.com/games-directory/games.directory/blob/v2.0-beta1/app/views/users/form/verification/_search.erb

The only downside is, at least for me, that you have to use .erb.

TL;DR

You can create a simple HTML template enclosed by a script tag which gets ignored when viewing the page because of type='text/template'

<div id='responseBody' class='row align-top'></div>

<script type='text/template' id='responseBodyTemplate'>

  <div class='cell-sm-12 -margin-top_2'>
    
    {{ _.forEach(identities, (identity) => { }}

      <h3 class='-line-reset'>{{= identity.name }}</h3>
      <p class='-line-reset -margin-top_4'>
        About: 
        <span class='-weight-regular'>{{= identity.about }}</span>
      </p>

    {{ }) }}

  </div>  <!-- cell-sm-12 -->

</script>
// Find the Template
const container = document.getElementById('responseBodyTemplate');

// Tell lodash to grab the contents of the Template
const template  = _.template(container.innerHTML);

// Magic happens
// 'parse' the template and pass the required variables
document.getElementById('responseBody').innerHTML = template({
  identities: [{ name: 'Foo', about: 'Bar' }]
});

@savroff
Copy link
Author

savroff commented Jan 11, 2018

@pacMakaveli thanks!
I personally like keep markup outside from js files. 馃槏 We similar approach to this one and keep HTML in erb sounds really reasanoble in my case.

@rainerborene
Copy link

Just for posterity I have found two libraries that does exactly what I was looking for: bind.js and simulacra. One of the authors published an article explaining how it works: Tiny two way data binding. It would be very simple to integrate with a stimulus controller and template tags.

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

No branches or pull requests

5 participants