Webpacker: Has anyone look at using vue router with rails router?

Created on 10 Jul 2017  路  15Comments  路  Source: rails/webpacker

This is a question/discussion. I think rails/webpacker does not support routing beyond rails view.

Has anyone look at integrating vue-router with rails' router? ie when rails cannot find a route, it may fall back to vue-router before showing routing error.

Is there any projects worth looking into? If there are other frameworks (like react) that has already integrate with rails' router, i would appreciate if you can share it.

Any opinions are welcome. Thanks in advance!

Most helpful comment

I have the following:

# application_controller.rb
def index
  render inline: '', layout: 'application' # Avoid having an empty view file.
end

# routes.rb
# Catch all for HTML 5 history routing. This must be the last route.
get '/*path', to: 'application#index', format: false

I'm only using Rails resource routes for internal APIs; in other words, I only have an application.html.slim layout, and no other views. The layout loads my Vue.js app, which uses vue-router.

All 15 comments

@ytbryan No, haven't tried vue router but integration should be straightforward as long as you have a rails router and client router for all routes. Have you seen this - https://github.com/gauravtiwari/webpacker-react-frontend?

@ytbryan ignore that example. I will push another one with React router - seems like I removed it or something

I have the following:

# application_controller.rb
def index
  render inline: '', layout: 'application' # Avoid having an empty view file.
end

# routes.rb
# Catch all for HTML 5 history routing. This must be the last route.
get '/*path', to: 'application#index', format: false

I'm only using Rails resource routes for internal APIs; in other words, I only have an application.html.slim layout, and no other views. The layout loads my Vue.js app, which uses vue-router.

I'm having some problems with this setup as well, my routes look like this:

  # Route to hit the landing page
  root to: "home#home", format: false

  get "signup", to: "home#signup"

  get "signin", to: "home#signin"

  # Route to hit the Vue app
  scope :app do
    get "/*path", to: "app#app", format: false
  end

Implementing @jpickwell's solution fixes routing in my Vue app rendered by /app, but now when refreshing the page while at the root route of the app at /app, I get a Rails error - no route matches.

Navigating to http://localhost:5000/app does not work
Navigating to http://localhost:5000/app/some_other_route does work

Does anyone have any insight?

This solved my problems:

  # Route to hit the Vue app
  scope :app do
    get "/", to: "app#app", format: false

    get "/*path", to: "app#app", format: false
  end

Still unsure if this is a viable solution, however.

@matisoffn, I think for your setup, that's probably the best solution. If your app structure can handle it, you could unscope the Vue app, and just make sure all vue-router routes don't conflict with the Rails routes. Since the catch-all is last, any Rails routes before it will be used before vue-router routes.

@ytbryan Happy with the comments? Feel free to close and share your thoughts 馃憤

Without reproducing all the Vue routes in the Rails router, won't this result in every refresh dumping the user back at the root route, no matter what client-side-routed URL they're on? This is what's happening to me and I'm attempting to solve it right now...

@vcavallo, yes, but refreshing/reloading the page makes a new request to the server. There's nothing you can do about this because that's how the browser works. vue-router is meant for SPAs where you're not performing refresh/reload for each Vue route. If you were, then you'd have an MPA, and vue-router just adds unnecessary bloat to your code.

@jpickwell thanks, yea. makes sense.
got any advice for a hybrid-ish SPA where you want plenty of refresh-would-wipe-state activity, but also a handful of this/route/is/important bits that persist between refreshes? (for instance, linking to a user record from outisde the app)
I originally had separate vue apps for those high-level sections, but maintaining that and furthermore solving for bits of state that should be more global was becoming a nightmare.

got any advice for a hybrid-ish SPA where you want plenty of refresh-would-wipe-state activity, but also a handful of this/route/is/important bits that persist between refreshes?

Embrace page refreshes, "app"-routers + rails is doable, but I would just allow routes.rb handle everything. Make sure your app chunks are cached & the ViewModel embedded and the difference will be negligible on a modern browser. It all depends on your implementation.

Embrace page refreshes, "app"-routers + rails is doable, but I would just allow routes.rb handle everything. Make sure your app chunks are cached & the ViewModel embedded and the difference will be negligible on a modern browser. It all depends on your implementation.

