In my angular-rails app I'm trying to implement a very basic live database search, such that on keyup, a results array populates or depopulates as the input value changes. Currently there are around 400 products in the database table, but there could be many more in the future. Here's my code:
On Rails side, inside products controller:
def index
#products = Product.search(params[:search]);
render json: #products
end
And my product.rb file:
def self.search(search)
where("name iLIKE ?", "%#{search}%")
end
While on the angular side, inside the relevant controller:
function MainController(dataService) {
var ctrl = this
ctrl.searchResultsArray = [];
ctrl.populateArray = function(search) {
dataService.getProductSummaries(search)
.then(function(response){
ctrl.searchResultsArray = response.data
})
};
};
Inside my dataService:
function dataService($http) {
var ctrl = this
ctrl.getProductSummaries = function(search) {
return $http({
method: 'GET',
url: '/products',
params: { search: search }
});
};
};
And inside my angular view (controlled by MainController as ctrl):
<input ng-keyup="ctrl.populateArray(ctrl.result)" ng-model="ctrl.result" />
<div ng-repeat="result in ctrl.searchResultsArray">
<li>
{{ result.name }} costs {{ result.price }}
</li>
</div>
The above code mostly works, but typing too quickly can shortcircuit it, and I occasionally see errors in my dev console, so it definitely is broken. What is it missing or doing wrong?
So, since you expect issues with too quickly type - im not sure you have real errros in console. Most likely you see canceled status code, because you've initiated new query to same url as before.
However, here is couple tricks i can advice to make your queries faster:
You can start requests only when user input 3+ symbols.
Think about .limit(n) in your rails controller. Depending on business requirements you can/cannot limit amount of returned matches from controller.
As i see you only need name and price fields of your Product model. So you can shrink response size. You can use any json builder gem, like jbuilder, or simply try something like that json: #products.select(:name, :price) - it will export only selected columns from database.
To speedup DB queries you can add index to name column.
Related
I am trying to create a form with dropdown where users can create a new object if something like "Create new" is selected, or they have the option of choosing from their previously created objects, which should prefill the form with the corresponding data.
At the moment I simply have a for each loop filling the dropdown list with the previously created objects which is outside the form, and a form that creates new objects.
How can I make this form dynamic so that when a previously selected object is selected, the form pre-fills and allows for editing?
You'd have to send out an AJAX GET request to a custom route and then have a controller action that responds to json format and returns a collection based on params passed in that GET request. Say, you are loading available car models based on a selected make:
routes:
get '/get_models/:make_id', to: 'car_makes#available_models'
controller action:
def available_models
#models = Model.where(id: ids)
respond_to do |format|
format.json { render json: #models }
end
end
AJAX request:
var fetchModels = function(makeId){
$.ajax({
method: 'GET',
url: '/get_models/' + makeId,
success: function(data) {
...
var target = $('select#car_models'); // field to which options are appended
$.each(data, function(id, model){
$(target).append(
$('<option>').text(model).attr('value', id) // populate options );
});
}
...
});
}
and then setting the AJAX request to fire on .change event in a parent select field:
$('select#car_makes').change(function(){
var makeId = $(this).val();
fetchModels(makeId);
});
There are a number of resources explaining other ways to achieve this:
https://gorails.com/episodes/jquery-ujs-and-ajax
http://railscasts.com/episodes/88-dynamic-select-menus-revised
So I ended up looping through all of the potential options for the select dropdown, and looping through the edit rails form with all the potential options, and using jQuery onchange to show / hide the correct html.
This doesn't feel like a very good solution to me, especially as it has to load the html of all the options every time.. but I don't know what else to do and it works.
If anyone has any ideas for a better solution, let me know :)
Basically, I have multiple-select box in the page. When the select box is changed, i want to put in session all id's being selected (one or more). The reason why i need something like that is following:
While creating new product user can create many variants of that product based on selected options(for example: Color and Size)(id's mentioned above).
So for example one product can have 2 variants(T-shirt in black color with size XL and green T-shirt with size L)
First of all i created POST route in products controller on which through AJAX i will send selected options:
resources :products do
collection do
post 'ajaxify_options'
end
end
Ajax code
$('#variant-options').change(function() {
var option_values = [];
$("#variant-options option:selected").each(function() {
option_values.push($(this).val());
});
$.ajax({
url: '/products/ajaxify_options',
type: 'POST',
data: ({'ids': option_values})
});
});
ajaxify_options action
def ajaxify_options (inside product controller)
set_option_values params[:ids]
head :ok, :content_type => 'text/html'
end
Here set_option_values is method in ApplicationController defined as:
def set_option_values option_values
session[:option_values] = [] if session[:option_values].nil?
session[:option_values] = option_values
end
with get method also (implemented as helper_method inside ApplicationController)
def get_option_values
return [] if session[:option_values].nil?
session[:option_values]
end
So now when user choose option (one or more) through get_option_values i can access to selected options from anywhere i need (partials for creating new product and variants).
Main-problem: When user select one or more options, through debugging i see that there is NO errors with ajax or server-side code everything is just fine and i can inspect values of selected options. But rails DO NOT reload session data immediately after AJAX call BUT only when i reload whole page.
Side-problem: Is everything fine with this code, i don't know why i think that I violate some Rails practices with populating session through ajax?
Similar question that i find is: Rails not reloading session on ajax post
I tried that solution but result is same.
I'm working on mashup that scrapes a couple of sites for data. I want to scrape and cache the data on demand rather than index the entire sites.
The first time the data is fetched, the operation can be extremely slow--at least a couple of minutes.
What's the best practice for displaying pages line-by-line like this? Is there a way to display the page dynamically, showing data as it's fetched?
Related:
How to display HTML to the browser incrementally over a long period of time?
How to create an Incremental loading webpage
How to make sure an HTML table renders incrementally
Display the result on the webpage as soon as the data is available at server
I've used jquery to allow each expensive partial to be rendered on clicking a button:
view:
#book_forecast
= link_to 'See Sales Estimates' , book_forecast_work_update_path(:work => #work), :remote => true
book_forecast.js.erb:
$( "#book_forecast" ).html( "<%= escape_javascript( render( :partial => 'works/work_update/evaluation_forecast', :locals => { :work => #work} ) ) %>" );
work_update controller:
def book_forecast
#work = Work.find(params[:work])
respond_to do | format |
format.js
end
end
works/work_update/evaluation_forecast.html.haml:
# slow-loading code
The downside is that the user has to click on a button to render each partial, but on the other hand, using jquery instead of rendering as normal means the expensive code doesn't run when the page loads.
You can also use a 'loading' icon so that the user's got something to look at whilst the heavy code runs, something like:
same view:
#loading
%h2
.notice
Loading, please wait...
js:
$(document).ready(function () {
$('#loading')
.hide()
.ajaxStart(function() {
$(this).show();
})
.ajaxStop(function() {
$(this).hide();
})
;
It looks like Rails Live Streaming is one approach. Disadvantage is that it appears to be highly web server dependent and touchy. Here's a tutorial:
http://tenderlovemaking.com/2012/07/30/is-it-live.html
class MyController < ActionController::Base
include ActionController::Live
def index
100.times {
response.stream.write "hello world\n"
}
response.stream.close
end
end
In my Rails app I'm using AngularJS and would like to display all submissions to the site that are associated to the :current_user; I use Devise.
Here is my factory:
var brainDB = angular.module('brainDB',['ngResource']).factory('userSubmission', function($resource){
var service = $resource('/api/:user_id/submissions/:id', {user_id: '#user_id'}, {id: '#id'} );
return service;
});
My Angular controller:
brainDB.controller('SubmissionsCtrl',['$scope', 'Submission', 'userSubmission',
function($scope, Submission, userSubmission){
$scope.submissions = Submission.query();
$scope.userSubmissions = userSubmission.query();
}]);
And here is relevant code from my submissions controller:
def usersubmission
#submissions = current_user.submissions
#submission = #submissions.find(params[:id])
render json: #submission
end
I have set up a route, "/api/:user_id/submissions/:id" which works just fine if you visit it in your browser with a valid user_id. However, I don't know how to tell Angular what the current_user.id is. How should I go about doing this? I've been trying all day and am desperate.
When your page is built you would have session scope already. We did something like this by simply passing session id to an app level JavaScript variable in view layout, in our case we use HAML. Something like below in head block before ng-app kicks in.
globalAppCtx = {}
globalAppCtx['user'] = JSON.parse("#{escape_javascript session[:user].to_json}");
In above code, session[:user] is ruby session with user literal containing relevant Hash that was required in ng-app. Else where, in the angular code or elsewhere in JavaScript, you can refer to globalAppCtx['user']
IMO this shouldn't be very expensive and saves you an AJAX request to get the value. I could not think of any downside of this approach.
I am not to sure i understand correctly scope in rails. Here what i am trying to do I have a model call article and called tags.
To start off the application it log you in at Article#index action which show all articles. However what I want to be able to refine my view by clicking on a tags that would dynamically be created. The query i would like would be Article.find(:all, :conditions => ['tags = ?', 'world'])
How can i achieve this? Or should i just use ajax to do this?
I think ajax makes sense in this case.
You could have something like this.
routes.rb
match "/articles/tag" => "articles#tag"
articles_controller.rb
def tag
#particularly_tagged_links = Link.find_all_by_tags(params[:tags])
end
application.js or wherever you are using jquery codes. And trig this function when the user clicks the tag or something.
function taggit(tag) {
$.ajax({
url: "articles/tags?"+tag
});
}