I'm trying to get the upgrade the example shown in this blog post
https://medium.com/#mitch_23203/the-exact-same-app-in-hyperstack-7f281cef46ca
to use hyperstack models like the followup blog post, but it just doesn't work.
Here is my code after I changed it:
class TodoIndex < HyperComponent
def create_new_todo_item
Todo.create(title: #todo)
#title = nil
end
render(DIV, class: 'ToDo') do
IMG(class: 'Logo', src: 'assets/logo.png', alt: 'Hyperstack Logo')
H1(class: 'ToDo-Header') { 'Hyperstack To Do' }
DIV(class: 'ToDo-Container') do
DIV(class: 'ToDo-Content') do
Todo.each do |item|
TodoItem(key: item, item: item.title)
.on(:delete_item) { item.destroy }
end
end
DIV do
INPUT(type: :text, value: #title)
.on(:change) { |e| mutate #title = e.target.value }
.on(:enter) { create_new_todo_item }
BUTTON(class: 'ToDo-Add') { '+' }
.on(:click) { create_new_todo_item }
end
end
end
end
Everything seems to work fine, and new Todo's are saved but the attributes are nil. Like the title doesn't get saved.
It should be:
def create_new_todo_item
Todo.create(title: #title) # not #todo
#title = nil
end
You set the title of Todo to its state value (#todo)...
Related
What I want to come true
I am creating an app. View is Nuxt and server is Rails.
I can't register the text as I want.
I am registering book information.
There are multiple pieces of information that I want to register, and the content and status of post_items_attributes are multiple, but only one can be registered. What should I do in this case?
If you set it to 0, you can only register one, so I don't know what to do.
this.$axios.$post(url.POST_API + 'posts', {
post: {
title: this.$title(selectedBook),
author: this.$author(selectedBook),
image: this.$image(selectedBook),
post_items_attributes: [{
/////////////////////////////////////////////////
content: list[0].content,
status: list[0].status
////////////////////////////////////////////////
}]
}
})
.then((responseBook) => {
context.commit('book/userBook', responseBook)
context.commit('book/clearBook')
context.commit('todos/clear')
})
},
this.$store.state.todos.list
export const state = () => ({
list: []
})
In list
Code
Nuxt
const list = context.state.todos.list
const selectedBook = context.state.book.selectedBook
// plugin/bookInfo $title,$author,$image
this.$axios.$post(url.POST_API + 'posts', {
// send to Rails
post: {
title: this.$title(selectedBook),
author: this.$author(selectedBook),
image: this.$image(selectedBook),
post_items_attributes: [{
/////////////////////////////////////////////////////////////
content: list[0].content,
status: list[0].status
////////////////////////////////////////////////////////////
}]
}
})
.then((responseBook) => {
context.commit('book/userBook', responseBook)
context.commit('book/clearBook')
context.commit('todos/clear')
})
},
Rails
def create
posts = Post.new(post_params)
if posts.save
render json: 'OK', status: 200
else
render json: 'EEEOR', status: 500
end
end
private
def post_params
params.require(:post).permit(:title, :author, :image, post_items_attributes: [:id, :content, :status])
end
You don't give example of list in the question code. But from the context, I conclude that this is an array of JS objects. So you can map it like this:
let post_items_attributes =
list.map(item => {
return {
content: item.content,
status: item.status,
}
})
And then pass it in the post:
post: {
title: this.$title(selectedBook),
author: this.$author(selectedBook),
image: this.$image(selectedBook),
post_items_attributes: post_items_attributes
}
Or you can do it inline without this variable
I'm trying to build an app that allows users to share quotes by artists about other artists. For instance, a quote by Bob Dylan about John Lennon. As such, my Artist model is set up in a way that allows an artist to be both the Speaker and Topic on a Quote, and each Quote belongs_to each Artist as the Speaker or Topic.
I'm having trouble getting a Rails error message to display inside a Bootstrap modal when using Selectize to trigger the modal. I got the modal working by following this demo.
The modal is used to create a new Artist from the quotes/new form, but I can't get the error messages for the Artist model to display in the Bootstrap modal or on the quotes/new page. When I try to create something that triggers an error message (such as validates_uniqueness) in the modal, it just closes the modal and doesn't display the error message. Everything else is working as expected.
What am I missing to connect the Ajax request to the view?
Here's the relevant section of my form:
<%= f.label :speaker, 'Who said it?' %>
<%= f.collection_select :speaker_id, #speakers, :id, :name,
{prompt: 'Select an artist'}, {class: 'form-control selectize-speaker'} %>
Full source for quotes/form.html.erb
Here's the relevant code in my controller:
class ArtistsController < ApplicationController
def create
#artist = current_user.artists.build(artist_params)
authorize #artist
respond_to do |format|
if #artist.save
if request.referer.include?("artists")
flash[:success] = "Artist was successfully created."
format.html { redirect_to #artist }
else
format.json { render json: #artist }
end
else
format.html { render :new }
format.json { render json: #artist.errors.full_messages }
end
end
end
end
Full source for artists_controller.rb
Relevant javascript code:
$(document).on('turbolinks:load', function() {
var selectizeCallback = null;
// Selectize Speaker
$('.speaker-modal').on('hide.bs.modal', function(e) {
if (selectizeCallback != null) {
selectizeCallback();
selecitzeCallback = null;
}
$('#new_speaker').trigger('reset');
});
$('#new_speaker').on('submit', function(e) {
e.preventDefault();
$.ajax({
method: 'POST',
url: $(this).attr('action'),
data: $(this).serialize(),
success: function(response) {
selectizeCallback({value: response.id, text: response.name});
selectizeCallback = null;
$('.speaker-modal').modal('toggle');
}
});
});
$('.selectize-speaker').selectize({
create: function(input, callback) {
selectizeCallback = callback;
$('.speaker-modal').modal();
$('#speaker_name').val(input);
}
}); // end selectize speaker
}); // end document on
Full source for quotes.js.
And my error message partial, shared/_error_messages.html.erb:
<% if object.errors.any? %>
<div id='error_explanation'>
<div class='alert alert-danger'>
<button type='button' class='close' data-dismiss='alert'>×</button>
<p><strong>The form contains
<%= pluralize(object.errors.count, 'error') %>.</strong></p>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
</div>
<% end %>
Additional source files:
models/quote.rb
models/artist.rb
controllers/quotes_controller.rb
Both a successful and unsuccessful save are returning a 200 response, which means that your success callback will be called:
success: function(response) {
selectizeCallback({value: response.id, text: response.name});
selectizeCallback = null;
$('.speaker-modal').modal('toggle');
}
This always toggles the modal, therefore closing it.
If you ensure that the response is a 4xx on validation error, then you can define an error callback which populates your errors list and does not close the modal.
So instead of:
format.json { render json: #artist.errors.full_messages }
Use something like:
format.json { render json: #artist.errors.full_messages, status: :bad_request }
Then, pass an error callback to your AJAX call:
error: function(response) {
// somehow populate your errors list
// display the errors list
}
This won't work right now, though, because the errors container won't exist: you only render it under this condition:
object.errors.any?
Which, on initial load, will always evaluate false. What you can instead do is always render the errors container, default it to some hidden class if there aren't any errors, and in your error callback, remove the hidden class after it's populated.
I have a component called Items that lives within a parents called ItemsContainer. When a button in Items is clicked an Ajax function is called to delete that Item.
At the moment however I am receiving a 500 error message and am not sure why.
Item Component
class Item extends React.Component{
constructor(props) {
super()
this.state = {
name: '',
price: 0,
code: '',
id: ''
}
}
componentWillMount() {
this.setState({
name: this.props.data.name,
price: this.props.data.price,
code: this.props.data.code,
id: this.props.data.id
})
}
deleteItem() {
let finalUrl = '/items/' + this.state.id;
$.ajax({
type: "DELETE",
url: finalUrl, /* THIS URL IS CALLING CORRECTLY ie. /items/8 */
dataType: "json",
success: function(response) {
console.log("successfully deleted");
},
error: function () {
console.log("error");
}
})
}
render(){
let itemName = this.props.data.name
let itemCode = this.props.data.code
let itemQuantity = 1
let itemPrice = (this.props.data.price * itemQuantity).toFixed(2)
const itemId = this.props.data.id
return(
<tr>
<td>{itemName}</td>
<td>{itemCode}</td>
<td>{itemQuantity}</td>
<td><button className="btn btn-warning" onClick={this.deleteItem.bind(this)}>Remove</button></td>
<td>£{itemPrice}</td>
</tr>
)
}
}
Rails Items Controller
class ItemsController < ApplicationController
def create
#item = Item.new(item_params)
if #item.save
render partial: 'items/item', locals: {item: #item}
else
render json: #item.errors.to_json
end
end
def destroy
if #item.destroy
render partial: 'items/item', locals: {item: #item}
else
render json: #item.errors.to_json
end
end
private
def item_params
params.require(:item).permit(
:name,
:price,
:code,
:id
)
end
end
Creating a new Item is working as expected but I can't work out why I am receiving my 500 error for my delete action. Any help would be much appreciated.
Please check your destroy method in rail controller.
There is no definition for #item hence 500 internal server error :)
Vehicles_controller.rb
class VehiclesController < ApplicationController
before_action :set_vehicle, only: [:show, :edit, :update, :destroy]
load_and_authorize_resource
# skip_before_action :verify_authenticity_token
# GET /vehicles
# GET /vehicles.json
def index
#q = Vehicle.search(params[:q])
#vehicles = #q.result(:distinct => true).order_by([:updated_at, :desc]).page(params[:page]).per(5)
#vehicle = Vehicle.new
end
# GET /vehicles/1
# GET /vehicles/1.json
def show
end
# GET /vehicles/new
def new
end
# GET /vehicles/1/edit
def edit
end
# POST /vehicles
# POST /vehicles.json
def create
params[:vehicle][:name] = params[:vehicle][:name].upcase if !params[:vehicle][:name].nil?
#vehicle = Vehicle.new(vehicle_params)
#vehicles = Vehicle.all.order_by([:updated_at, :desc]).page(params[:page]).per(5)
respond_to do |format|
if #vehicle.save
format.html { redirect_to :back, notice: 'Vehicle was successfully created.' }
format.json { render action: 'index', status: :created, location: #vehicle }
format.js
else
format.js
format.html { render action: 'new' }
format.json { render json: #vehicle.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /vehicles/1
# PATCH/PUT /vehicles/1.json
def update
params[:vehicle][:name] = params[:vehicle][:name].upcase if !params[:vehicle][:name].nil?
respond_to do |format|
if #vehicle.update(vehicle_params)
format.html { redirect_to #vehicle, notice: 'Vehicle was successfully updated.' }
format.json {render json: #vehicle, status: :ok}
else
format.html { render action: 'edit' }
format.json { render json: #vehicle.errors, status: :unprocessable_entity }
end
end
end
# DELETE /vehicles/1
# DELETE /vehicles/1.json
def destroy
#vehicle.destroy
respond_to do |format|
format.html { redirect_to vehicles_url, notice: "#{#vehicle.name} deleted successfully" }
format.json { head :no_content }
format.js { render :layout => false}
end
end
def vehicle_search
#q = Vehicle.search(params[:q])
#vehicles = #q.result(:distinct => true).order_by([:updated_at, :desc]).page(params[:page]).per(5)
respond_to do |format|
format.html
format.js
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_vehicle
#vehicle = Vehicle.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def vehicle_params
params.require(:vehicle).permit(:name, :created_at, :updated_at)
end
end
index.html.erb
<% #vehicles.each do |vehicle| %>
<tr>
<td><%= best_in_place vehicle,:name, class: "v_name", id: vehicle.id%></td>
<td><%= link_to '<i class="fa fa-trash-o"></i>'.html_safe, vehicle, method: :delete, remote: true, data: { confirm: "Are you sure to delete <b>\"#{vehicle.name}\"?</b>", commit: "OK" }, title: "Delete Vehicle", class: "btn btn-danger delete_vehicle" %>
</td>
</tr>
<%end%>
vehicle.rb
class Vehicle
include Mongoid::Document
include Mongoid::Timestamps
field :name, type: String
validates :name, presence: true, uniqueness: true, :format => {:with => /[1-9a-zA-Z][0-9a-zA-Z ]{3,}/}
has_many :pre_processings
has_many :batch_counts
end
destroy.js.erb
$('.delete_vehicle').bind('ajax:success', function() {
$(this).closest('tr').fadeOut();
});
$(".alert").show();
$(".alert").html("Vehicle \"<b><%=#vehicle.name %></b>\" deleted successfully.")
$(".alert").fadeOut(5000);
Here i am using destroy.js.erb to delete vehicle name. It works fine.
Here i am using Inline Edit for Best_in_place. After update the Vehicle name, The ajax alert shows previous vehicle name. Not update vehicle name. So how to show updated vehicle name alert.
$(".alert").html("Vehicle \"<b><%=#vehicle.name %></b>\" deleted successfully.")
The above alert i will show current vehicle name to delete. But if i update using inline edit after i want to delete i'll shows previous record.
Example:
Vehicle Name: MH P5 2312 , I want to delete it, The alert shows are you delete "MH P5 2312 " Vehicle.
After inline edit i will change Vehicle Name: AP 16 1234, So i want to delete Vehicle Name: AP 16 1234, but the ajax alert shows, Vehicle Name: MH P5 2312 delete vehicle name.
Advance Thanks.
This is because you need to update the alert message on ajax request success
in your destroy.js.erb
you should ditch the .erb and fetch the car name from an html element instead
your js code should be as the following:
$('.delete_vehicle').bind('ajax:success', function() {
var vehicleName = $('vehicle_link').data('vehicle_name');
// here I assume that you have a link for initiating the delete process and you can add an attribute to this anchor tag element called data-vehicle_name and set its value to the car name.
$(this).closest('tr').fadeOut();
$(".alert").show();
$(".alert").html("Vehicle \"<b>" + vehicleName + "</b>\" deleted successfully.");
$(".alert").fadeOut(5000);
});
Firstly, if you're going to use the ajax:success callback (which you don't in this instance), you'll need to bind the alert functions to it:
$('.delete_vehicle').on('ajax:success', function() {
$(this).closest('tr').fadeOut();
$(".alert").show().html("Vehicle \"<b><%=#vehicle.name %></b>\" deleted successfully.").fadeOut(5000);
});
Secondly, if you're not getting an updated #vehicle.name because - I think - your Rails instance will have already rendered it. I'm not exactly sure why, but the key will be to pass your data through the ajax request if you were still going to use it:
#app/controllers/vehicles_controller.rb
class VehiclesController < ApplicationController
def destroy
...
format.js { render :layout => false, json: #vehicle }
end
end
#app/assets/javascripts/application.js
$('.delete_vehicle').on('ajax:success', function(event, data, status, xhr) {
vehicle = JSON.parse(data);
$(this).closest('tr').fadeOut();
$(".alert").show().html("Vehicle \"<b>" + + "</b>" deleted successfully.").fadeOut(5000);
});
--
Bottom line is that js.erb is meant to give you actionable Javascript functionality from the controller action. I think your main problem is you're using ajax within this, likely causing a conflict.
Above is how I'd use Ajax (IE put the code in the javascript assets folder); if you wanted to use destroy.js.erb still, you could use the following:
#app/views/vehicles/destroy.js.erb
$("<%= #vehicle.id %>").closest('tr').fadeOut();
$(".alert").show();
$(".alert").html("Vehicle \"<b><%=#vehicle.name %></b>\" deleted successfully.")
$(".alert").fadeOut(5000);
I use x-editable to allow the user to associate itself to another model. The styling changes based on what type of model the user is associated to.
Right now, it works, but the css class only changes after I refresh the page.
How would I go about resetting the css class, after x-editable saves the new value?
This is how my input looks like ->
== editable user, :heat_id, url: admin_user_path(user), type: "select", value: (user.heat.level if user.heat), source: Heat.all.map{ | heat | {value: heat.id, text: heat.level} }, class: "#{user.heat.badge if user.heat}"
I'd essentially need to reapply
class: "#{user.heat.badge if user.heat}"
Update
I figured out that x-editable-rails actually had this built in. The solution was to write ->
== editable user, :heat_id, url: admin_user_path(user), type: "select", value: (user.heat.level if user.heat), source: Heat.all.map{ | heat | {value: heat.id, text: heat.level} }, classes: Hash[Heat.all.map{ | heat | [heat.id, heat.badge]}], class: "#{user.heat.badge if user.heat}"
BUT, I still don't know how to change elements after a save using ajax.
For example, what I would like to do is, after a save event, to re-render the partial 'progress'.
How would I go about doing that?
$('.profile_editable').on('save', function(e, params) {
var progress_value = params.response.progress_value
$('.progress_title').text("Your profile is " + progress_value + "% completed" )
$('.bar').css('width', progress_value + '%');
});
Then, pass info in json response -->
def update
#common_app = CommonApp.find(params[:id])
if #common_app.update_attributes(common_app_params)
respond_to do |format|
format.html { render :action => "show" }
format.json { render json: {success: true, progress_value: #common_app.user.progress, user_name: #common_app.user.name } }
end
else
render json: {errors: #common_app.errors}, status: 400
end
end