Recipe Editor

This commit is contained in:
Dan Elbert 2016-01-14 18:56:45 -06:00
parent 3b4134f684
commit 0388d428a6
12 changed files with 98 additions and 43 deletions

View File

@ -1,16 +1,24 @@
(function($) { (function($) {
function reorder($container) { function reorder($container) {
console.log($container);
$container.find("div.nested-fields").each(function(idx, editor) { $container.find("div.nested-fields").each(function(idx, editor) {
var $editor = $(editor); var $editor = $(editor);
$editor.find('input.sort-order').val(idx + 1).trigger("changed"); $editor.find('input.sort_order').val(idx + 1).trigger("changed");
}) })
} }
function initializeIngredientEditor($container, ingredientSearchEngine) { function initializeIngredientEditor($container, ingredientSearchEngine) {
$container.find(".ingredient-typeahead").typeahead({ // $container is either an element that contains many editors, or a single editor.
var $editors = $container.find(".ingredient-typeahead").closest(".nested-fields");
}, $editors.each(function(idx, elem) {
var $editor = $(elem);
var $ingredientId = $editor.find("input.ingredient_id");
var $customDensity = $editor.find("input.custom_density");
var $group = $editor.find("div.typeahead-group");
$editor.find(".ingredient-typeahead").typeahead({},
{ {
name: 'ingredients', name: 'ingredients',
source: ingredientSearchEngine, source: ingredientSearchEngine,
@ -18,6 +26,45 @@
return datum.name; return datum.name;
} }
}); });
if ($ingredientId.val().length) {
$customDensity.prop('disabled', true);
$group.addClass("has-success");
}
});
}
function ingredientItemPicked($typeahead, datum) {
var $container = $typeahead.closest(".nested-fields");
var $ingredientId = $container.find("input.ingredient_id");
var $customDensity = $container.find("input.custom_density");
var $group = $container.find("div.typeahead-group");
$ingredientId.val(datum.id);
$typeahead.typeahead('val', datum.name);
$customDensity.val(datum.density).prop('disabled', true);
$group.addClass("has-success");
}
function ingredientNameChange($typeahead, ingredientSearchEngine) {
var $container = $typeahead.closest(".nested-fields");
var $ingredientId = $container.find("input.ingredient_id");
var $customDensity = $container.find("input.custom_density");
var $group = $container.find("div.typeahead-group");
var id = $ingredientId.val();
var value = $typeahead.typeahead('val');
if (id && id.length) {
var found = ingredientSearchEngine.get([id]);
if (found && found[0] && found[0].name != value) {
// User has chosen something custom
$ingredientId.val('');
$customDensity.val('').prop('disabled', false);
$group.removeClass("has-success");
}
}
} }
$(document).on("ready page:load", function() { $(document).on("ready page:load", function() {
@ -66,11 +113,23 @@
$ingredientList $ingredientList
.on("cocoon:after-insert", function(e, item) { .on("cocoon:after-insert", function(e, item) {
reorder($(this)); reorder($ingredientList);
initializeIngredientEditor(item, ingredientSearchEngine); initializeIngredientEditor(item, ingredientSearchEngine);
}) })
.on("cocoon:after-remove", function(e, item) { .on("cocoon:after-remove", function(e, item) {
reorder($(this)); reorder($ingredientList);
})
.on("typeahead:change", function(evt, value) {
console.log("changed");
ingredientNameChange($(evt.target), ingredientSearchEngine);
})
.on("typeahead:select", function(evt, value) {
console.log("selected");
ingredientItemPicked($(evt.target), value);
})
.on("typeahead:autocomplete", function(evt, value) {
console.log("autocomplete");
ingredientItemPicked($(evt.target), value);
}); });
}); });

View File

@ -3,7 +3,6 @@
// You can use Sass (SCSS) here: http://sass-lang.com/ // You can use Sass (SCSS) here: http://sass-lang.com/
@mixin editor { @mixin editor {
@extend .panel-body;
} }
div.ingredient-editor { div.ingredient-editor {

View File

@ -1,5 +1,5 @@
class IngredientsController < ApplicationController class IngredientsController < ApplicationController
before_action :set_ingredient, only: [:show, :edit, :update, :destroy] before_action :set_ingredient, only: [:edit, :update, :destroy]
# GET /ingredients # GET /ingredients
# GET /ingredients.json # GET /ingredients.json
@ -7,11 +7,6 @@ class IngredientsController < ApplicationController
@ingredients = Ingredient.all.order(:name) @ingredients = Ingredient.all.order(:name)
end end
# GET /ingredients/1
# GET /ingredients/1.json
def show
end
# GET /ingredients/new # GET /ingredients/new
def new def new
@ingredient = Ingredient.new @ingredient = Ingredient.new

View File

@ -6,4 +6,20 @@ class RecipeIngredient < ActiveRecord::Base
validates :sort_order, presence: true validates :sort_order, presence: true
validates :custom_density, density: true, allow_blank: true validates :custom_density, density: true, allow_blank: true
def custom_name
if self.ingredient_id.present?
self.ingredient.name
else
super
end
end
def custom_density
if self.ingredient_id.present?
self.ingredient.density
else
super
end
end
end end

View File

@ -5,7 +5,6 @@
<%= render 'form' %> <%= render 'form' %>
<%= link_to 'Show', @ingredient, class: 'btn btn-primary' %> |
<%= link_to 'Back', ingredients_path, class: 'btn btn-primary' %> <%= link_to 'Back', ingredients_path, class: 'btn btn-primary' %>
</div> </div>

View File

@ -16,10 +16,8 @@
<tbody> <tbody>
<% @ingredients.each do |ingredient| %> <% @ingredients.each do |ingredient| %>
<tr> <tr>
<td><%= ingredient.name %></td> <td><%= link_to ingredient.name, edit_ingredient_path(ingredient) %></td>
<td><%= ingredient.density %></td> <td><%= ingredient.density %></td>
<td><%= link_to 'Show', ingredient %></td>
<td><%= link_to 'Edit', edit_ingredient_path(ingredient) %></td>
<td><%= link_to 'Destroy', ingredient, method: :delete, data: { confirm: 'Are you sure?' } %></td> <td><%= link_to 'Destroy', ingredient, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr> </tr>
<% end %> <% end %>

View File

@ -1,13 +0,0 @@
<div class="row">
<div class="col-xs-12">
<h3><%= @ingredient.name %></h3>
<p><%= @ingredient.notes %></p>
<%= link_to 'Edit', edit_ingredient_path(@ingredient), class: 'btn btn-default' %>
<%= link_to 'Back', ingredients_path, class: 'btn btn-default' %>
</div>
</div>

View File

@ -1 +0,0 @@
json.extract! @ingredient, :id, :created_at, :updated_at

View File

@ -1,12 +1,12 @@
<div class="panel panel-default"> <div class="panel panel-default nested-fields ingredient-editor">
<div class="nested-fields ingredient-editor"> <div class="panel-body">
<div class="row"> <div class="row">
<div class="col-xs-10"> <div class="col-xs-10">
<div class="form-group form-group-sm"> <div class="form-group form-group-sm typeahead-group">
<%= f.label :custom_name, "Name" %> <%= f.label :custom_name, "Name" %>
<%= f.text_field :custom_name, class: 'form-control ingredient-typeahead' %> <%= f.text_field :custom_name, class: 'form-control ingredient-typeahead custom_name' %>
</div> </div>
<div class="row"> <div class="row">
@ -27,7 +27,7 @@
<div class="col-xs-4"> <div class="col-xs-4">
<div class="form-group form-group-sm"> <div class="form-group form-group-sm">
<%= f.label :custom_density, "Density" %> <%= f.label :custom_density, "Density" %>
<%= f.text_field :custom_density, class: 'form-control' %> <%= f.text_field :custom_density, class: 'form-control custom_density' %>
</div> </div>
</div> </div>
</div> </div>
@ -40,6 +40,7 @@
</div> </div>
</div> </div>
<%= f.hidden_field :sort_order, class: 'sort-order' %> <%= f.hidden_field :ingredient_id, class: 'ingredient_id' %>
<%= f.hidden_field :sort_order, class: 'sort_order' %>
</div> </div>
</div> </div>

View File

@ -19,7 +19,7 @@
</div> </div>
</div> </div>
<%= f.hidden_field :sort_order, class: 'sort-order' %> <%= f.hidden_field :sort_order, class: 'sort_order' %>
</div> </div>
</div> </div>

View File

@ -1,7 +1,8 @@
Rails.application.routes.draw do Rails.application.routes.draw do
resources :recipes resources :recipes
resources :ingredients do
resources :ingredients, except: [:show] do
collection do collection do
constraints format: 'json' do constraints format: 'json' do
get :search get :search

View File

@ -10,6 +10,7 @@
puts "Seeding..." puts "Seeding..."
Ingredient.create!([ Ingredient.create!([
{name: 'Water', density: '1 g/ml'},
{name: 'Butter, Salted', density: '226 gram/cup'}, {name: 'Butter, Salted', density: '226 gram/cup'},
{name: 'Butter, Unsalted', density: '226 gram/cup'}, {name: 'Butter, Unsalted', density: '226 gram/cup'},
{name: 'Flour, Bleached All Purpose', density: '130 gram/cup'}, {name: 'Flour, Bleached All Purpose', density: '130 gram/cup'},