Explicas muy bien, pero llega un punto en que hablas muy muy rapido, en especial en esta parte que se torna un poco mas complicad.
Introducción
Todo lo que aprenderás sobre Ruby on Rails
¿Qué es Ruby on Rails y por qué usarlo?
Entorno de desarollo
Entorno de desarrollo de Ruby on Rails
Instalación de Ruby, RoR en Linux
Instalación de Ruby, RoR en Mac y Windows
Nuestra primera aplicación
Entender la web con rieles
Primero pasos con Ruby on Rails
Entender el enrutamiento básico
Manipular el patrón MVC
Los secretos de Rails
Assets y Layouts
Agregar el primer conjunto de scaffolds
Cómo funcionan las migraciones
Optimiza tu código con HAML
Agiliza la construcción de formularios con Simple Form
Soporte de varios idiomas para tu aplicación
Debugging: detecta los errores en tu código
Proyecto del curso: primeros pasos
¿Qué vamos a desarrollar?
Diseñando el modelo de datos
Construye los primeros scaffolds del proyecto
Internacionalizando los modelos
Agregando validaciones al modelo
Proyecto del curso: usuarios
Añadiendo el concepto de usuario
Asignando un propietario a la tarea
Añadiendo participantes a la tarea
Creando formularios anidados
Interactuando con Cocoon para anidar formularios
CanCan: ¿puedes hacerlo?
Proyecto del curso: interacciones
Callbacks en Rails
Añadiendo datos semilla
Enviando e-mails a los participantes
Añandiendo notas a la tarea
Añadiendo notas con AJAX
Embelleciendo nuestra aplicación
Cierre
Desplegando a Heroku
Conclusiones del curso
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
Johan Tique
Aportes 26
Preguntas 7
Explicas muy bien, pero llega un punto en que hablas muy muy rapido, en especial en esta parte que se torna un poco mas complicad.
Buenos videos, pero a modo de concejo se pudieron haber explicado mejor algunas cosas si se hubieran dividido en varios videos para explicarlos mejor
esta clase estuvo muy tesa pero a mi punto de vista si la parte del active record, y la forma como generas la relaciones y las posibilidades de modificar el nombre de las mismas si deberían tener unas dos clases mas bien enfocadas me parece super importante entender bien esta parte, a mi punto de vista esta clase quedo un poco cargada.
Para la implementación del código para esta clase ulitice la gema vanilla-nested
https://github.com/arielj/vanilla-nested
Ha resultado facil de integrar al proyecto.
# tasks.rb
class Task < ApplicationRecord
belongs_to :category
belongs_to :owner, class_name: 'User'
has_many :participating_users, class_name: 'Participant'
has_many :participants, through: :participating_users, source: :user
validates :participating_users, presence: true
validates :name, :description, presence: true
validates :name, uniqueness: { case_sensitive: false }
validate :due_date_validate
accepts_nested_attributes_for :participating_users, reject_if: :all_blank, allow_destroy: true
def due_date_validate
return if due_date.blank?
return if due_date > Date.today
errors.add :due_date, I18n.t('tasks.errors.invalid_due_date')
end
end
# create_participans.rb
class CreateParticipants < ActiveRecord::Migration[7.0]
def change
create_table :participants do |t|
t.integer :role
t.references :user, null: false, foreign_key: true, on_delete: :cascade
t.references :task, null: false, foreign_key: true, on_delete: :cascade
t.timestamps
end
end
end
# _form.html.erb
<%= simple_form_for(@task) do |form| %>
<%= form.error_notification %>
<%= form.error_notification message: form.object.errors[:base].to_sentence if form.object.errors[:base].present? %>
<div class="form-inputs">
<%= form.input :name, label: t('.name') %>
<%= form.input :description, label: t('.description') %>
<%= form.input :due_date, label: t('.due_time') %>
<%= form.association :category, label: t('categories.category') %>
</div>
<%= link_to_add_nested(form, :participating_users, '#participants', partial: 'participating_user_fields', link_text: "Agregar") %>
<div id="participants">
<%= form.fields_for :participating_users do |g|%>
<%= render 'participating_user_fields', form: g %>
<% end %>
</div>
<div class="form-actions">
<%= form.button :submit %>
</div>
<% end %>
# _participating_user_fields.html.erb
<div class="nested-fields">
<%= form.input :user_id, as: :select, collection: User.all - [ current_user ] %>
<%= form.input :role %>
<%= link_to_remove_nested(form) %>
</div>
Me parece demasiado complicado. La idea de las herramientas es agilizar el desarrollo, pero en este caso es más complicado que hacerlo a mano.
Concuerdo con Harry. Explicas muy bien pero en los últimos videos estas hablando muy rápido al punto que se vuelve confuso
Se me quitaron las ganas de aprender Rails
Excelente sus clases profesor Johan Tique. Espero tener muchos cursos de UD.
Los formularios anidados quieren decir que en un solo formulario haya interacción con dos modelos. En este caso será con Task y Participant, con la ayuda de la gema Cocoon.
Para instalar esta gema hay que agregarla en el Gemfile, lo siguiente es agregar una dependencia de Cocoon para que pueda trabajar de forma correcta, esta dependencia se agrega con yarn desde el siguiente repositorio: github:nathanvda/cocoon#c24ba53.
En el archivo eviroment.js:
const webpack = require('webpack')
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
Popper: ['popper.js', 'default'],
})
)
En el archivo application.js:
import 'cocoon'
Esta configuración es necesaria ya que cocoon tiene como dependencia a jquery.
Una vez hecho esto hacemos la migración, para migrar el modelo Participant (rails db:migrate
).
En el formulario de tasks es donde se requiere asociar con los participantes, para esto se usará simple_fields_for, un método que permite asociar un modelo anidado que exista en la estructura de modelos.
= simple_form_for(@task) do |f|
= f.error_notification
= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present?
.form-inputs
= f.input :name, label: t('tasks.name')
= f.input :description, label: t('tasks.description')
= f.input :due_date, label: t('tasks.due_date')
= f.association :category, label: t('tasks.category')
.participants
= f.simple_fields_for :paticipating_users
Con el método simple_fields_for se habilita la comunicación con participating_users, que es como se nombró la relación entre tareas y participantes.
Para el formulario principal el constructor es f (primera línea) y para el formulario anidado el constructor es g.
Para presentar el formulario anidado se usará un parcial, que es un archivo creado en la carpeta views>tasks con el nombre _participating_user_fields.html.haml. Y se llamará al formulario principal con la función render dentro del formulario anidado.
.participants
= f.simple_fields_for :paticipating_users
= render 'participating_users_fields', f: g
Parcial:
.nested-fields
= f.input :user_id, as: :select, collections: User.all - [current_user]
= f.input :role
En el primer input se inserta un elemento select, para poder elegir entre todos los usuarios, pero le restamos el usuario actual para que no aparezca en la lista. Era necesario poner current_user en un arreglo para que Ruby pudiera realizar las operaciones entre arreglos con User.all (también es un arreglo) sin problemas.
Para los que estan llevando el curso en la actualidad, si presentan algun problema con respecto al boton de “Agregar un participante” y ya revisaron todo el codigo y no tienen ningun error, puede que el problema sea por las dependencias en el package.json
Me lanzaba un error de que no era posible cargar @popper.js/core
Y al final el errror es que bootstrap en su version 5 deja de usar jquery, para ello deben de asegurarse de tener el package.json com se los dejo adelante
Solo escriben las versiones que tengo y ejecutan
- yarn install para descargar todas las dependencias
"dependencies": {
"@rails/actioncable": "^6.0.0",
"@rails/activestorage": "^6.0.0",
"@rails/ujs": "^6.0.0",
"@rails/webpacker": "5.4.3",
"bootstrap": "^4.5.3",
"cocoon": "github:nathanvda/cocoon#c24ba53",
"jquery": "^3.5.1",
"popper.js": "^1.16.1",
"roboto-fontface": "^0.10.0",
"turbolinks": "^5.2.0",
"webpack": "^4.46.0",
"webpack-cli": "^3.3.12"
},
Hola a todos, disculpen, tengo una duda de un problema que tengo con la parte de “Agregar participante”, es algo que ya comentaron anteriormente pero no me ha funcionado nada de ello.
Adjuntaré mi código para un mejor entendimiento. Cabe recalcar que la consola del explorador no me marca nada y la terminan donde está ejecutándose el servidor solo menciona que se ha renderizado:
_form.html.haml
-# frozen_string_literal: true
= simple_form_for(@task) do |f|
= f.error_notification
= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present?
.form-inputs
= f.input :name, label: t('.name')
= f.input :description, label: t('.description')
= f.input :due_date, label: t('.due_date')
= f.association :category, label: t('.category')
#addParticipants
= link_to_add_association f, :participating_users, 'data-association-insertion-node' => '.participants .participants-container', 'data-turbolinks' => false do
Agregar participante
.participants
= f.simple_fields_for :participating_users do |g|
= render 'participating_user_fields', f: g
.participants-container
.form-actions
= f.button :submit
_participating_user_fields.html
.nested-fields
= f.input :user_id, as: :select, collection: User.all - [current_user]
= f.input :role
task.rb
class Task < ApplicationRecord
belongs_to :category
belongs_to :owner, class_name: 'User'
has_many :participating_users, class_name: 'Participant'
has_many :participants, through: :participating_users, source: :user
validates :participating_users, presence: true
validates :name, :description, presence: true
validates :name, uniqueness: { case_insensitive: false }
validate :due_date_validity
accepts_nested_attributes_for :participating_users, allow_destroy: true
def due_date_validity
return if due_date.blank?
return if due_date > Date.today
end
end
Si me gustaria que me ayudaras al llegar aqui
#addParticipants
= link_to_add_association f, ':participating_users', 'data-association-insertion-node' => '.participants .participants-container', 'data-turbolinks' => false do
agregar
me sale error
Encountered a syntax error while rendering template: check -# frozen_string_literal: true = simple_form_for(@task) do |f| = f.error_notification = f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? .form-inputs = f.input :name, label: t(’.name’) = f.input :description, label: t(’.description’) = f.input :due_date, label: t(’.due_data’) = f.association :category #addParticipants = link_to_add_association f, ‘:participating_users’, ‘data-association-insertion-node’ => ‘.participants .participants-container’, ‘data-turbolinks’ => false do agregar .participants = f.simple_fields_for :participating_users do |g| = render ‘participating_user_fields’, f: g .participants-container .form-actions = f.button :submit
la explicación muy deficiente no se si es el afán por acabar los vídeos rápido pero parecía una metralleta hablando y llega un punto donde ya es imposible seguirlo
Bueno. Esto de verdad me quito sueño. Pensé que lo había resuelto simplemente colocando esta validación en el modelo Participant:
validates_uniqueness_of :user_id,scope: [:task_id], allow_blank: false, message: "participants cant be repeated in same task"
Pero dejaba pasar a los usuarios y no entendía el porque. Me puse a debugguear y encontré que como en el momento de correr la validación que añadí todavía no se ha seteado el task_id por lo que al momento de la validación lo que tiene es:
user_id: 1
task_id: nil
Por lo que la validación pasa ya que no existe ningun paritcipant con esos valores. Investigue mucho y lo que encontré que sirvió fue crear una migración para hacer la validación desde la base de datos.
class AddConstrainToParticipant < ActiveRecord::Migration[6.1]
def change
add_index :participants, [:user_id, :task_id], unique: true
end
end
De esta manera manejo el error directamente en la base de datos. Por lo que cuando se inserte los datos erroneos se hará un rollback en la base de datos y se activará una excepcion que estoy manejando en el task_controller.
rescue_from ActiveRecord::RecordNotUnique do |exception|
flash[:alert] = "participants cant be repeated in the same task"
redirect_to new_task_path
end
Esa es la validación que coloqué en el taskt_controller donde seteo un mensaje y redirijo la vista nuevamente a crear un nuevo participante.
En la vista (_form.html.haml de taskts) hay que agregar el flash. Yo lo hice así:
= flash[:alert] if flash[:alert].present?
De esa manera aparece un mensaje con el error y se tienen que vovler a agregar todos los datos.
Si alguien puede comentarme como lo hizo ojalá exista una manera menos rebuscada.
Gracias a todos por los comentarios y preguntas! Obtuve varios errores pero luego los encontré leyendo cada uno de los comentarios. 💪🏽
No se si depronto les pasa que al hacer el render no encuentra el modulo jquery. Lo unico que hice fue agregarlo por consola con yarn
yarn add jquery
Despeues lo agregué al app/javascript/packs/application.js
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
//jquery
require("jquery")
import 'cocoon'
Para el reto propuesto por el profesor esta es mi solución:
En el modelo app\models\task.rb agregar la validación
validate :uniqueness_participant
def uniqueness_participant
users = participating_users.map{|p| p[:user_id] }
return if users.uniq.length == users.length
errors.add :base, I18n.t('task.errors.repeated_participant')
end
En la parte final menciona que en la implementación actual es posible añadir muchos participantes y a estos añadirles al mismo usuario. No entendí muy bien esa parte. Creí que en la parte donde restábamos -[current_user] se resolvía eso. Tal vez entendí mal, por eso me atrevo a preguntar si es a eso a lo que se refiere. A no permitirle que se agregue el usuario actual porque eso no tendría sentido.
Tengo un detalles cuando le doy click a participantes no me muestra los campos y no busco que paso la consola no me marca error
Buenos dias. Revise todas las preguntas para ver si conseguia solucionar el error que al clickear en ‘agregar participantes’ no me aparece nada pero aun sigo sin solucionarlo.
task.rb
# == Schema Information
#
# Table name: tasks
#
# id :bigint not null, primary key
# name :string
# description :text
# due_date :date
# category_id :bigint not null
# created_at :datetime not null
# updated_at :datetime not null
#
class Task < ApplicationRecord
belongs_to :category
belongs_to :owner, class_name: 'User'
#Relaciones
has_many :participating_users, class_name: 'Participant'
has_many :participants, through: :participating_users, source: :user
#Las tareas siempre tienen participantes
validates :participating_users, presence: true
#Estas columnas deben existir para poder guardarse
validates :name, :description, presence: true
#Debe ser unico, no puede haber dos categorias con el mismo nombre
validates :name, uniqueness: {case_sensitive: false}
validate :due_date_validity
#Para que el modelo acepte atributos anidados
accepts_nested_attributes_for :participating_users, allow_destroy: true
def due_date_validity
return if due_date.blank?
return if due_date > Date.today
errors.add :due_date, I18n.t('taks.errors.invalid_due_date')
end
end
metodo task_params de tasks_controller.rb
# Only allow a list of trusted parameters through.
def task_params
params.require(:task).permit(
:name,
:description,
:due_date,
:category_id,
participating_users_attributes: [
:user_id,
:role,
:id,
:_destroy
]
)
end
_form.html.haml
-# frozen_string_literal: true
= simple_form_for(@task) do |f|
= f.error_notification
= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present?
.form-inputs
= f.input :name, label: t('.name')
= f.input :description, label: t('.description')
= f.input :due_date, label: t('.due_date')
= f.association :category, label: t('.category')
#addParticipants
= link_to_add_association f, :participating_users, 'data-association-insertion-node' => '.participants .participants-container', 'data-turbolinks' => false do
Agregar un participante
.participants
= f.simple_fields_for :participating_users do |g|
= render 'participating_user_fields', f: g
.participants-container
.form-actions
= f.button :submit
_participating_user_fields
.nested-fields
= f.input :user_id, as: :select, collection: User.all - [current_user]
= f.input :role
Tengo algún error aquí, no se despliegan los campos al hacer click en agregar participante sin embargo no muestra ningún error en el log del servidor ni en el navegador, pero al hacer click en Crear Task muestra lo siguiente en la consola:
…
Aquí les dejo mi código:
task.rb
class Task < ApplicationRecord
belongs_to :category
belongs_to :owner, class_name: 'User'
has_many :participating_users, class_name: 'Participant'
has_many :participants, through: :participating_users, source: :user
validates :participating_users, presence: true
validates :name, :description, presence: true
validates :name, uniqueness: { case_sensitive: false }
validate :due_date_validity
accepts_nested_attributes_for :participating_users, allow_destroy: true
def due_date_validity
return if due_date.blank?
return if due_date > Date.today
errors.add :due_date, I18n.t('task.errors.invalid_due_date')
end
end
tasks_controller.rb
class TasksController < ApplicationController
before_action :set_task, only: [:show, :edit, :update, :destroy]
# GET /tasks
# GET /tasks.json
def index
@tasks = Task.all
end
# GET /tasks/1
# GET /tasks/1.json
def show
end
# GET /tasks/new
def new
@task = Task.new
end
# GET /tasks/1/edit
def edit
end
# POST /tasks
# POST /tasks.json
def create
@task = Task.new(task_params)
@task.owner = current_user
respond_to do |format|
if @task.save
format.html { redirect_to @task, notice: 'Task was successfully created.' }
format.json { render :show, status: :created, location: @task }
else
format.html { render :new }
format.json { render json: @task.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /tasks/1
# PATCH/PUT /tasks/1.json
def update
respond_to do |format|
if @task.update(task_params)
format.html { redirect_to @task, notice: 'Task was successfully updated.' }
format.json { render :show, status: :ok, location: @task }
else
format.html { render :edit }
format.json { render json: @task.errors, status: :unprocessable_entity }
end
end
end
# DELETE /tasks/1
# DELETE /tasks/1.json
def destroy
@task.destroy
respond_to do |format|
format.html { redirect_to tasks_url, notice: 'Task was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_task
@task = Task.find(params[:id])
end
# Only allow a list of trusted parameters through.
def task_params
params.require(:task).permit(
:name,
:description,
:due_date,
:category_id,
participating_users_attributes:
[:user_id,
:role,
:id,
:destroy]
)
end
end
_form.html.haml
= simple_form_for(@task) do |f|
= f.error_notification
= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present?
.form-inputs
= f.input :name
= f.input :description
= f.input :due_date
= f.association :category
#addParticipants
= link_to_add_association f, :participating_users, 'data-association-insertion-mode' => '.participants participants-container', 'data-turbolinks' => false do
agregar participante
.participants
= f.simple_fields_for :participating_users do |g|
= render 'participating_user_fields', f: g
.participants-container
.form-actions
= f.button :submit
_participating_user_fields.html.haml
.nested-fields
= f.input :user_id, as: :select, collection: User.all - [current_user]
= f.input :role
Agradecido de antemano por cualquier ayuda que me puedan dar para solventar este error.
buenas noches estoy teniendo el error que los participantes no se despliegan.
en aplication.js
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
import Rails from "@rails/ujs"
import Turbolinks from "turbolinks"
import * as ActiveStorage from "@rails/activestorage"
import "channels"
import 'cocoon'
Rails.start()
Turbolinks.start()
ActiveStorage.start()
/*
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require('cocoon')
*/
prove de ambas formas como sugirio el coma#ero y no funciono
esta es mi version del tasks.rb de models
# == Schema Information
#
# Table name: tasks
#
# id :bigint not null, primary key
# name :string
# description :text
# due_date :date
# category_id :bigint not null
# created_at :datetime not null
# updated_at :datetime not null
# owner_id :bigint not null
# code :string
#
class Task < ApplicationRecord
belongs_to :category
belongs_to :owner, class_name: 'User'
has_many :participating_users, class_name: 'Participant'
has_many :participants, through: :participating_users, source: :user
has_many :notes
validates :participating_users, presence: true
validates :name, :description, presence: true
validates :name, uniqueness: {case_insensitive: false}
validate :due_date_validity
before_create :create_code
after_create :send_email
accepts_nested_attributes_for :participating_users, allow_destroy: true
def due_date_validity
return if due_date.blank?
return if due_date >= Date.today
errors.add :due_date, I18n.t('task.errors.invalid_due_date')
end
def create_code
code = "#{owner_id}#{Time.now.to_i.to_s(36)}#{SecureRandom.hex(8)}"
end
def send_email
(participants + [owner]).each do |user|
ParticipantMailer.with(user: user, task: self).new_task_email.deliver!
end
end
end
en cuanto a las vistas tengo:
_participating_user_fields.html.haml
.nested-fields
= f.input :user_id, as: :select, collection: User.all - [current_user]
= f.input :role
_form.html.haml
-# frozen_string_literal: true
= simple_form_for(@task) do |f|
= f.error_notification
= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present?
.form-inputs
= f.input :name
= f.input :description
= f.input :due_date
= f.association :category
#addParticipants
= link_to_add_association f, :participating_users, 'data-association-insertion-node' => '.participants .participants-container' , 'data-turbolinks' =>false do
add participant
.participants
= f.simple_fields_for :participating_users do |g|
= render 'participating_user_fields', f: g
.participants-container
.form-actions
= f.button :submit
sigo sin encontrar la razon del error, mi suposicion es que es un problema de identacion.
Tengo el mismo error de no poder mostrar los users en Agregar participante y ya lei todos los comentarios. En verdad no se que hacer lol
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?