Posts tagged code
– page 4

Using the will_paginate gem to generate a React renderable HTML string

Generally when I post about programming it’s because I’ve finally found a solution to something that has been driving me crazy, some problem with a lot of documentation to read through and not a lot of Stack Overflow answers (and if they do exists, the use case is always a little off). This is no exception!

First, why would you want to combine React and Ruby gems? In my case, I thought it was the simple, lazy path. Way back when, the site I work for was built using will_paginate for all pagination. I recently ran into a case where I needed to include that pagination in a React app. It wasn’t for anything fancy, the links and logic needed to match the rest of site. So, trying to re-create the gem magic in React seemed like overkill compared to creating a partial with the code, generating an HTML string using the Rails render_to_string method, and sending it to React to dangerouslySetInnerHTML.

Well, it turns out will_paginate uses the url_for method and it was not happy to be called without the request data (undefined method `host’ for nil:NilClass?!) it uses to build urls. After a lot of hunting around, I came across this pull request to get will_paginate working with mountable engines that seemed adaptable. My helper accepts the request from the controller and pulls out the necessary fields to send with it’s own instance of url_for

module WillPaginateHelper
  class StringLinkRenderer < WillPaginate::ActionView::LinkRenderer
    protected

    def url(page)
      @base_url_params ||= begin
        url_params = merge_get_params(default_url_params)
        url_params[:only_path] = true
        merge_optional_params(url_params)
      end

      url_params = @base_url_params.dup
      add_current_page_param(url_params, page)

      if @options[:url_builder]
        @options[:url_builder].call(url_params)
      else
        @template.url_for(url_params)
      end
    end
  end

  def will_paginate_string(collection = nil, options = {}, request = nil)
    params = {
      :host => request[:host],
      :controller => request[:controller],
      :action => request[:action]
    }

    unless request.query_parameters.empty?
      request.query_parameters.each do |k, v|
        params[k] = v
      end
    end

    options.merge!(
      :renderer => WillPaginateHelper::StringLinkRenderer,
      :url_builder => ->(params) { Rails.application.routes.url_for(params) },
      :params => params
    )

    will_paginate(collection, options)
  end
end

(GitHub Gist)

Then, in the partial that is being rendered to a string, it’s possible to call will_paginate_string(collection, options, request) and the results can be sent as a JSON response to render in React.

Admittedly, Rails isn’t my strong suit so I’d be interested to hear if I missed some easier way to do this.

Asset Manifests with Swig Templates

I’m still using the Swig templating engine for my Node/Express apps and recently decided to streamline my asset caching process. I wanted a simple way to insert hashed assets into my templates but everything that Google came up with seemed a little too complicated. So I wrote a quick custom Swig tag to read from an asset manifest (in my case public/assets.json generated by the Webpack Manifest Plugin):

let assets = require('../../public/assets.json');

module.exports = function (swig) {
  swig.setTag('asset', _parse, _compile, false, true);
};

let _parse = function (str, line, parser) {
  parser.on('*', function (token) {
    let match = token.match.match(/^["'](.*?)["']$/);
    let assetPath = match ? match[1] : null;

    if (assetPath) {
      let asset = assets[assetPath] || assetPath;
      this.out.push(asset);
    }
  });

  return true;
};

let _compile = function (compiler, args) {
  if (!args || !args[0]) {
    throw new Error('The asset tag expects a filename as a string');
  }

  let assetPath = args[0].startsWith('/') ? args[0] : '/' + args[0];
  return `_output +="${assetPath}";`;
};

(GitHub Gist)

I keep this file in an app/helpers directory and in app.js my views are set up with the lines:

require('./app/helpers/assets')(swig);
app.engine('html', swig.renderFile);
app.set('view engine', 'html');

After that stylesheets or JavaScript can be added to any template using the unhashed name like so:

<script src="{% asset 'main.js' %}"></script>
<link rel="stylesheet" href="{% asset 'main.css' %}" />

Using Bookmarklets for Basic Chrome for iOS Debugging

So I was trying to figure out a design bug that could only be recreated in the Chrome browser on an iPhone and it was kind of a hassle. Safari on iOS and the Chrome DevTools emulator looked fine so they were no help. Xcode’s simulator and remote debugging only work with Safari so they were out too. I know there are other resources you can either pay for or take the time to install that might get the job done but I really only needed to test a couple lines of CSS.

I remembered from years back that Firebug Lite could run on any browser or mobile device but it’s actually a little heavy and hard to use on a small screen. But — looking it up — I had the epiphany that if you know JavaScript there’s no reason you can’t just make your own bookmarklet for debugging. This might be totally obvious but it took a while to occur to me so there you go.

If you want to do a really quick styling update, all you need is something like this (note: this is not the issue I was trying to fix):

javascript:(function () {
    var el = document.querySelector('body');
    el.style.color = '#fff';
    el.style.backgroundColor = '#000';
})();

Or, if you want to insert an external file:

javascript:(function () {
    var src = 'path/to/styles.css';
    var link = document.createElement('link');
    link.rel = 'stylesheet';
    link.href = src;
    document.head.appendChild(link);
})();

Then you just open Chrome and create a dummy bookmark, select edit, and replace the URL field with your JavaScript. You can also rename the bookmark to something more accurate if you want.

For more information, TutsPlus has a useful tutorial on creating bookmarklets.

Blind-style Nav Transition

I’ve been playing around with new animations for fullscreen navigation menus and thought I’d share. The Pen below is a sort of window blind/disappearing curtain swipe transition.

See the Pen Nav Animation Experiment by Lauren Ashpole (@laurenashpole) on CodePen.

Advertisement
Advertisement

Diagonal Hover Effects with Skew

Just finished up a quick Pen to try out some diagonal swipe/background hover animations using transform: skew.

I have an example page here and you can check out the code below. All of the background images are courtesy of Unsplash It.

See the Pen Diagonal Hover Effects by Lauren Ashpole (@laurenashpole) on CodePen.

AMP forms with Node.js

UPDATE 11/14/19: At some point in the years since I originally posted this, Google made some improvements to the way AMP handles URLs and I’ve updated my headers to keep up. Most of the information below is still applicable but I’ve updated the Access-Control-Allow-Origin and AMP-Access-Control-Allow-Source-Origin settings to:

res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
res.setHeader('AMP-Access-Control-Allow-Source-Origin', req.query.__amp_source_origin);

So keep that in mind while reading the original post…


I’m in the middle of creating Google AMP pages for my fonts and everything was going pretty well until I tried getting the amp-form component working with Node.js. I followed the AMP by Example code, my page passed validation, but whenever I tried to use the form data all I got was an empty req.body.

Finding a solution took more effort than the usual reading through the first few Stack Overflow results, so I thought it might be useful for anyone else out there struggling.

Continue Reading

Positioning Rotated Text

Using transform: rotate() for a sideways text effect is pretty simple CSS but if you’re anything like me, you forget the details for positioning that text and have to play around with translates and transform-origins in the Inspector every time.

So I made myself a cheatsheet for positioning rotated text and thought I’d share it. You can align text to the top, center, or bottom of its parent container and change the direction of the text up or down.

Check it out here.

Animated Drawer with Fixed, Variable Height Header

This is a fairly common request in my office: slide in animated drawers with fixed headers and scrollable bodies. Also, the content in the headers is constantly changing so both the body and header heights need to adapt.

Before flexbox, this was kind of a nightmare. Absolute positioning doesn’t play particularly nice with transforms. Multiple classes to set multiple heights seems like overkill. So does using JavaScript just to get this layout.

But with flexbox, it can be done in basically four lines (eight with prefixes). Use a flexbox column display for the drawer itself:

.drawer {
    display: -webkit-box;
    display: flex;
    -webkit-box-orient: vertical;
    -webkit-box-direction: normal;
    flex-direction: column;
}

And set the drawer body to grow and scroll:

.drawer-body {
    -webkit-box-flex: 2;
    flex-grow: 2;
    overflow-y: auto;
}

That’s all it takes. Here’s a Pen that also includes the code for animating on and off the page.

See the Pen Animated Drawer with Fixed, Variable Height Header by Lauren Ashpole (@laurenashpole) on CodePen.