5

I have a jQuery heavy app I'm in the middle of that has many jQuery plugins and I want to restructure the app, so I'm looking at different frameworks like Angular, React, Riot 2.0, etc.

I like React and Riot, but I can't figure out how I'd make your typical jQuery app (unmetered access to the DOM) into this new world of virtual DOM, etc.

Some of these components are complex and I wouldn't want to rewrite them all into the "VDOM way" and create another nightmare.

Does anyone have an answer to this? Maybe jQuery plugin heavy apps aren't suited, or is there a way to split/combine the UI using both, or maybe a React/Riot like framework that would play well with jQuery?

Timmerz
  • 6,090
  • 5
  • 36
  • 49

5 Answers5

14

To access the DOM with jQuery inside a Riot 2.0 custom tag, you can use the mount event and this.root like this:

<my-custom-tag>
  <h3>Riot.JS Custom tag + jQuery plugin</h3>
  <p>My paragraph</p>
  <script>
    this.on('mount', function() {
      var $node = $(this.root);
      $node.find('p').html('updated by jQuery!');
    });
  </script>
</my-custom-tag>

I am not sure it is the "best practice" but it works with Riot 2.0.10 (Feb 19, 2015).

If the custom tag contains form or input elements, it's simpler, you can access them by their name property, you don't need the mount event:

<my-custom-form>
  <input name="email" type="text">
  <script>
    $(this.email).val('mike@worldcompany.com');
  </script>
</my-custom-form>
Michael Rambeau
  • 579
  • 4
  • 8
  • Thanks for your answer. Does this mean that $ o jQuery have to be available in the global scope? I'd rather pass it as an opt to the tag – ffflabs Dec 28 '15 at 18:32
  • 1
    Hello, yes, at the time of my answer, I was dealing with a legacy application where jQuery was in the global scope, "as usual". So my goal was to build a dynamic page using custom tags from Riot and some existing jQuery plugins. If you are building something more modern using modules, you might not need jQuery (see http://youmightnotneedjquery.com/ for example). Otherwise, I think you can pass $ as an "opt" from the parent as you said, I guess it will work even if I have never used such code. – Michael Rambeau Jan 02 '16 at 08:14
5

Riot 2.0 was released just 4 days ago so there are obviously no extensions yet.

But it's a good candidate to transform jQuery- based apps to the "modern world" with using custom tags or "components" in React community.

Custom tags lets you build reusable pieces of client side code with a less need for jQuery selectors and manipulation methods. And HTML and JS is less scattered on the file system.

And Riot 2.0 is designed to play well together with jQuery. You can freely use jQuery plugins together with custom tags so you can transform your app iteratively - one tag at the time if you like.

3

This is an excellent guide to wrapping DOM libs with React components:

https://github.com/rpflorence/react-training/blob/gh-pages/lessons/05-wrapping-dom-libs.md

Jonny Buchanan
  • 61,926
  • 17
  • 143
  • 150
2

In angular someone has probably already recreated the thing you want. You use directives to either implement reusable components or wrap existing ones. To wrap a plugin:

  • initialize in link (based on isolate scope)
  • use scope.$watch(key, fn) to update the plugin when something changes
  • use scope.$apply() in plugin callbacks to tell angular something might have changed, update any relevant two way bindings, and invoke any relevant expression bindings
  • use scope.$on('$destroy', fn) to clean up
  • See ui-bootstrap for examples and api design.

The same applies to React, but components instead of directives.

  • initialize in componentDidMount (based on props)
  • update the plugin in componentDidUpdate (based on props)
  • call this.props.onWhatever in plugin callbacks
  • clean up in componentWillUnmount
  • See react-bootstrap for examples and api design.

And I don't think Riot is relevant here.

Brigand
  • 84,529
  • 20
  • 165
  • 173
  • 1
    why is riot not relevant? – Timmerz Jan 24 '15 at 21:38
  • 1
    As far as I see there's no conventions or examples of riot.js being used to do much of anything, I can't even find a datepicker or similar... if at some point it becomes serious, ping me here, and I'll update my answer. I'd avoid it for now. – Brigand Jan 24 '15 at 23:01
  • 1
    Check https://github.com/riot/riot#components - some components (including datepicker) have already been created. – gius Aug 19 '15 at 15:38
  • 5
    Downvoted for asserting that Riot is irrelevant. This was part of the question. (Also, the developers' single product is based on Riot.) – Daniel Oct 19 '15 at 16:29
  • I initially downvoted and then decided to propose an edit to update the Riot.JS mention with the simple approaches described in other answers below. I went this route because this answer is already marked as the Accepted answer. We might as well have it correct rather than expect people to do further sleuthing. – Jasmine Hegman May 31 '16 at 19:36
  • @JasmineHegman what happened to this edit, did it get rejected? – Marco Nov 11 '19 at 14:07
  • That's a great question! I don't know :( Is there a way to see History of proposed edits? It's been a while but I will re-edit it the same way later either way, but I have to go to work now. Thanks for catching this, I just assumed it went through, bad on my part. – Jasmine Hegman Nov 11 '19 at 16:01
1

We had the same problem, i.e. turn a pretty big global jquery based admin frontend into nestable conflict free components - w.o much training effort for our partners - so riotjs was just ideal for us.

We agreed on the solution below (see also Michael Rambeau's answer) and are up to now quite content with that.

Via a mixin we give all our riot component tags this function:

var ComponentTagsMixin = {
  init: function () {
     //<other stuff needed in all tags>

     // jquery local to the vDom of this instance:
     $: function(selector) {
            // you could fail here if (!this.isMounted)                                        
            return $(this.root.querySelector(selector))           
     }          
}

```

and so, within the tags you simply turn $ into this.$. Example, jquery nestable:

$('#nestable').nestable(....)
$('.dd').nestable('expandAll');

into

this.$('#nestable').nestable(....)
this.$('.dd').nestable('expandAll');

and now this allows to have many of those components within the same page w/o conflict and local namespaces and data.

As already pointed out, this is only accessible after the tag is mounted, so run your stuff within an this.on('mount'...) block.

This works remarkably well with legacy jquery libs since they typically allow the user to redefine their Dom selectors for double run-ability within a page anyway.

Red Pill
  • 511
  • 6
  • 15