yea... that wouldn't be as bad if I wasn't already a good chunk of the way through a large app and realizing I want URLs to work. :(
I think what you're saying @jakeNiemiec is the correct approach but it represents a pretty sizeable rebuild for my current application. Also, what do you mean "the ViewModel embedded? Like having the server render data into the template for consumption by the front end app at that page?

@vcavallo, Sorry to text-dump you, but the following was brought up by the maintainer of Redux. It gives a good 1000ft. view of the problem-space around client-side state management. TL;DRs at the end.

I have programmed in Servlets, JSP Model 2, ASP.NET, iOS, Android, Windows. None of these require a state management library.
All of these have State Management involved somewhere, you might not "obviously" have a "State Management Library" because your stack might encode it simply as "best practices" or it might be a low level detail in the stack that you treat as a simple, black box, but more often than not in many of those environments you are building ad hoc state management libraries all over the place, each application its own little snowflake of state management.

As someone else pointed out, you might be strongly relying on a database or an ORM as your primary state management library. Most React projects are using In Memory data structures, for a vast majority of their work, and most state management libraries you can think of as simply an "ORM-like" thing for managing CRUD in memory.

But we can push the abstraction further. The ORM is often used to build the "M" in an MVC pattern. There are all sorts of complicated Model patterns in various MVC frameworks. Some have more boilerplate than others, some have complex engines and libraries that do a lot of management work. A lot of models use "plain old class objects" (POCOs) of one sort or another, but then have all sorts of ravioli code inside those classes to maintain that the state follows business logic rules, and notifies other classes of necessary changes, etc. That of course depends somewhat on the separation of concerns between your Models and Controllers. (I've seen a lot of "MVC frameworks" and "MVC apps", and almost no two are alike in their M/C separation choices, or their overall state management architecture.)

React itself solely concerns itself with View as a framework; it leaves Models/Controllers entirely to the user. Various React state management libraries are different variations on Model/Controller logic/preferences.

(Some React state management libraries more explicitly define the Model/Controller separation and powers than just about any MVC framework for the platforms listed above.)

As you get into more recent, more "reactive", patterns you see a lot of movement toward "MVVM" Model-View-ViewModel. In those patterns Views and ViewModels are a bit more tightly coupled than MVC. Often a VM has a way to signal data changes (for "two-way" binding) to the View so it can update just the things that changed. In .NET the pattern is called INotifyPropertyChanged. Most other JS frameworks right now (Angular, Vue, Aurelia) follow similar patterns to MVVM, somewhat tightly coupling Views to ViewModels in a similar way to MVVM, even if the frameworks often obscure those details in proxy/signal "automagic". If you look at MVVM projects you will also find some variety for how data changes are signaled. Even .NET's INotifyPropertyChanged has lead to a variety of different toolkits and MVVM libraries designed to manage it and orchestrate it.

React instead only "allows" one-way data binding in views. So even from an MVVM standpoint as opposed to an MVC standpoint, this leaves React state management libraries with obvious goals to do some of the INotifyPropertyChanged-type actions of knowing which state changes at a time, giving React a leg-up in its "one-way" data binding to better know when to re-render components based on state changes. (In many cases a benefit here is that most React state management libraries don't necessarily return to tightly-coupling Views and ViewModel equivalents, most of them look more like MVC than MVVM, despite something of a closer comparison to MVVM in desired outcomes.)

Most of the individual state management concepts in React state management libraries exist in other frameworks, they just often have other names (ORMs, Model Libraries, Controller Patterns, MVVM Frameworks, etc) or are even considered as "user" parts of the stack, left to the individual project to build ad hoc as needed.

Part of what React's state management diaspora has been good for is questioning some basic assumptions in MVC and MVVM patterns. One of those assumptions is "mutable state" where you might just directly update POCO objects each time state changes. (Which works, and has been used for a long time, but also has a history of interesting bugs and debugging issues.) The library shown here is interesting because it's one trying to get the programming benefits of immutable state with the "POCO" terseness of mutable state (draftState.itemList[55].name = 'new name' instead of something more like nextState = curState.setIn(['itemList', 55, 'name'], 'new name')).

what do you mean "the ViewModel embedded? Like having the server render data into the template for consumption by the front end app at that page?

So Rails is MVC, right? Let's call the V: Rails View. The Rails View is responsible for rendering Vue, which uses the MVVM pattern. Therefore, you have: Rails(Model View(Vue:MVVM) Controller).

_TL;DR_: With all of that out of the way, what I am trying to say is don't use a client-side MVC pattern within your Rails View. This can lead to Client-side & Rails-side Controller logic that can get out of sync. Have Rails render the ViewModel like:

<head>
  <script>window.someAppProps = <%== @app_props.to_json %>;</script>
  ...

Changes or submissions get reported back to the server-side rails controller. The rails controller then returns an updated ViewModel. This works because your app should only worry about UI stuff & leave business logic to the server.

I am not so great at conveying this in a Rails-friendly way, but I once made the mistake of writing an app that tried to sync client-side data with the server...the required maintenance compounded with every change. This thinking is the result of making rails apps with both angular & react.

If you email me, I'll send you some examples that are currently in production. I have been meaning to make a clearer write-up on this topic, especially with https://github.com/rails/rails/pull/36388 coming out.

_Even shorter TL;DR_: Your client-side JS should let rails controllers handle non-UI state.

thanks man, that's helpful and I'll take it into consideration going forward. for the time being, the root of all my problems was a pesky explicit router.push('/') hiding somewhere in the codebase. meaning: there really was no problem at all (aside from me).

for future apps I would like to start from the ground up with the paradigm you're talking about. for now i'll just try to keep the current build from getting too wacky.

@jakeNiemiec, the Vue link you provided is old. Here are newer links, with the first saying that Vue is focused on the view layer, and the second making a reference to MVVM: https://vuejs.org/v2/guide/index.html and https://vuejs.org/v2/guide/instance.html

Was this page helpful?
0 / 5 - 0 ratings

Related issues

eriknygren picture eriknygren  路  3Comments

ijdickinson picture ijdickinson  路  3Comments

inopinatus picture inopinatus  路  3Comments

Eearslya picture Eearslya  路  3Comments

towry picture towry  路  3Comments