I just started to learn elixir and phoenix.
I am trying to add comments to blog but i got this error
function Map.put/4 is undefined or private
Error is in this line (show.html.heex):
<%= render "comment_form.html", Map.put(assigns, :changeset, :action, Routes.post_post_path(#conn, :add_comment, #post)) %>
My code in post_controller.ex
def show(conn, %{"id" => id}) do
post =
id
|> Posts.get_post!
|> Repo.preload([:comments])
changeset = Comment.changeset(%Comment{}, %{})
render(conn, "show.html", post: post, changeset: changeset)
end
And comment_form.html.heex
<%= form_for #changeset, #action, fn f -> %>
<div class="form-group">
<label>Name</label>
<%= text_input f, :name, class: "form-control" %>
</div>
<div class="form-group">
<label>Content</label>
<%= textarea f, :content, class: "form-control" %>
</div>
<div class="form-group">
<%= submit "Add comment", class: "btn btn-primary" %>
</div>
<% end %>
You may have accidently added :changeset in the code:
- Map.put(assigns, :changeset, :action, Routes.post_post_path(#conn, :add_comment, #post))
+ Map.put(assigns, :action, Routes.post_post_path(#conn, :add_comment, #post))
Map.put/3 can put a new key-value pair into an existing map. Here 3 means the arity (the number of arguments) is 3.
In your case, it makes little sense to put a new assign of :changeset into the inherited assigns which already contain that key.
Related
I have a app about hotel, I have a model Room, RoomType:
room.rb
class Room < ApplicationRecord
enum status: {freeing: true, using: false}
belongs_to :room_type
room_type.rb
class Room < ApplicationRecord
has_many :rooms
I want to search room through roomtype and I did in filter_form:
<%= form_tag admin_rooms_path, method: :get do %>
<div class="form-group">
<%= label_tag t(".room_directory") %>
<%= text_field_tag :number_or_room_types_name_cont,
params[:number_or_room_types_name_cont],
class: "form-control" %>
</div>
or I try to work:
<div class="col-md-3">
<div class="form-group">
<%= label_tag t(".room_type") %>
<%= select_tag :room_types_name_cont, options_for_select(#supports.room_types, params[:room_types_name_cont]),
{include_blank: true, class: "form-control"} %>
</div>
and in room_support.rb:
def room_types
#room_types ||= RoomType.all.map{|room_type| [room_type.name,
room_type.id]}
end
controller I have a search_params:
def search_params
params.permit :number_or_room_types_name_cont,
:status_eq, :max_people_gteq
end
But it not work for me. Hope that everyone helpe me!
Thanks so much.
I have two models, Parent is Property, child is Phone. When attempting to create a new Property record with nested Phone data, I receive an error message: Phones property must exist.
I've studied the Rails Guide and a number of other documents without determining the cause. Here is a public github link if you want to see all the code: https://github.com/allenroulston/testnest.git
class Property < ApplicationRecord
has_many :phones
accepts_nested_attributes_for :phones
end
class Phone < ApplicationRecord
belongs_to :property
end
# the form accepting the data
<%= form_for(property) do |f| %>
<% if property.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(property.errors.count, "error") %> prohibited this property from being saved:</h2>
<ul>
<% property.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :address %>
<%= f.text_field :address %>
</div>
<div class="field">
<%= f.label :city %>
<%= f.text_field :city %>
</div>
<div class="field">
<%= f.label "Telephone (example: 613 555 1234 )" %>
<%= f.fields_for :phones do |p| %>
Area Code <%= p.text_field :area %>
Exchange <%= p.text_field :exchange %>
Number <%= p.text_field :number %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
# relevant controller methods ##################
# GET /properties/new
def new
#property = Property.new
#property.phones.build
end
# POST /properties
# POST /properties.json
def create
#property = Property.new(property_params)
respond_to do |format|
if #property.save
format.html { redirect_to #property, notice: 'Property was successfully created.' }
format.json { render :show, status: :created, location: #property }
else
format.html { render :new }
format.json { render json: #property.errors, status: :unprocessable_entity }
end
end
end
As far as I know, this is because when you use nested_attributes_for when creating a new object, the parent object is not yet created, so when attempting to create a reference to the parent object, the validation fails. To fix this you should change to: has_many :phones, inverse_of: :property.
You need to add in the Property model as :-
accepts_nested_attributes_for :phones, reject_if: :all_blank, allow_destroy: true
I'm trying to change the user password using the gem bcrypt and the hash-salt method.
Here's my code where i include my attempt to change password, but it gives me an error of a missing template.
User Controller
def create
#user = User.new(user_params)
end
def change_password
#user = User.find(params[:id])
if #user.password_hash == BCrypt::Engine.hash_secret(params[:current_password], #user.password_salt)
#user.password = params[:password]
#user.save
redirect_to "/users/#{#user.id}"
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
User Model
before_save :encrypt_password
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
return user
else
return nil
end
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
also, the Routes
patch 'users/:id/change_password' => 'users#change_password'
resources :users
and last but not less important, the form.
<%= form_for(#user, :url => "change_password") do |f| %>
<%= hidden_field(:user, :email, :value => #user.email) %>
<div class="form-group">
<div class="form-group col-md-4"><%= f.label :contraseña_actual %></div>
<div class="form-group col-md-8"><%= f.password_field(:current_password, :class => "form-control") %></div>
</div>
<div class="form-group col-md-4"><%= f.label :nueva_contraseña %></div>
<div class="form-group col-md-8"><%= f.password_field(:password, :class => "form-control") %></div>
<div class="form-group">
<div class="form-group col-md-4"><%= f.label :confirmar_contraseña %></div>
<div class="form-group col-md-8"><%= f.password_field(:password_confirmation, :class => "form-control") %></div>
</div>
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-default">Cambiar Contraseña</button>
</div>
<% end %>
My logic:
/* GET /events/list */
router.get('/events/list', function(req, res) {
new db.Tag({})
.fetchAll()
.then(function(tags) {
res.locals.title = "List of events";
res.locals.tags = tags;
res.render('events/list.ejs');
});
});
My view:
<% for (var tag in tags) { %>
<div class="checkbox">
<label>
<input type="checkbox" data-tag-id="<%= tag.tagId %>" />
<%= tag.name %>
</label>
</div>
<% } %>
What I'm getting:
[x] undefined
[x] undefined
[x] undefined
[x] undefined
What I should be getting:
[x] foo
[x] bar
[x] zort
[x] troz
I also tried passing
res.locals.tags = tags.toJSON();
and also
res.locals.tags = JSON.stringify(tags);
So.. how do I finally pass my collection to an EJS view?
I also logged (console.log(tags)) just after then(function(tags) and I'm getting the models (tags in this case) correctly.
I also tried tags.forEach in my EJS view but a native javascript array like this: [{tagId:1, name:"blah"}, {tagId:2, name"Foo"}] doesn't have "forEach" method implemented
In your template, use forEach (if available) or loop using the index instead.
server.js
res.locals.tags = tags.toJSON();
view.html (with [].forEach)
<% tags.forEach(function(tag) { %>
<div class="checkbox">
<label>
<input type="checkbox" data-tag-id="<%= tag.tagId %>" />
<%= tag.name %>
</label>
</div>
<% }) %>
view.html (with indices)
<% for (var i in tags) { %>
<div class="checkbox">
<label>
<input type="checkbox" data-tag-id="<%= tags[i].tagId %>" />
<%= tags[i].name %>
</label>
</div>
<% } %>
Here's an alternative to using Array.prototype.forEach. You give the Bookshelf collection to the view. Bookshelf.Collection has its own .forEach:
server.js
res.locals.tags = tags; // NOT .toJSON()
view.html
<% tags.forEach(function(tag) { %>
<div class="checkbox">
<label>
<input type="checkbox" data-tag-id="<%= tag.id %>" />
<%= tag.get('name') %>
</label>
</div>
<% }) %>
I am having a bit off difficulty implementing the ransack gem I have a pages controller with an index action and post controller also with an index action. However when I perform a search in the index action of the pages controller (pages#index) the results are rendered in the index action of the post controller(posts#index). Does this mean i can only search from within the views of a particular resource or am I making a mistake ?
pages#index
def index
#q = Post.search(params[:q])
#posts = #q.result(distinct: true)
end
pages#index
<%= search_form_for #q do |f| %>
<div class="field">
<%= f.label :title_cont %><br>
<%= f.text_field :title_cont, :class => "input text" %>
</div>
<div class="actions">
<%= f.submit "Search"%>
</div>
<% end %>
<%= search_form_for #q,:url=>pages_path do |f| %>
<div class="field">
<%= f.label :title_cont %><br>
<%= f.text_field :title_cont, :class => "input text" %>
</div>
<div class="actions">
<%= f.submit "Search"%>
</div>
<% end %>
Modify the code in the search form like this. It will work now.