konnorrogers / mrujs Goto Github PK
View Code? Open in Web Editor NEWUJS for modern javascript.
Home Page: https://mrujs.netlify.app
License: MIT License
UJS for modern javascript.
Home Page: https://mrujs.netlify.app
License: MIT License
Currently we only advance. Not sure when to replace other than when a user sets it.
Although I don't want to entirely break the current behavior, I think @seanpdoyle added a number of key points above best practices that I think would be prudent of mrujs to document. For example, using <button>
over <a>
for data-method
and using formmethod
and formaction
instead of data-method
and data-url
respectively.
The mutationObserver now properly checks for changed attributes, but what happens if attributes are removed? Should they be rechecked for their validity and have their event listeners removed? If so, how do we make this happen? Do we create a Set
of all watched nodes and run through them and make sure theyre correct? this is an...interesting...problem to say the least.
Perhaps another filter could be added to the src/utils/dom.ts to check the validity of the node[s] whos attributes were removed?
Additionally, morphs should be enable-able / disable-able on remote forms / links. data-ujs-morph="true"
and data-mrujs-morph="true"
should both be supported.
no error
Uncaught TypeError: Cannot redefine property: submitter
at Function.defineProperty (<anonymous>)
at turbo.es2017-esm.js:39
in application.js
import mrujs from "mrujs"
import { Turbo } from "@hotwired/turbo-rails"
window.Turbo = Turbo
mrujs.start()
rails 6.1.4
vite ruby
Test on Chrome Android 78 which is no native support SubmitEvent API
I believe this issue is caused by repeated execution of the polyfill twice.
Object.defineProperty will set writable: false, enumerable: false, configurable: false
by default.
Long story short, successful form submissions requires the form to redirect the user. Im not sure this is technically necessary.
Happy to hear other peoples feelings on this matter. The code at this point is mostly a hold-over from Turbo.
Any discussion around this would be super helpful if its better to just morph anything that doesnt redirect instead of just >400 status codes. This would also mean you dont need to set a status for failed form submissions (although you probably should)
the current data-ujs-confirm
in the plugins section is hacky and gross. @seanpdoyle made an excellent example that shows a far more robust way to handle confirms.
Rails-ujs exposes event.detail.status
and so should we!
We should also take a deeper look at exposed event.detail from rails-ujs herE:
https://guides.rubyonrails.org/working_with_javascript_in_rails.html#rails-ujs-event-handlers
According to https://m.patrikonrails.com/a-definitive-guide-to-railss-unobtrusive-javascript-adapter-ef13bd047fff
...when a developer uses redirect_to
in a controller action, the client receives Turbolinks commands in raw JS:
# app/controllers/users_controller.rb
class UserController < ApplicationController
def destroy
User.find(params[:id]).destroy
redirect_to users_path
end
end
Turbolinks.clearCache()
Turbolinks.visit("http://web.dev/users",{"action":"replace"})
This may or may not be modified by the inclusion of turbolinks-rails
in the project:
https://github.com/turbolinks/turbolinks-rails
It seems like this project might need to include a sidecar gem called mrujs-rails
that duplicates the functionality of the above, except by sending JSON to the client. The real security issue was always generating the raw JS on the server.
Update:
It all comes down to this Concern:
https://github.com/turbolinks/turbolinks-rails/blob/master/lib/turbolinks/redirection.rb
Following the thought progression, here... it seems reasonable to assume that this is where https://github.com/jorgemanrubia/turbolinks_render comes into the picture via https://medium.com/@jmanrubia/escaping-the-spa-rabbit-hole-with-turbolinks-903f942bf52c
I should be able to add a data-remote link to the DOM and have it work the same as one that was present at page load.
data-remote links added to the DOM after page load don't submit a fetch request and act like normal links.
Interestingly, remote forms that are added to the DOM after page load still seem to work.
See https://github.com/existentialmutt/cable_car_messages/tree/mrujs . Start up the app and click on New Message, then "Cancel" .
tested with mrujs-0.3.0-beta.11
currently you need to add data-turbo="false"
to remote links and forms.
This DX could be improve but automatically attaching this behavior.
Credit goes to @julianrubisch for suggesting the idea.
Hey Konnor, I've been so pre-occupied with cable_car I realized mrujs is doing some cool things with remote html forms and I wanted to get clear on exactly how they work. While tinkering around with a simple scaffold I noticed different behavior depending on whether I have turbo or turbolinks enabled and that seems off.
Turbo and Turbolinks should have consistent behavior for remote form submissions.
When using Turbolinks: browser follows the redirect with a non-turbolinks (i.e. whole page load) navigation.
When using Turbo: nothing happens
Start up the app in the repo provided and go to http://localhost:3000 . Fill out both fields in the form and hit submit.
Switch between using Turbo and Turbolinks by editing app/javascript/application.js
https://github.com/existentialmutt/mrujs_sandbox
I should add that unsuccessful form submissions work in both cases. The page is updated in-place with the HTML from the response. That's really cool!
I'd really love to know what the intended behavior should be. Coming from OG UJS where everything needs to be handled manually I'm not quite sure what to expect.
Currently, buttons require specific data-method
and data-url
's. @seanpdoyle pointed out that there is an opportunity here to build upon browser fundamentals. Theres no reason a <button>
cant submit an ajax request if it provides a formmethod
and formaction
.
Mrujs uses actual fetch
under the hood, so it does not need to worry about the CSRF token generation Sean referred to. More info can be found here:
Rails 6.1 no longer does remote forms by default.
config.action_view.form_with_generates_remote_forms = true
No more lies!
Maybe I just need to get better with fetch, but after catching an ajax:success event I fumbled with jumping through async hoops to get to the body content. Finally settled on event.detail.response.json().then(operations => CableReady.perform(operations))
which I guess isn't that awful but took me a few tries and some digging through fetch docs to get there.
I'd love it if event.detail
provided a responseBody property (or function) that provides direct access to the response body content as a string.
currently only basic elements work. We should provide a migration for custom elements and allow for users to define custom element query selectors.
Work would start here on the various selectors:
204 no contents should request the current page
204s do nothing.
Use morphdom to request the current page and then morph the response
Currently, there is a whats essentially a monkey path for fetch due to how Rails send redirects as 302 Headers. Its possible in the future we may not want to mask every method under a POST. We should allow this to be configurable.
work would be done in 2 places:
https://github.com/ParamagicDev/mrujs/blob/main/src/linkSubmission.ts
https://github.com/ParamagicDev/mrujs/blob/main/src/formSubmission.ts
These are form and link submission and masks requests to deal with how rails redirects.
I'm using some @rails/ujs
methods that might be missing or need docs for compatibility to switch over to mrujs
:
Rails.fire
Rails.confirm
- I see this in my code base but I don't think its usedThe following come from https://github.com/kirillplatonov/shopify-hotwire-sample/blob/910a2216dd61a5e52628e155901a42d185d1cf92/app/javascript/packs/application.js#L11-L17
Rails.delegate
Rails.linkDisableSelector
-- oops, found it https://github.com/ParamagicDev/mrujs/search?q=linkDisableSelectorRails.buttonDisableSelector
Rails.formSubmitSelector
My code base also uses Rails.ajax
but AFAIK I need to port that to fetch
right?
Clicking on
<a class="text-danger" data-method="delete" data-confirm="Are you sure you want to delete this contact?" data-turbo="false" href="/sellers/2/contacts/10">Delete</a>
is expected to make a delete
request to the server, where the controller action performs a redirect, and the page respond with a redirect
Controller
def destroy
@contact.destroy
flash[:success] = 'Contact Deleted'
redirect_to seller_contacts_path(current_organization)
end
The controller action is hit and the browser receives the response to redirect, however the DOM remains unchanged
Response vs network errors should still be separate. But they should also have a unified interface.
When calling window.mrujs.fetch()
should it trigger the ajax:<name>
events?
UJS did not do this, but I view Mrujs as a superset and wonder if some cool interactions could happen from this....
Edit:
thumbsup for "yes trigger events"
thumbsdown for "no, async / await is plenty."
Users cannot easily prefetch links
Expose the navigationAdapter's prefetch functionality so an end user can take advantage.
When I destroy something with an data-method="delete"
, the destroy URL should not be available with the back button.
When I destroy something with data-method="delete
, the destroy url is added to the back navigation.
const callback = (message) => {
return window.confirm(message)
}
window.mrujs.registerConfirm("my-modal", callback)
const asyncConfirm = async (message) => {
try {
await myAsyncFunction(message)
return "Success!"
} catch(err) {
Promise.reject(err)
}
}
window.mrujs.registerAsyncConfirm("my-modal", asyncConfirm)
<button data-confirm-my-modal="Show my modal!">Click me for a modal</button>
<button data-async-confirm-my-modal="Show my modal!">Click me for a modal</button>
@seanpdoyle described a number of exciting, and intriguing points here in this PR:
hotwired/turbo-rails#257 (comment)
For example, his data-confirm
implementation excites me since I currently have a very hacky data-async-confirm
solution. In addition, there are many other items such as respecting frames on form submissions that is important for working in a Turbo enabled app.
There are some things that may not apply. For example, data-method
in mrujs constructs a Fetch request under the hood and does not actually submit a full form, as well as some turbo-specific things such as request-start
since mrujs will handle its own lifecycle of ajax:
events this may not be necessary.
Regardless, over the next few days I plan to go in and pluck key points out from his post / PR and hope to start implementing some of these features into Mrujs since theyre exciting to me and I think they should be considered.
are Turbo specific and should move into the Turbo plugin.
currently Turbo + Mrujs have quite a bit of intersection. I'm not even sure if this is a good idea tbh. This is more so a "future possibility"
there will be a lot of trial and error to make this happen. So this issue will serve as a future reference of possible changes that need to be made.
first change: modify querySelectors.
Currently, fetch requests cant be aborted. We should add in an abort controller for each fetch request to allow it to be cancelable.
https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort
For type declarations to be emitted to dist/
.
Only transpiled source code is emitted.
Run yarn build
in the project root.
https://github.com/ohmree/mrujs
I think this issue has to do with this section of the typescript plugin's readme.
In my fork (linked above) I've tried to fix it and managed to get rollup to output declarations correctly for the root project, but for plugins/
the nesting is still wrong.
One thing that did eliminate the nesting for the plugins dir was to mess with rootDir
in both the plugins project and the root one (I think, can't remember exactly what I did but it shouldn't be hard to reproduce).
That makes the build fail because /plugins/
references /src/types.ts
but the declarations are emitted correctly.
Mrujs doesnt do things like respect frames. @seanpdoyle created a PR with what Turbo glue code would look like. I think theres a serious opportunity here to embed this into Mrujs.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.