In my view I've a form where I only want to show inputs when its parameters appear in the URL:
= form_tag search_path, :method => :get do
= select_tag 'search[type]', options_for_select([['Publishers', 'publishers'], ['Editions', 'editions'], ['Contributors', 'contributors']], selected = (params[:search][:type] rescue nil)), prompt: "Search type", :required => "true"
= select_tag 'search[filter]', options_for_select([['Filter by', 'filter_by'], ['All', 'all']], selected = (params[:search][:filter] rescue nil)), prompt: "Select filter", :required => "true"
- invisible = params[:search][:value].present? ? '' : 'invisible'
= text_field_tag 'search[value]', (params[:id] || params[:search][:value] rescue nil), :required => "true", :class => "#{invisible}", :placeholder => "Type search"
My idea was control it showing a CSS class called "invisible" with display: none;, as above. But when I render the view it returns this Rails error in the line where I declare the "invisible":
undefined method `[]' for nil:NilClass
What's wrong?
This is because when the form is loading params[:search] is nil, and you're trying to call [:filter] on it, hence undefined method [] for nil.
There's various solutions, some nice, some not so nice, pick your favourite:
invisible = params[:search].try(:[], :value) ? '' : 'invisible'
invisible = params[:search] && params[:search][:value] ? '' : 'invisible'
invisible = (params[:search] || {})[:value] ? '' : 'inivisble'
invisible = (params[:search][:value] rescue nil) ? '' : 'invisible'
It is because params[:search] is nil and you are calling params[:search][:filter]
Try:
invisible = params[:search].present? ? (params[:search][:filter].present? ? '' : 'invisible') : ''
...
- invisible = params[:search].try(:[], :filter).blank? ? 'invisible' : ''
...
Related
I would like to be able to change a JSON object parsed with the APEX_JSON.parse() function, but I found nothing in the official documentation, no tips or examples about that.
In the code below I putted comments in the specific points in order to give a clear idea about my need.
SET SERVEROUTPUT ON
DECLARE
j apex_json.t_values;
l_paths apex_t_varchar2;
writers clob;
json_object_result clob;
BEGIN
writers := '[
{
"id": null,
"hashCode": "7ef605fc8dba5425d6965fbd4c8fbe1f",
"description": "Machado de Assis",
"base64Img": "8eNZ28Dh5rZQIPhNEfwqoo1LCVx..."
},
{
"id": 151,
"hashCode": "a8f15eda80c50adb0e71943adc8015cf",
"description": "José Alencar",
"base64Img": "/XIQIHCol8eNZ28Dh5rZQIPhNEfwqoo1LCVx/9k=..."
}
]';
apex_json.parse(j, writers);
l_paths := apex_json.find_paths_like (
p_values => j,
p_return_path => '[%]',
p_subpath => '.hashCode',
p_value => '7ef605fc8dba5425d6965fbd4c8fbe1f'
);
dbms_output.put_line('Itens found: ' || l_paths.count);
if (l_paths.count = 1) then
dbms_output.put_line('id: ' || apex_json.get_varchar2(p_values => j, p_path => l_paths(1) || '.id'));
dbms_output.put_line('hashCode: ' || apex_json.get_varchar2(p_values => j, p_path => l_paths(1) || '.hashCode'));
dbms_output.put_line('description: ' || apex_json.get_varchar2(p_values => j, p_path => l_paths(1) || '.description'));
dbms_output.put_line('base64Img: ' || apex_json.get_varchar2(p_values => j, p_path => l_paths(1) || '.base64Img'));
-- Here I would like to nullify base64Img attribute value
json_object_result := 'here goes the object matched in the search with the base64Img attribute value null';
end if;
END;
In my json_object_result variable I would like to save the JSON object matched in the apex_json.find_paths_like() with its base64Img attribute null. In my example the result would be
{
"id": null,
"hashCode": "7ef605fc8dba5425d6965fbd4c8fbe1f",
"description": "Machado de Assis",
"base64Img": ""
}
or
{
"id": null,
"hashCode": "7ef605fc8dba5425d6965fbd4c8fbe1f",
"description": "Machado de Assis"
}
Is it possible to make something like that with APEX_JSON?
inside user.rb model
before_save :update_stripe
def update_stripe
if customer_id.nil? #line 1
if !stripe_token.present? #line 2
raise "Stripe token not present. Can't create account."
end
customer = Stripe::Customer.create(
:email => email, #line 3
:card => stripe_token, #line 4
)
self.role = "owner" #line 5
end
end
when i do 'role = "owner"' it doesn't sets 'role' setter but doing 'self.role = "owner"' sets it, in 'line 5', How ? Also 'customer_id', 'stripe_token', 'email' and 'stripe_token' getter works to get value without 'self' keyword in 'line 1', 'line 2', 'line 3' and 'line 4' resp. How ?
doing setter_method = val, means, you actually create a local variable inside your method and not accessing your setter, as mentioned in the link provided by Frederick Cheung.
I have upgraded Refinery CMS to the newest version (2.1.0), where there is a new approach in rendering the navigation menu :
(in partial _header.html.erb)
<%= Refinery::Pages::MenuPresenter.new(refinery_menu_pages, self).to_html %>
The older version of the same partial :
<%= render(:partial => "/refinery/menu", :locals => {
:dom_id => 'menu',
:css => 'menu'
}) %>
How could I add bootstrap styles to the navbar using MenuPresenter?
It can be done, but the solution is not pretty because the Menu Presenter in Refinery 2.1 doesn't support all the right CSS options out of the box. But with a bit of perseverance, this is roughly what to do:
Firstly, create a new blank file here: config/initializers/refinery/monkey_patch_menu_presenter.rb
In this patch file, paste in the contents of this updated version of the menu presenter (published October 2013): menu_presenter.rb
Next, based on the instructions in section 5 of the menu presenter guide, in your app/helpers/application_helper.rb file, add a new method called navigation_menu:
def navigation_menu
presenter = Refinery::Pages::MenuPresenter.new(refinery_menu_pages, self)
presenter.css = "navbar-inner"
presenter.menu_tag = :div
presenter.list_tag_css = "nav"
presenter.selected_css = "active"
presenter.first_css = ""
presenter.last_css = ""
presenter.max_depth = 0 # prevents dropdown menus, which don't render correctly
presenter
end
Finally, in your app/views/refinery/_header.html.erb file (use $ bundle exec rake refinery:override view=refinery/_header if it doesn't exist), replace the call for:
<%= Refinery::Pages::MenuPresenter.new(refinery_menu_pages, self).to_html %>
with:
<div class="navbar">
<%= navigation_menu.to_html %>
</div>
Ensure that you have the loaded the Bootstrap CSS/JS files and have wrapped the whole page in a <div class="container"> element. Then restart your application for the patch to take affect and hopefully you'll see a familiar bootstrap navigation bar.
Good luck!
Martyn.
Here a version of above menu_presenter.rb that renders sub-menus as well
(This if for Bootstrap 3, RefineryCMS 2.1.1):
require 'active_support/core_ext/string'
require 'active_support/configurable'
require 'action_view/helpers/tag_helper'
require 'action_view/helpers/url_helper'
module Refinery
module Pages
class MenuPresenter
include ActionView::Helpers::TagHelper
include ActionView::Helpers::UrlHelper
include ActiveSupport::Configurable
config_accessor :roots, :menu_tag, :list_tag, :list_item_tag, :css, :dom_id,
:max_depth, :selected_css, :first_css, :last_css, :list_tag_css,
:link_tag_css
self.dom_id = 'menu'
self.css = "collapse navbar-collapse"
self.menu_tag = :div
self.list_tag = :ul
self.list_item_tag = :li
self.selected_css = 'active'
self.first_css = :first
self.last_css = :last
self.list_tag_css = "nav navbar-nav"
def roots
config.roots.presence || collection.roots
end
attr_accessor :context, :collection
delegate :output_buffer, :output_buffer=, :to => :context
def initialize(collection, context)
#collection = collection
#context = context
end
def to_html
render_menu(roots) if roots.present?
end
private
def render_menu(items)
content_tag(menu_tag, :id => dom_id, :class => css) do
render_menu_items(items)
end
end
def render_menu_items(menu_items)
if menu_items.present?
content_tag(list_tag, :class => list_tag_css) do
menu_items.each_with_index.inject(ActiveSupport::SafeBuffer.new) do |buffer, (item, index)|
buffer << render_menu_item(item, index)
end
end
end
end
def render_menu_items_children(menu_items)
if menu_items.present?
content_tag(list_tag, :class => 'dropdown-menu') do
menu_items.each_with_index.inject(ActiveSupport::SafeBuffer.new) do |buffer, (item, index)|
buffer << render_menu_item(item, index)
end
end
end
end
def render_menu_item_link_dropdown(menu_item)
link_to( menu_item.title, context.refinery.url_for(menu_item.url), class: "dropdown-toggle", data: {toggle:"dropdown", target: "#"})
end
def render_menu_item_link(menu_item)
link_to(menu_item.title, context.refinery.url_for(menu_item.url), :class => link_tag_css)
end
def render_menu_item(menu_item, index)
content_tag(list_item_tag, :class => menu_item_css(menu_item, index)) do
buffer = ActiveSupport::SafeBuffer.new
# Check for sub menu
menu_item_children(menu_item).empty? ? buffer << render_menu_item_link(menu_item) : buffer << render_menu_item_link_dropdown(menu_item)
buffer << render_menu_items_children(menu_item_children(menu_item))
buffer
end
end
# Determines whether any item underneath the supplied item is the current item according to rails.
# Just calls selected_item? for each descendant of the supplied item
# unless it first quickly determines that there are no descendants.
def descendant_item_selected?(item)
item.has_children? && item.descendants.any?(&method(:selected_item?))
end
def selected_item_or_descendant_item_selected?(item)
selected_item?(item) || descendant_item_selected?(item)
end
# Determine whether the supplied item is the currently open item according to Refinery.
def selected_item?(item)
path = context.request.path
path = path.force_encoding('utf-8') if path.respond_to?(:force_encoding)
# Ensure we match the path without the locale, if present.
if %r{^/#{::I18n.locale}/} === path
path = path.split(%r{^/#{::I18n.locale}}).last.presence || "/"
end
# First try to match against a "menu match" value, if available.
return true if item.try(:menu_match).present? && path =~ Regexp.new(item.menu_match)
# Find the first url that is a string.
url = [item.url]
url << ['', item.url[:path]].compact.flatten.join('/') if item.url.respond_to?(:keys)
url = url.last.match(%r{^/#{::I18n.locale.to_s}(/.*)}) ? $1 : url.detect{|u| u.is_a?(String)}
# Now use all possible vectors to try to find a valid match
[path, URI.decode(path)].include?(url) || path == "/#{item.original_id}"
end
def menu_item_css(menu_item, index)
css = []
css << selected_css if selected_item_or_descendant_item_selected?(menu_item)
css << "dropdown" unless menu_item_children(menu_item).empty?
css << first_css if index == 0
css << last_css if index == menu_item.shown_siblings.length
css.reject(&:blank?).presence
end
def menu_item_children(menu_item)
within_max_depth?(menu_item) ? menu_item.children : []
end
def within_max_depth?(menu_item)
!max_depth || menu_item.depth < max_depth
end
end
end
end
In Rails/Haml
I know this works
%i.class{:class => (name ? "arrow-up" : "arrow-down")}
I also know that I can make a helper function
%i.class{:class => (getArrowClass name)}
def getArrowClass value
if value == 1
return 'arrow-up'
elsif value == 0
return 'arrow-down'
else
return ''
end
end #getArrowClass
For some reason, whenever I do the helper way it messes up my view (tables don't stick to their places), so now my question is how can I do the if elsif else in the class using the first method.
something like that
%i.class{:class => (if name then "arrow-up" elsif "arrow-down" else "")}
Please try with
%i.class{:class => ((name == 1) ? "classup" : (name == 0) ? "classdown" : "")}
all
I am trying to use #Html.CheckBoxFor.
Here is my code:
Model:
public bool WC0001 { get; set; }
View:
#using (Html.BeginForm("ListSearch", "Committee", FormMethod.Get))
{
#Html.CheckBoxFor(model => model.WC0001) <label for="WC0001" class="mrg_r20">Development</label>
}
I am using WebGrid as well.
When I click page 2, I get this error.
Cannot implicitly convert type 'string' to 'bool'
So I looked at the url.
It is "http://localhost:28685/Committee/ListSearch?WC0001=true%2cfalse&page=2"
Why do I get WC0001 parameter as true%2cfalse?
And what is the workaround for this?
It didn't happen when I used to post the form.
But since I changed it as FormMethod.Get because of WebGrid, it started to occur.
Please somebody help me.
EDIT:
This is what happens, when reload the page.
EDIT:
Here is my action code:
[HttpGet]
public ActionResult ListSearch(Kuksiwon.Models.Committee profile, FormCollection collection)
{
DataTable _dt = _bp.DtReturnS(false
, "WSP_KW100_R1"
, profile.APJIKJONGCODE == null ? "" : profile.APJIKJONGCODE
, profile.APKUKSICODE == null ? "" : profile.APKUKSICODE
, profile.CTNAME == null ? "" : profile.CTNAME
, collection["CTSEXList"] == null ? "" : collection["CTSEXList"].ToString()
, profile.CTOADDRESS1 == null ? "" : profile.CTOADDRESS1
, profile.CTOADDRESS2 == null ? "" : profile.CTOADDRESS2
, profile.CTUNIVCODE1 == null ? "" : profile.CTUNIVCODE1
, profile.CTHOSPCODE1 == null ? "" : profile.CTHOSPCODE1
, profile.CTETCNAME == null ? "" : profile.CTETCNAME
, profile.CTUNIVDIV1 == null ? "" : profile.CTUNIVDIV1
, profile.CTUNIVJIKUP1 == null ? "" : profile.CTUNIVJIKUP1
, profile.CTEMAIL == null ? "" : profile.CTEMAIL
, profile.WC0001 ? "WC0001" : ""
, profile.WC0002 ? "WC0002" : ""
, profile.WC0003 ? "WC0003" : ""
, profile.WC0004 ? "WC0004" : ""
, profile.WC0005 ? "WC0005" : ""
, profile.WC0006 ? "WC0006" : ""
, profile.WC0007 ? "WC0007" : ""
, profile.WC0008 ? "WC0008" : ""
, profile.WCNONE ? "1" : "0"
, profile.RSNAME == null ? "" : profile.RSNAME
, profile.BKNAME == null ? "" : profile.BKNAME
, profile.CTSUBJECT == null ? "" : profile.CTSUBJECT
, profile.CTCOLLEGE == null ? "" : profile.CTCOLLEGE
);
ViewBag.ListSearch = _dt;
profile.CTSEXList = profile.CommonCodeList("SX", "1");
return View(profile);
}
This is a bug (well, I consider it a bug) in what MVC spits out for #Html.CheckBoxFor()-- it spits on the actual input checkbox, but also a hidden field with the same name. They're doing this so that an HTTP post will have the default value (if the checkbox is unchecked) still sent along with the data. But for a GET, the browser is sending both values in the querystring, causing it to come out on the server as a string array (leading to your exception).
Here's the jQuery I ended up using to disable the hidden fields if their checkboxes are checked (disabled fields never get sent via FORM submissions). Stick this in your document ready logic:
$('input[type=checkbox] ~ input[type=hidden]').each(function() {
var curHiddenField = $(this);
var checkBox = curHiddenField.prev();
checkBox.change(function () {
curHiddenField.val(checkBox[0].checked);
if (checkBox[0].checked) {
curHiddenField.attr('disabled', 'disabled');
} else {
curHiddenField.removeAttr('disabled');
}
}).change();
});
The only possible reason I can think of that would cause this issue is if your ModelState were modified with an invalid value. That would be the only way to get the error while rendering the page.
You should check the ModelState in your controller like this:
var wc0001 = ModelState["WC0001"].Value;
var raw = wc0001.RawValue; // Should be ["true", "false"]
var attempted = wc0001.AttemptedValue; // Should be "true,false"
var converted = wc0001.ConvertTo(typeof(bool)); // If this fails, then Html.CheckBoxFor will fail too.
Update:
After looking at your action method, I don't see anything that would cause an invalid ModelState. Please let me know the values of .RawValue and .AttemptedValue because those will probably have some clue as to the cause of this problem.
After doing some testing, I think I might have figured out your problem.
As you probably know, rendering a CheckBox results in a an <input type="checkbox" /> and an <input type="hidden" /> with the same name. If the checkbox is checked, when the form is submitted, the GET/POST will have duplicate entries for that field:
ListSearch?WC0001=true&WC0001=false
MVC treats these values as an array of strings ["true", "false"], and when it binds to a bool, it only parses the first array item.
You might have already realized the problem now. Your sample URL has combined these values into:
ListSearch?WC0001=true,false
MVC will treat this value as a single string "true,false", which means it will FAIL to bind to a bool!
The problem is almost certainly caused by some JavaScript that is creating the URL, and the script is probably trying to be smart and combine duplicate inputs into a comma-separated list. This won't work for MVC, though, so you need to keep the values separate.