I've added the Active and Archived buttons to this page for extra filtering.
The existing search box functionality uses the following js.coffee which calls the controller index to pull the data from the db.
triggerSearch: (e) ->
if searchCompanyTimer
window.clearTimeout searchCompanyTimer
searchCompanyTimer = window.setTimeout(( ->
searchCompanyTimer = null
query_text = $(e.currentTarget).val()
el_id = $(e.currentTarget)[0].id
$.get( "companies", "q[name_cont]": query_text )
), 500, e)
I have added 2 similar js.coffee methods which set an active flag to true or false depending on which button was pressed.
Here is one of those methods.
triggerShowActive: (e) ->
if searchCompanyTimer
window.clearTimeout searchCompanyTimer
searchCompanyTimer = window.setTimeout(( ->
searchCompanyTimer = null
$.get( '/companies', {active: true} )
), 500, e)
here is part of my controller.
class CompaniesController < ApplicationController
respond_to :js, :json, :html
$active_inactive_flag = true
def index
puts "params are: #{params}"
puts "active param is: #{params[:active]}"
puts "#active_inactive_flag pre any conditions is: #{$active_inactive_flag}"
$active_inactive_flag = params[:active] ? params[:active] : $active_inactive_flag
puts "#active_inactive_flag after check on params[:active] is: #{$active_inactive_flag}"
if $active_inactive_flag.try :nonzero?
$active_inactive_flag = true
end
puts "#active_inactive_flag after check if it has a value, else true - is: #{$active_inactive_flag}"
#companies =
Company.search(params[:q])
.result
.order('created_at DESC')
.page(params[:page])
.per(Settings.paginate_limit)
.where(is_active: $active_inactive_flag)
end
here is my index.html.erb file where the buttons and search code is.
<div class="row">
<div class="col-md-3">
<label>Filter By:</label>
<button class="show_active btn btn-primary" style="border-radius:10px">Active</button>
<button class="show_inactive btn btn-primary" style="border-radius:10px">Archived </button>
</div>
<div class="col-md-9">
<div class="input-group">
<span class="input-group-addon" id="basic-addon1">Filter by name</span>
<input class="form-control" id="q_name_cont" name="q[name_cont]" type="text">
</div>
</div>
</div>
I am using a global variable (tried instance and class variables also) in the controller but it's not working as expected. Sometimes the value in $active_inactive_flag switches from false to true or vice versa incorrectly. I don't want to use a global variable but I am at a loss as to how to combine both filter and search correctly. When the page loads we want Active button to be on by default and return active companies. The problem I am having is knowing what button was pressed when the search box is used.
Edit:
Here is an example of what is happening.
Any direction would be grateful.
Thanks Dave,
in js.coffee added the following:
returnActiveInctiveFlag = true
and then to the triggerSearch updated to:
$.get( "companies", { "q[name_cont]": query_text, active: returnActiveInctiveFlag } )
In my new methods updated returnActiveInctiveFlag accordingly.
Was really overcomplicating it. Thanks again for getting me thinking in the correct way.
Related
I work on a project with Ruby on rails. I currently have some issues with my index page on which I use Datatables to display my data.
I use the server-side option to display my data since it's a very large database. I added a selector to allow the user to select a project whose vulnerabilities he wants to see. I managed to get the project in my controller and I passed it in an instance variable because I need it in my view. However, the variable isn't reloaded with the ajax call.
How can I reload the instance variables in my view ? I don't need only the project but also other variables such as the displayed variables to make graphs depending on the criticity...
Maybe I'm doing this the wrong way. Could you help me ? Below is my code.
View :
<div class="col-md-8 col-xs-8" style="display: inline-block">
<div class="x_panel">
<h4>Filtres</h4>
<div class="x_content">
<div class="col-md-6 col-xs-6" style="display: inline-block">
<label>Filter by projet : </label>
<%= select_tag "datatable_project", options_from_collection_for_select(#user.projects, :id, :name), include_blank: "Tous", onchange: "selectProject(this.value)" %>
</div>
</div>
</div>
</div>
<div class="x_panel">
<table id="datatable-history-external" class="table responsive-utilities mcs-datatable-history-external" width="100%">
<thead>
<tr class="headings">
<th class="column-title all" style="width:150px">CVE</th>
<th class="column-title all">Base score</th>
<% if not #selected_project.nil? and #selected_project.level == 2 %>
<th class="column-title all">Environmental score</th>
<% else %>
<th class="column-title never"></th>
<% end %>
<th class="column-title none">Description</th>
</tr>
</thead>
</table>
</div>
I want to display an new column if the level of the selected project is 2. I tried to change the class from the javascript but it didn't work since I use responsive to display a childRow.
Controller :
def pagination
# Recuperation of the DataTable parameters
draw = params[:draw]
longueur = params[:length]
start = params[:start]
# Call method from model to get validated vulnerabilities
vulnerability_ids = []
#get project selection from the user
selected_project_id = get_selection
if not selected_project_id.blank?
#selected_project = Project.find(selected_project_id)
vulnerability_ids = #selected_project.validated_vulnerabilities.pluck(:id)
else
#user.projects.each {|project| vulnerability_ids += project.validated_vulnerabilities.pluck(:id)}
end
vulnerability_ids = vulnerability_ids.uniq
#vulnerabilities = Vulnerability.where(id: vulnerability_ids)
result = []
result = #vulnerabilities.offset(start.to_i).limit(longueur.to_i)
recordsTotal = #vulnerabilities.length
# creation of the json that we want to return and display
result_json = create_datatable_json(result, draw, recordsTotal)
render :json =>result_json.to_json
end
Controller Helper :
def get_selections
parameters = request.query_parameters
selected_project = parameters["project"]
return selected_project
end
def create_datatable_json(result, draw, recordsTotal)
vulns = []
result.each do |vul|
cves = []
#if there is a selected project and it is of level 2 we want to display a column with the max environmental notation
if #selected_project.nil? or not #selected_project.analysis_level == 2
max_environmental_score = "NC"
else
max_environmental_notation = EnvironmentalNotation.get_max_environmental_notation(vul, #selected_project).environmental_score
end
if vul.notation
vuln = [:id => vul.id, :cve => first_cves, :base_score => vul.notation.base_score.to_s, :description => vul.description, :environmental_score => max_environmental_score.to_s]
else
vuln = [:id => vul.id, :cve => first_cves, :base_score => "NC", :description => vul.description, :environmental_score => max_environmental_score.to_s]
end
vulns += vuln
end
result_json = {
:draw => draw,
:recordsTotal => recordsTotal,
:recordsFiltered => recordsTotal,
:data => vulns
}
return result_json
end
Javascript :
// Select project to filter the vulnerabilities to display in table
function selectProject(value) {
project = value;
Turbolinks.visit(window.location);
}
$(document).on("turbolinks:load", function() {
$('.mcs-datatable-history-external').each(function() {
if ($.fn.dataTable.isDataTable($(this))){
$(this).destroy();
}
var url = "vulnerabilities/pagination;
if (project != null) {
url = url+"/?project="+project;
$('#datatable_project').val(project);
}
add_in_array(
$(this).DataTable({
processing: true,
serverSide: true,
stateSave: true,
responsive: {
details: {
display: $.fn.dataTable.Responsive.display.childRowImmediate,
type: '',
target: 3
}
},
ajax: {
type: "POST",
format: "js",
url: url
},
columns: [
{ data: "cve", targets:0},
{ data: "base_score", targets:1, orderable: true, searchable: true},
{ data: "environmental_score", targets:2, orderable: true, searchable: false},
{ data: "description", className: "no-border-top", targets:3, orderable: false, searchable: true},
],
searching: true,
}));
})
}
I need to get #selected_project and #vulnerabilities in my view but they are nil.
I don't know if I'm clear enough. I've put a lot of code here. Don't hesitate to ask for some explanations if needed.
first make sure that the changes are saved to data base later if the instance is not reloaded then you can use
#instance_variable.reload
To reload the instance
You can use it whenever you need to display/use the updated data of instance. Say there is an instance #user and you have updated the details of user like #user.email in the db and later you need to display the email of user using that instance like #user.email. In such case the instance doesnt get reloaded and it will display the previous value.
So if you use #user.reload then rails will fetch the details of that instance from DB. so that there will be less scope for error
The app is using typeahead and bootstrap 3 for, autocomplete function.
but now we have so many records that , it is getting really slower.
var substringMatcher = function(strs) {
return function findMatches(q, cb) {
var matches, substrRegex;
// an array that will be populated with substring matches
matches = [];
// regex used to determine if a string contains the substring `q`
substrRegex = new RegExp(q, 'i');
// iterate through the pool of strings and for any string that
// contains the substring `q`, add it to the `matches` array
$.each(strs, function(i, str) {
if (substrRegex.test(str)) {
// the typeahead jQuery plugin expects suggestions to a
// JavaScript object, refer to typeahead docs for more info
matches.push({ value: str });
}
});
cb(matches);
};
};
$('.typeahead').each(function(elem) {
$(elem).typeahead({
hint: false,
highlight: true,
minLength: 3
}, {
displayKey: 'value',
source: substringMatcher($(elem).data('source'))
});
});
these are javasxript parts of typeahead fuction used by the app
on the view side of the form
<div class="form-group">
<label class="col-sm-2 control-label" for="typeahead_book">Title</label>
<div class="col-sm-3">
<input value="<%=#option.book.try(:title)%>" name="option[book_title]" type="text" class="form-control typeahead typeahead-remote-book" id="typeahead_book" data-provide="typeahead" data-items="10" data-source='<%=#book_titles%>'>
<p class="help-block"><small><%= $autocomplete_message %></small></p>
</div>
i use a callback as
before_filter :get_autocomplete_lists, only: [:new, :edit]
def get_autocomplete_lists
#book_titles = Rails.cache.fetch("booktitles", :force => true, expires_in: 10.minutes) do
Book.scoped.map(&:title)
end
#publisher_names = Rails.cache.fetch("publishernames", :force => true, expires_in: 10.minutes) do
Publisher.all.map(&:name)
end
#users_by_email_name = Rails.cache.fetch("users", :force => true, expires_in: 7.days) do
User.scoped.map{|user| "#{user.email} - #{user.name}"}
end
#country_names = Rails.cache.fetch("countrynames", :force => true, expires_in: 7.days) do
Country.all.map(&:first).sort
end
# #languages = LanguageList::COMMON_LANGUAGES.map(&:name)
# #country_names = Anatolialit::Country::List.sort
#
#languages = Rails.cache.fetch("languages", :force => true, expires_in: 7.days) do
Anatolialit::Language::EnList.sort
end
end
on controller that generates source of the book , as #book_titles ,
THE APP IS WORKING NOW but i have to refactor the code for performance issues.Because after 10k record #book_titles is very big.
THAN WHAT I DID
i created a new controller action on application controller
def book_titles
searchterm = (params[:title])
books =Book.any_of({ :title => /.*#{searchterm}.*/ })
respond_to do |format|
format.json { render json: books.map(&:title) }
end
end
and i defined a route to use as application controller as an api service
get 'booktitles/:title' => 'application#book_titles', format: :json
now when i use a localhost:3000/booktitles/the
it brings me as a json data all the books that indicates 'the' in the title.
I think everything Until here is ok. about refactoring.
but when i use this code below for userside per request for sourcing typeahead
$(document).ready(function() {
$('.typeahead-remote-book').typeahead({
source: function (query, process) {
return $.get('/booktitles', { query: query }, function (data) {
return typeahead.process(data);
});
}
});
});
and i changed the view part of the form to
<div class="form-group">
<label class="col-sm-2 control-label" for="typeahead_book">Title</label>
<div class="col-sm-3">
<input value="<%=#option.book.try(:title)%>" name="option[book_title]" type="text" class="form-control typeahead typeahead-remote-book" id="typeahead_book" data-provide="typeahead" data-items="10" >
<p class="help-block"><small><%= $autocomplete_message %></small></p>
</div>
</div>
*I do not know what is wrong with the situation , but it does not work *
could u please help me ? about solving the problem.
Thanks for your kind help..
best regards.
The problem is you're trying to do realime searching in the database and that is not very efficient. At least make sure you're indexing books on title. You should have a migration like:
class AddIndexOnBookTitles < ActiveRecord::Migration
def change
add_index :books, :title, unique: false #or omit false if dupes not allowed.
end
end
Then you don't want book objects, but just titles, which you should probably cache somewhere. We can talk about that later.
Don't use .map on an ActiveRecordRelation object but instead .pluck to just pluck the fields you need out of the DB.
def book_titles
books = Book.any_of({ :title => /.*#{params[:title]}.*/ }).pluck(:title)
respond_to do |format|
format.json { render json: books }
end
end
That still might be slow if you have a lot of books. But give that a try first and see if it's any better. If not, we'll need to do some caching or maybe use Elasticsearch which is designed for this type of thing.
I want to get the save function in the Mercury editor working but to no avail.
I have a model to save the page, title and content.
mercury.js:
$(window).bind('mercury:ready', function() {
var link = $('#mercury_iframe').contents().find('#edit_link');
Mercury.saveURL = link.data('save-url');
link.hide();
});
$(window).bind('mercury:saved', function() {
window.location = window.location.href.replace(/\/editor\//i, '/');
});
static_pages_controller.rb:
def update
#static_page = StaticPage.find(params[:id])
#static_page.page = params[:page]
#static_page.title = params[:content][:aboutContainer][:value][:about_title][:value]
#static_page.content = params[:content][:aboutContainer][:value][:about_content][:value]
#static_page.save!
render plain: ''
end
about.html.erb:
<% provide(:title, 'About') %>
<div class="container" id="aboutContainer" data-mercury="full">
<h1 id="about_title"><%= raw #static_page.title %></h1>
<div class="col-sm-12">
<p id="description about_content"><%= raw #static_page.content %></p>
</div>
<p><%= link_to "Edit Page", "/editor" + request.path, id: "edit_link",
data: {save_url: static_page_update_path(#static_page)} %></p>
</div>
Ok, so i basically realised that I needed a show action so I can grab records from the model and save to the #static_page object
I was following this guide: http://railscasts.com/episodes/296-mercury-editor?autoplay=true
Please note I had to change my routes to using those in the link (or similar routes to them) and had to place them before the default mercury routes and had to change:
#static_page.title = params[:content][:aboutContainer][:value][:about_title][:value]
#static_page.content = params[:content][:aboutContainer][:value][:about_content][:value]
to:
#static_page.title = params[:content][:about_title][:value]
#static_page.content = params[:content][:about_content][:value]
I then removed the class 'container' div in about.html.erb and moved all the code to show.html.erb not needing about.html.erb.
How to pass the search parameters in the link to print.
I have a method #index controller #TicketsController
def index
#search = current_account.searches.find_by(id: params[:search_id])
#q = policy_scope(Ticket).ransack(params[:q])
if params[:q].blank? && params[:advanced_search].blank? || params[:q]&.to_unsafe_h&.length == 1 && params[:q][:s].present?
#q.add_default_condition('status', 'in', 'open')
session[:last_ticket_search] = nil
else
session[:last_ticket_search] = {q: params[:q]}
if params[:advanced_search].present?
session[:last_ticket_search].merge!(advanced_search: 't', selected_columns: params[:selected_columns])
session[:last_ticket_search].merge!(params[:search_id].present? ? {search_id: params[:search_id]} : {commit: 'Apply'})
end
end
#selected_columns = selected_columns
#tickets = #q.result.select('tickets.*')
.includes(:account, :ticket_responses, :assignee, :tags)
.order('tickets.created_at DESC')
.paginate(page: params[:page], per_page: per_page('tickets'))
end
I need to make a page for printing all objects.
For this I made the link Print in `tickets/index.html.erb
<a class="btn btn-primary print-button" href="<%= tickets_path(variant: :print) %>" target="_blank" title="Print ticket" data-tooltip data-placement="bottom">
<i class="fa fa-print"></i>
</a>
The point is that if you click on it will open the whole list of objects here, all is well. But if I use search, I will generate the list of all objects. I need a list of objects I defined in the search. So how do I pass a search parameter in the controller to generate the print page?
I tried that.
<%= tickets_path(q: params[:q], advanced_search: params[:advanced_search], variant: :print) %>
But the controller does not share parameters and accepts a string.
need: "utf8"=>"✓", "q"=><ActionController::Parameters {"assignee_id_eq"=>"", "status_in"=>"closed", "ticket_type_eq"=>"", "simple_search"=>""}
I get: {"q"=>"assignee_id_eq=&simple_search=&status_in=closed&ticket_type_eq=", "variant"=>"print", "controller"=>"tickets", "action"=>"index"}
Just create an instance variable with your required parameters parsed into a hash which will be available in the view:
def new
#search_params = Rack::Utils.parse_nested_query params[:q]
...
end
and in the view:
<%= tickets_path(q: #search_params[:q], advanced_search: params[:advanced_search], variant: :print) %>
In my app, people can comment on pets' images. I am using the react example from here, although I changed quite a few stuff.
Right now, it is successful in displaying the existing comments. Now, when a user is creating a comment, I have to pass the comment body, user id, and pet id. I was trying to do the following:
var CommentForm = React.createClass({
handleSubmit:function()
{
var user=this.refs.user_id.getDOMNode().value.trim();
var comment=this.refs.body.getDOMNode().value.trim();
var pet_id=this.refs.pet_id.getDOMNode().value.trim();
this.props.onCommentSubmit({comment:comment, user:user, pet:pet_id});
if(!user||!comment||!pet_id)
return false;
var formData = $( this.refs.form.getDOMNode() ).serialize();
this.props.onCommentSubmit( formData, this.props.form.action );
// reset form
this.refs.body.getDOMNode().value = "";
},
render: function () {
return (
<form ref="form" className="comment-form" action={ this.props.form.action } accept-charset="UTF-8" method="post" onSubmit={ this.handleSubmit }>
<p><input type="hidden" name={ this.props.form.csrf_param } value={ this.props.form.csrf_token } /></p>
<p><input type="hidden" ref="user" value={ this.props.user_id } /></p>
<p><input type="hidden" ref="pet_id" value={ this.props.pet_id } /></p>
<p><textarea ref="body" name="comment[text]" placeholder="Say something..." /></p>
<p><button type="submit">Post comment</button></p>
</form>
)
}
});
And apparently, it doesn't look like it is passing the pet_id correctly, because I am getting the error message
ActiveRecord::RecordNotFound in CommentsController#create
Couldn't find Pet with 'id'=
My CommentsController looks like
def create
#pet = Pet.find(params[:pet_id])
#comment = #pet.comments.new(comment_params)
#comment.user = current_user
For further clarification, I have three models, Pets, Users and Comments, and when users make comments, the comment gets the user_id, and pet_id as its parameters.
edit:
My react component looks like
<%= react_component('CommentBox',
{:presenter => #presenter.to_json},
{:prerender => true}) %>
and my PetController looks like
def show
#comments = #pet.comments
#user = current_user
#presenter = {
:comments => #comments,
:user => current_user,
:pet_id => #pet,
:form => {
:action => comments_path,
:csrf_param => request_forgery_protection_token,
:csrf_token => form_authenticity_token
}
So there are a few issues I can see. Firstly your using ref where you should be name.
<input type="hidden" ref="pet_id" value={ this.props.pet_id } />
should be
<input type="hidden" name="pet_id" value={ this.props.pet_id } />
Your setting both an action and an onSubmit. Is there a reason to do this? Why not just read it from the props when you perform the ajax request? This is most likely causing your form to be submitted and the browser to load another page. The form submitting has nothing to do with what is on the server. The issue is in your client side code.
I would also consider putting your model values in to their own array. This is generally what rails expects back from the server. In your case it should be params[:pet][:id] not params[:pet_id]. Many of the rails active record methods such as update attributes can then be directly called giving you less verbose code.
#pet = Pet.find(params[:pet][:id])
#pet.update_attributes(prams[:pet])
#pet.save