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

Watch rails views and reload page on changes #1879

Closed
mbajur opened this issue Jan 9, 2019 · 17 comments
Closed

Watch rails views and reload page on changes #1879

mbajur opened this issue Jan 9, 2019 · 17 comments

Comments

@mbajur
Copy link

mbajur commented Jan 9, 2019

I'm currently using webpacker with page reloading on css/js changes and it all works just great. Thanks for this amazing piece of software. I was wondering - would that be possible to do the same for rails views? It would be great if updating erb or slim view could trigger a webpack page refresh (ideally without rebuilding entire js/css bundle).

I'm fully aware that's not the point of using webpack(er) and know there are solutions like guard and livereload for that but i would love to avoid using yet another deamon running in the background.

Thanks in advance for any clues!

@jakeNiemiec
Copy link
Member

jakeNiemiec commented Jan 9, 2019

From what I can tell the stock (webpack) devServer only pays attention to files that are resolved by webpack.

I use the webpack BrowserSyncPlugin & files: ['./app/**/{*.erb,*.rb}'] to achieve this, but you might need to use http-proxy-middleware in order to re-proxy the existing webpackER devServer if you want to go that route. It is very dependant on your setup, best of luck!

@scottatron
Copy link

Not quite as nice as using webpack itself, but I've got this working by using watchexec.

I run up webpack-dev-server, then run:

watchexec -w app/views touch app/javascript/packs/application.js

@mtomov
Copy link

mtomov commented Oct 22, 2019

There's an even easier way, just set the devServer contentBase property like this in config/webpack/development.js

environment.config.devServer.watchContentBase = true
environment.config.devServer.contentBase = [
  path.join(__dirname, '../../app/views')
]

@swrobel
Copy link
Contributor

swrobel commented Nov 26, 2019

Here's an example that won't cause webpack to do a full recompile. This will simply trigger a browser reload. Your specific watches may vary, but they're pretty easy to configure using an array of glob syntax w/ chokidar (which is already included by webpack) - just note that it doesn't accept regexes:

development.js:

const chokidar = require('chokidar')
environment.config.devServer.before = (app, server) => {
  chokidar.watch([
    'config/locales/*.yml',
    'app/views/**/*.slim'
  ]).on('change', () => server.sockWrite(server.sockets, 'content-changed'))
}

@jesster2k10
Copy link

Here's an example that won't cause webpack to do a full recompile. This will simply trigger a browser reload. Your specific watches may vary, but they're pretty easy to configure using an array of glob syntax w/ chokidar (which is already included by webpack) - just note that it doesn't accept regexes:

development.js:

const chokidar = require('chokidar')
environment.config.devServer.before = (app, server) => {
  chokidar.watch([
    'config/locales/*.yml',
    'app/views/**/*.slim'
  ]).on('change', () => server.sockWrite(server.sockets, 'content-changed'))
}

Hey, I can't seem to get this working. The "chokidar" isn't watching any changes in *.html.erb files

@swrobel
Copy link
Contributor

swrobel commented May 11, 2020

Here's an example that won't cause webpack to do a full recompile. This will simply trigger a browser reload. Your specific watches may vary, but they're pretty easy to configure using an array of glob syntax w/ chokidar (which is already included by webpack) - just note that it doesn't accept regexes:
development.js:

const chokidar = require('chokidar')
environment.config.devServer.before = (app, server) => {
  chokidar.watch([
    'config/locales/*.yml',
    'app/views/**/*.erb'
  ]).on('change', () => server.sockWrite(server.sockets, 'content-changed'))
}

Hey, I can't seem to get this working. The "chokidar" isn't watching any changes in *.html.erb files

Yes, my glob was for slim templates, which are what I use. I've modified the code above to look for erb files (though anyone can & should modify these globs for their needs)

@jesster2k10
Copy link

Here's an example that won't cause webpack to do a full recompile. This will simply trigger a browser reload. Your specific watches may vary, but they're pretty easy to configure using an array of glob syntax w/ chokidar (which is already included by webpack) - just note that it doesn't accept regexes:
development.js:

const chokidar = require('chokidar')
environment.config.devServer.before = (app, server) => {
  chokidar.watch([
    'config/locales/*.yml',
    'app/views/**/*.erb'
  ]).on('change', () => server.sockWrite(server.sockets, 'content-changed'))
}

Hey, I can't seem to get this working. The "chokidar" isn't watching any changes in *.html.erb files

Yes, my glob was for slim templates, which are what I use. I've modified the code above to look for erb files (though anyone can & should modify these globs for their needs)

Thank you for that. I'm not sure what the issue was but after closing my terminal and restarting my server, it worked like a charm!

@cabgfx
Copy link

cabgfx commented Jun 2, 2020

thanks, @swrobel that worked great!

Can confirm this works on Rails 6.0.3.1, webpacker 4.2.2 👍

My config/webpack/development.js:

process.env.NODE_ENV = process.env.NODE_ENV || 'development'

const environment = require('./environment')
const chokidar = require('chokidar')

environment.config.devServer.before = (app, server) => {
  chokidar.watch([
    'config/locales/*.yml',
    'app/views/**/*.erb'
  ]).on('change', () => server.sockWrite(server.sockets, 'content-changed'))
}

