I have create.gsp for creating domain objects. Besides normal html body and headers I have two div's. One for displaying command object errors and one for data. I want to submit the form with AJAX. When I submit the form with
<g:formRemote name="formName" update="errorsDiv" url="[controller:myController', action:'checkAndForward']">
this gets called:
def checkAndForward= {CmdObject p ->
if (p.hasErrors()) {
render(template:"/error",params:params,model :[p:p])
} else {
forward controller: "test", action: "save", params:params
}
}
def save = {
id=myservice.getData()
render(view: "show", id:id)
}
This is working and the save action is called in the controller but ..
The problem is that after 'save' action 'show.gsp' is displayed into where errors div is located.
I would like to have only 'show' page refreshed as if I was calling /save from browser (with posted params of course)
Related
I have an Entry model. I render each entry using a partial. Below each entry, I have a button. Clicking on that button shows the complete entry within a turbo frame.
When the turbo frame loads, the #toggle action is fired in the stimulus controller. Rails generates a unique id for every button using dom_id(entry, :show). (show_entry_1000529641 for example).
I want to reference the button element in the #toggle action.
I understand that the usual way of referencing targets is by first declaring them at the top static targets = [...]. But since the target ids are generated dynamically, I'm not sure what to do.
At the moment, I am setting a data-button attribute on the turbo frame. Then using that value to look up the button element.
I feel like there is probably a better way of doing this. What is the correct/better way?
#entries/_entry.html.erb
#...
<%=
button_to "Show #{entry}",
entry_path(entry),
id: dom_id(entry, :show),
method: :get,
data: { turbo_frame: dom_id(entry) } %>
#...
<%= turbo_frame_tag entry,
data: {
controller: "buttons",
action: "turbo:frame-load->buttons#toggle",
button: dom_id(entry, :show)
} %>
import { Controller } from "#hotwired/stimulus"
export default class extends Controller {
toggle() {
const button_id = this.element.getAttribute('data-button')
const button = document.getElementById(button_id)
console.log(button)
}
}
The Targets use a name as a reference, not an ID (see documentation)
The correct Stimulus approach is to have all necessary elements inside the controller and reference these using Target names. The controller <div> acts as a scope therefore you can have multiple same name controllers with same name targets in one page and it would just work, no need for element IDs.
There where you would normally use a reference by ID, use this.exampleTarget in Stimulus. An analogy for a reference by a class is this.exampleTargets. Targets must be defined in the static targets = ['example'] and in the element's data attribute data-controllerName-target="example"
I suggest you try something like this:
#entries/_entry.html.erb
#...
<div data-controller="buttons">
<%=
button_to "Show #{entry}",
entry_path(entry),
id: dom_id(entry, :show),
method: :get,
data: { turbo_frame: dom_id(entry),
buttons_target: 'button'
} %>
#...
<%= turbo_frame_tag entry,
data: {
action: "turbo:frame-load->buttons#toggle",
button: dom_id(entry, :show)
} %>
</div>
import { Controller } from "#hotwired/stimulus"
static targets = ['button']
export default class extends Controller {
toggle() {
console.log(this.buttonTarget)
}
}
I have been stuck on this problem for quite some time now and looked through several posts as well, however I cannot achieve exactly what I want for my Rails application. Essentially, I want to be able to click on a table row on my page and have a modal pop up which displays all the information for that specific record. Here are the scenarios which I have thought of/attempted partially:
Set the data-link attribute in table row with some JS as follows
HTML:
<tr data-link="<%= kid_path %>">
...
</tr>
JS:
$("tr[data-link]").dblclick(function() {
window.location = $(this).data("link")
})
This worked fine to open the show path page generated by the scaffold, but I was not able to modify it to work with a modal and have the same data for the kid in the modal.
Use data-id and JavaScript to load onto the modal
HTML:
<tr data-id="<%= kid.id %>">
...
</tr>
JS:
$(function () {
$('#showModal').modal({
keyboard: true,
backdrop: "static",
show: false,
}).on('show', function () {
});
$(".table-striped").find('tr[data-id]').on('click', function () {
debugger;
$('#showDetails').html($('<p>' + 'Kid ID: ' + $(this).data('id') + '<%= Kid.find(30).first_name %>' + '</p>'));
$('#showModal').modal('show');
});
});
In this approach I am able to load the modal on row click and am able to access the Kid ID, however I cannot move further to access other attributes of the record. For example, I want to set #Kid = kid.find(id) using JS where id would be the extracted ID from the row. And then, I want to be able to write the generic modal template which displays other elements (ex. kid.first_name, kid.last_name, etc).
I am totally stuck and cannot find any approach that helps to accomplish my goal. Any help is appreciated, thank you.
You need to ajax call record attributes because the line Kid.find(30).first_name doesn't exist at the time page loaded.
Try this:
KidsController
def show
kid = Kid.find(params[:id])
respond_to do |format|
format.html { // Usually show.html.erb }
format.json do
# Return kid as json object
result = {
first_name: kid.first_name,
last_name: kid.last_name
}
# If you want to return the whole kid record attributes in json: result = kid.attributes.to_json
render json: result
end
end
end
Try /kid/[:id].json to verify that you are not getting UnknownFormat error.
JS
$(".table-striped").find('tr[data-id]').on('click', function () {
var kid_id = $(this).data('id');
$.getJSON("/kid/" + kid_id, function(data) {
// Render the modal body here
// first_name = data.first_name, last_name = data.last_name etc
$('#showDetails').html($('<p>'+ data.first_name + '</p>'));
$('#showModal').modal('show');
});
})
If you have setup correct route for Kid model then these are what you needed.
UPDATED: I made a typo in the result hash. FIXED
In my customer index view if I click on delete customer button, the following actions take place in my Customers controller delete method.
Customers_controller.rb
class CustomersController < ApplicationController
.
.
def destroy
if Customerarticle.where(author_id: params[:id]).count > 0
#####I need the logic here to display a modal window on my index page of customers here#####
else
Customer.find(params[:id]).destroy
flash[:success] = "Customer deleted"
# debugger
redirect_to customers_path
end
So based on my condition if it's true means customer can't be deleted, and that message to be displayed in a modal pop up window on Customers index page itself, how to achieve it ?
I suggest you issue an ajax request when the button was clicked and make it redirect to your destroy action. Then, within the controller do your logic and simply return a true/false (or whatever else you need) and use it in your JS callback.
For example:
In your view.js
$(document).ready(function(){
$("#delete_button").click(function(e){
e.preventDefault();
$.ajax({
type: "POST",
url: "/URL/TO/CONTROLLER",
data: { id: someID},
success: function(result){
if(result == true) {
// Do something
} else {
window.location.replace(result);
}
}});
});
});
In your controller:
def destroy
if Customerarticle.where(author_id: params[:id]).count > 0
return true
else
Customer.find(params[:id]).destroy
flash[:success] = "Customer deleted"
return customers_path
end
end
This should work just fine. Remember, it definitely needs some re-work and adjustments :)
How to pass value through link in controller function.
I want to pass below rc value in link so that routes collect it and send to controller.I am new bies in rails.Anyone please help me to solve the problem.
my html.erb .which collect value from text box through jQuery function.
<script type="text/javascript">
var rc=jQuery("#commonid").val();
</script>
<div ><%=link_to "Alert By Region",alerts/filter_alert %></div>
my routes.rb
file which match the link and send to controller
match 'alerts/filter_alert', :controller => 'alerts', :action => 'filter_alert'
my controller
def filter_alert(x)
#message=x
#title = #title + ' - Alerts'
render :site_alerts
end
Javascript things belong to Javascript. You need to manipulate this value dynamically upon visitor's clicking this link.
In Rails' controller side, you can't hard code the method. You need to use params from query string because you won't know what the input is.
# Views
link_to 'Alert By Region', 'alerts/filter_alert', id: 'alert-link'
# Javascript
$('#alert-link').on('click', function() {
var rc = $("#commonid").val();
if rc {
this.attr['href'] += '?rc='+ encodeURI(rc);
return true;
} else {
alert 'input is blank!';
return false;
}
});
# Controller
def filter_alert
rc = params[:rc]
# Then process based on this
end
I cannot render the errors from my command object. It does the job well but my .gsp view does not render the errors I raise.
Here is my controller action:
def handleModifyProfile2 = { CreditProviderModificationCommand cpmc -> // bind params to the command object
if (cpmc.hasErrors()) {
flash.message = "Error modifying your profile:"
redirect(action: "modifyProfile", params: [creditProvider : cpmc])
} ...
Here is how I try to render the errors in my .gsp view:
<g:hasErrors bean="${creditProvider}">
<div class="errors">
<g:renderErrors bean="${creditProvider}" as="list" />
</div>
</g:hasErrors>
How can I get the errors to be displayed in the view?
You can't send the command across in a redirect using params. You have a couple options:
render() in the error condition instead of redirect()ing:
if(cpmc.hasErrors()) {
render(view: 'profile', model: [creditProvider: cpmc])
}
This is the most common idiom for what you're doing.
Store the command in the session to persist it across the redirect:
if(cpmc.hasErrors()) {
session.cpmc = cpmc
redirect(...)
}
// and in your action
def cpmc = session.cpmc ?: null
render(view: 'profile', model: [creditProvider: cpmc])
This option is somewhat questionable. If not done correctly, you can pollute the session and leave objects hanging around, taking up memory. If done correctly, though, it can be a decent way to implement a post-redirect-get.
With Grails 3 (I don't know if this worked earlier) it's possible to use the flash for this. According to the documentation, the flash will be "cleared at the end of the next request".
I like to use a pattern like this:
def save(MyDomain myDomain) {
if (myDomain.validate()) {
myDomain.save()
} else {
flash.errors = myDomain.errors
}
redirect(action: 'edit', id: myDomain.id)
}
def edit(MyDomain myDomain) {
if (flash.errors) {
myDomain.errors = (Errors) flash.errors
}
return [myDomain: myDomain]
}
I don't like to use render() for this kind of error handling, because it makes URLs shown in the browser inconsistent with the shown page. This breaks when users set bookmarks, for example.