How do you pass data from a controller to a model with Ruby on Rails?
The concept you're wrestling with is MVC architecture, which is about separating responsibilities. The models should handle interaction with the DB (or other backend) without needing any knowledge of the context they're being used in (whether it be a an HTTP request or otherwise), views should not need to know about the backend, and controllers handle interactions between the two.
So in the case of your Rails app, the views and controllers have access to the request
object, while your models do not. If you want to pass information from the current request to your model, it's up to your controller to do so. I would define your add_community
as follows:
class User < ActiveRecord::Base
def add_community(city, state)
self.community = city.to_s + state.to_s # to_s just in case you got nils
end
end
And then in your controller:
class UsersController < ApplicationController
def create # I'm assuming it's create you're dealing with
...
@user.add_community(request.location.city, request.location.state)
...
end
end
I prefer not to pass the request
object directly, because that really maintains the separation of the model from the current request. The User
model doesn't need to know about request
objects or how they work. All it knows is it's getting a city
and a state
.
Hope that helps.
Rails 4 pass data from controller to model attr_accessor
The @heros
variable is a collection of Hero instances. You can loop through them and set it if you want.
class HerosController < ApplicationController
def index
@heros = Hero.all.order(:name).each do |hero|
hero.current_league = current_league
end
end
end
While it works, I don't find this answer to be all that elegant. Granted, I don't know the full extend of the thing you are making, but based on the code here I would create a composite object. Something like this:
class HeroInLeague
attr_reader :league, :hero
def initialize(league, hero)
@league = league
@hero = hero
end
def some_method
# ...
end
end
Then you can create these objects inside your controller:
class HerosController < ApplicationController
def index
@heros_in_league = Hero.all.order(:name).map { |hero|
HeroInLeague.new(current_league, hero)
}
end
end
Now you've created a place for methods to go that are related the combination of heros and leagues. Why is this important? Well, with the previous approach you'd probably end up with methods on Hero that don't make any sense when there is no current league. (like the some_method
method). That makes the Hero class a bit of a mess. Now you've created a place to put some_method
and all its friends.
You can use delegators to make the interface of HeroInLeague
a bit more friendly, so you don't have to do hero_in_league.hero.foo
, but can call hero_in_league.foo
directly.
How Do I Pass Additional Parameters in Rails from Controller to Model
I have an association of cart and transaction in which cart has_many transactions and transaction belongs_to cart
Since that's the case and you already have a cart object, in your controller just instantiate the transaction from the cart:
transaction = cart.transactions.build app_token: params[:token]
transaction.save
cart_id
will then be available to all the instance methods in the model, so there is no need to extend app_token=
with additional logic unrelated to the app_token. Instead, take advantage of ActiveRecord callbacks. For example, you could use a before_save
callback to implement your logic:
class Transaction < ActiveRecord::Base
belongs_to :cart
before_save :do_something_with_cart
def do_something_with_cart
# I want to add a few more lines of code here so that I can manipulate cart_id
end
end
If for some reason a callback does not fit your use casae, call the custom method directly in the controller:
transaction = cart.transactions.build app_token: params[:token]
transaction.do_something_with_cart
Rails - How to pass data between Rails controller and JavaScript Stimulus controller to dynamically change view
This sounds like plain old pagination.
If there could be thousands of movies you'd be generation markup/html for thousands of movies. With pagination you'd get get 3 records at a time. Could even use a turbo-frame to replace the next 3.
But if you really want to do what your thinking, you'd have hide all cards except for the first 3, then use stimulus to get the next or last three cards.
A test example
Controller
class TestController < ApplicationController
def index
@movies = Array.new(100) { |e| e = e + 1 }
end
Template using slim
.bg-white
div[data-controller="movies"]
- cnt = 0
button.btn[data-action="click->movies#last_set"] Last
button.btn[data-action="click->movies#next_set"] Next
- @movies.each do |m|
- if cnt < 3
div[data-movies-target="movie" class='block'] = "This is movie #{m}"
- cnt += 1
- else
div[data-movies-target="movie" class='hidden'] = "This is movie #{m}"
Stimulus Controller
import { Controller } from "@hotwired/stimulus"
// Connects to data-controller="movies"
export default class extends Controller {
static targets = ['movie']
connect() {
this.size = this.movieTargets.length
this.idx = 0
// this.idx points to the first current cards (3 for example)
}
next_set() {
let curr = this.idx
this.idx = curr + 3 // set new idx
if (this.idx > this.size) {this.idx = this.size - 3}
for (var i = curr; i <= curr + 2; i++) {
this.movieTargets[i].classList.add("hidden")
}
for (var i = this.idx; i <= this.idx + 2; i++) {
this.movieTargets[i].classList.remove("hidden")
}
// console.log(`idx = ${this.idx}`)
}
last_set() {
let curr = this.idx
this.idx = curr - 3 // set new idx
if (this.idx < 0 ) {this.idx = 0}
for (var i = curr; i <= curr + 2; i++) {
this.movieTargets[i].classList.add("hidden")
}
for (var i = this.idx; i <= this.idx + 2; i++) {
this.movieTargets[i].classList.remove("hidden")
}
// console.log(`idx = ${this.idx}`)
}
}
Just a rough example.
EDIT Not very good at javascript and cleaned up the stimulus controller, added variable number of elements to display and work with pages instead of elements.
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ['perPage','movie','status','nextBtn','lastBtn']
connect() {
this.size = this.movieTargets.length
this.page = 0
this.perPage = Number(this.perPageTarget.innerHTML)
this.pages = Math.floor(this.size/this.perPage)
if ((this.size % this.perPage) > 0) {this.pages += 1}
this.setStatus()
// this.page points to the current set of cards (3 for example)
}
next_set() {
if (this.page == (this.pages - 1)) {return}
this.hide()
this.page += 1
this.show()
this.setStatus()
}
last_set() {
if (this.page == 0) {return}
this.hide()
this.page -= 1
this.show()
this.setStatus()
}
hide(){
let curr = this.page * this.perPage
let last = curr + this.perPage - 1
while(curr <= last){
if (curr >= 0 && curr < this.size) {
this.movieTargets[curr].classList.add("hidden")
}
curr++
}
}
show(){
let curr = this.page * this.perPage
let last = curr + this.perPage - 1
while(curr <= last){
if (curr >= 0 && curr < this.size) {
this.movieTargets[curr].classList.remove("hidden")
}
curr++
}
}
setStatus(){
this.statusTarget.innerHTML = `Page ${this.page + 1} of ${this.pages}`
if (this.page == 0) {
this.lastBtnTarget.classList.add('hidden')
}else{
this.lastBtnTarget.classList.remove('hidden')
}
if (this.page == this.pages) {
this.nextBtnTarget.classList.add('hidden')
}else{
this.nextBtnTarget.classList.remove('hidden')
}
}
}
Related Topics
Complicated Graphviz Tree Structure
How to Create an Anchor and Redirect to This Specific Anchor in Ruby on Rails
Rspec Failing Error: Expected False to Respond to 'False'
Form_For Error Messages in Ruby on Rails
Hacking Activerecord: Add Global Named Scope
Can't Install Gems Because "Undefined Method 'Invoke_With_Build_Args' for Nil:Nilclass"
Best Way to Pretty Print a Hash
Ruby on Rails: How to Get Error Messages from a Child Resource Displayed
Check If a Constant Is Already Defined
Which Ruby Version am I Really Running
Rails 3 Install Error: "Invalid Value for @Cert_Chain"
Ruby Regex Error: Incompatible Encoding Regexp Match (Ascii-8Bit Regexp with Utf-8 String)
How Does Require Rubygems Help Find Rubygem Files
Uninitialized Constant "Controller Name"
What Is Your Preferred Way to Produce Charts in a Ruby on Rails Web Application