module.exports = environment.toWebpackConfig()

@guillaumebriday
Copy link
Member

Thanks @cabgfx

@henrypoydar
Copy link

Using merge for this in @rails/webpacker 6.0.0.beta-5 config/webpack/development.js:

process.env.NODE_ENV = process.env.NODE_ENV || "development";

const webpackConfig = require("./base");
const { merge } = require("@rails/webpacker");
const chokidar = require("chokidar");

// Hot reload views via webpack dev server. https://github.com/rails/webpacker/issues/1879.
// However, CI system tests may choke on this since NODE_ENV is  'development' in tests. 
// So check for RAILS_ENV and skip hot reload in testing.
module.exports = process.env.RAILS_ENV == "development" ? merge(webpackConfig, {
  devServer: {
    before: (app, server) => {
      chokidar
      .watch([
        "config/locales/*.yml",
        "app/views/**/*.md",
        "app/views/**/*.html.erb",
        "app/views/**/*.html.haml",
      ])
      .on("change", () => server.sockWrite(server.sockets, "content-changed"));
    }
  },
}) : webpackConfig;


@travisp
Copy link

travisp commented Jun 3, 2021

For what it's worth, I had to add the option "awaitWriteFinish" or else the reload did not always successfully show the changed file (and I had to either manually refresh, or hit save a second time). The relevant section looks like this on webpacker 6:

before: (app, server) => {
      chokidar
        .watch([
          "config/locales/*.yml",
          "app/views/**/*.html.erb",
          "app/views/**/*.html.haml",
        ], {
          awaitWriteFinish: true
        })
        .on("change", () => server.sockWrite(server.sockets, "content-changed"));
    }
  }

@mabras
Copy link

mabras commented Jun 17, 2021

For what it's worth, I had to add the option "awaitWriteFinish" or else the reload did not always successfully show the changed file (and I had to either manually refresh, or hit save a second time). The relevant section looks like this on webpacker 6:

Make sure of hmr and inline value set to true.

// config/webpacker.yml
  dev_server:
    ...
    hmr: true
    inline: true
    ...

@swrobel
Copy link
Contributor

swrobel commented Jun 17, 2021

Make sure of hmr and inline value set to true.

inline does need to be set to true, but hmr is not required. I have it turned off as mine isn't a SPA.

@chmich
Copy link

chmich commented Jun 25, 2021

THANKS all! much smoother work with that settings.

Question:
If a page is broken there appears the rails-error-page.
In my example with better_errors: https://github.com/BetterErrors/better_errors
After repairing the html i have to click reload once and then the machine runs again and is reloading after all changes.
The cherry on the cake would be that the browser would then also refresh the page, when the error is fixed.

Does anyone know if that could be possible?

@joshsaintjacque
Copy link

THANKS all! much smoother work with that settings.

Question:
If a page is broken there appears the rails-error-page.
In my example with better_errors: https://github.com/BetterErrors/better_errors
After repairing the html i have to click reload once and then the machine runs again and is reloading after all changes.
The cherry on the cake would be that the browser would then also refresh the page, when the error is fixed.

Does anyone know if that could be possible?

Adding awaitWriteFinish: true to my development.js change listener solved this issue for me.

@aried3r
Copy link
Contributor

aried3r commented Oct 19, 2021

before has been renamed to onBeforeSetupMiddleware in webpack-dev-server v4. See the migration guide: https://github.com/webpack/webpack-dev-server/blob/master/migration-v4.md#-breaking-changes.

devServer.socketWrite has been replaced by .sendMessage() but an additional change seems to be necessary, see webpack/webpack-dev-server#1271 (comment).

Setting inline to true does not seem to be necessary anymore, as it's the default now, I believe. But doesn't hurt to set it explicitly. See #3031.

This is what we're currently using with Rails 6.1, webpacker 6.0.0-rc.5, webpacker 5, webpack-dev-server 4:

// config/webpack/development.js

module.exports =
  process.env.RAILS_ENV == "development"
    ? merge(webpackConfig, {
        devServer: {
          onBeforeSetupMiddleware: (devServer) => {
            chokidar
              .watch([
                "config/locales/*.yml",
                "app/views/**/*.html.erb",
                "app/assets/**/*.scss",
              ])
              .on("change", () =>
                devServer.sendMessage(
                  devServer.webSocketServer.clients,
                  "content-changed"
                )
              )
          },
        },
      })
    : webpackConfig

Adding awaitWriteFinish: true to my development.js change listener solved this issue for me.

This did not work for us. If somebody can get this to work reliably with better_errors it'd be highly appreciated!

@kylebolton
Copy link

kylebolton commented Jan 19, 2022

For anyone having CI issues/packs-tests issues, I had to wrap the above code in a condition so RSpec didn't complain.

process.env.NODE_ENV = process.env.NODE_ENV || 'development'

const environment = require('./environment')
const chokidar = require('chokidar')

if (process.env.RAILS_ENV === 'development') {
    environment.config.devServer.before = (app, server) => {
        chokidar.watch([
            'config/locales/*.yml',
            'app/views/**/*.erb'
        ],
        ).on('change', () => server.sockWrite(server.sockets, 'content-changed'))
    }
}

module.exports = environment.toWebpackConfig()

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