简体   繁体   中英

How to pass my model parameters to a view Rails?

I am trying to generate pdf's in Rails using the prawn gem.The data for the pdf is partly hardcoded but some of it should come from a rails form.

I have generated a "Template" scaffold with name, address and ID. Using Prawn, I have created a pdf file. It prints out a pdf without any issues. I want to pass the model parameters inline into the pdf so that I could for example, write: "This form is for #{Template.name} who lives at #{Template.address} and has an id of #{Template.NationalId}", where name, address and NationalId come from the form.

I also want to generalize this method and able to do this for different pdfs reports, certificates etc from the same app.

How do I pass this data in?

Below is my code:

  #Controller
  class TemplatesController < ApplicationController
  before_action :set_template, only: %i[ show edit update destroy ]

def index
  @templates = Template.all

  respond_to do |format|
    format.html
    format.pdf do
      pdf = ReportPdf.new
      send_data pdf.render, filename: 'report.pdf', type: 'application/pdf', 
      disposition: "inline"
    end
  end
end

def create
@template = Template.new(template_params)

respond_to do |format|
  if @template.save
    format.html { redirect_to @template, notice: "Template was successfully created." }
    # format.pdf { render :index, status: :created, location: @template }
    format.json { render :index, status: :created, location: @template }
  else
    format.html { render :new, status: :unprocessable_entity }
    format.json { render json: @template.errors, status: :unprocessable_entity }
  end
end
end

#...lots of other controllers update, destroy etc

private
# Use callbacks to share common setup or constraints between actions.
def set_template
  @template = Template.find(params[:id])
end

# Only allow a list of trusted parameters through.
def template_params
  params.require(:template).permit(:name, :address, :NationalId)
end
end

#My Report.pdf. (Insert data into the text content area)

class ReportPdf < Prawn::Document
def initialize
    super()
    header
    text_content
end

def header
    #This inserts an image in the pdf file and sets the size of the image
    image "#{Rails.root}/app/assets/images/logo.jpg", width: 230, height: 75
  end

  def text_content
    # The cursor for inserting content starts on the top left of the page. Here we move it down a little to create more space between the text and the image inserted above
    y_position = cursor - 50

    # The bounding_box takes the x and y coordinates for positioning its content and some options to style it
    bounding_box([0, y_position], :width => 270, :height => 300) do
      text "**Pass params here**", size: 15, style: :bold
      text "Lorem ipsum dolor sit amet, consectetur adipiscing elit. ."
    end

    bounding_box([300, y_position], :width => 270, :height => 300) do
      text "Duis vel", size: 15, style: :bold
      text "Duis vel tortor elementum, ultrices tortor vel, accumsan dui. Nullam in dolor rutrum, gravida turpis eu, vestibulum lectus. Pellentesque aliquet dignissim justo ut fringilla. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut venenatis massa non eros venenatis aliquet. Suspendisse potenti. Mauris sed tincidunt mauris, et vulputate risus. Aliquam eget nibh at erat dignissim aliquam non et risus. Fusce mattis neque id diam pulvinar, fermentum luctus enim porttitor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos."
    end
    
    end

  end

Form

 <%= form_with(model: template, local: true) do |form| %>
   <% if template.errors.any? %>
     <div id="error_explanation">
       <h2><%= pluralize(template.errors.count, "error") %> prohibited this template 
       from being saved:</h2>

       <ul>
         <% template.errors.full_messages.each do |message| %>
           <li><%= message %></li>
         <% end %>
       </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.label :address %>
    <%= form.text_field :address %>
  </div>

  <div class="field">
    <%= form.label :address %>
    <%= form.text_field :address %>
  </div>

  <div class="field">
    <%= form.label :NationalId %>
    <%= form.text_field :NationalId %>
  </div>

  <div class="actions">
    <%= form.submit %>
    </div>
  <% end %>

You can pass options to the intializer method:

class ReportPdf < Prawn::Document
  attr_reader :template
  # You should keep the initialzer signature of the super method
  def initialize(templates:, **options, &block)
    @templates = templates
    # the super method still uses the old style initialize(options = {}, &block)
    # instead of keyword arguments
    super(options, &block) 
    header
    text_content
  end

  def text_content
  # The cursor for inserting content starts on the top left of the page. Here we move it down a little to create more space between the text and the image inserted above
    y_position = cursor - 50

    # The bounding_box takes the x and y coordinates for positioning its content and some options to style it
    bounding_box([0, y_position], :width => 270, :height => 300) do
      text "**Pass params here**", size: 15, style: :bold
      text "Lorem ipsum dolor sit amet, consectetur adipiscing elit. ."
    end

    templates.each do |template|
      bounding_box([300, y_position], :width => 270, :height => 300) do
        text "Duis vel", size: 15, style: :bold  
        # @todo NationalId should be named national_id 
        #  change the name of the column or setup an alias if you cannot
        text "This form is for #{template.name} who lives at #{template.address} 
        and has an id of #{template.NationalId}"
      end
    end
  end
end

Here we are using a manditory keyword argument. You could also use a positional argument or make the argument optional:

# postitional argument
def initialize(templates, **options, &block)

# optional keyword argument
def initialize(templates: [], **options, &block)

The advantage to keyword arguments is explicitness and that you don't have to remember the order of the arguments.

def index
  @templates = Template.all

  respond_to do |format|
    format.html
    format.pdf do
      pdf = ReportPdf.new(templates: @templates)
      send_data pdf.render, filename: 'report.pdf', type: 'application/pdf', 
      disposition: "inline"
    end
  end
end

When you create your PDF with the ReportPdf class (using pdf = ReportPdf.new ), you can pass in whatever parameters you want. You just need to add matching parameters to the initialize method in the ReportPdf class. So, for example:

pdf = ReportPdf.new(@name, @address)
def initialize(name, address)
  @name = name
  @address = address
  super
  header
  text_content
end

If you need to pass in lots of variables, it may be cleaner to add methods to your ReportPdf class to receive them:

pdf = ReportPdf
  .new
  .name(@name)
  .address(@address)
  .salutation(@salutation)
  .something_else(@value)
  etc

It looks like your close. Are you wanting to print all the doc from the index page or just the one submitted on the form, which would be easiest from the show page. Instead of printing the one doc from the index print it through the show page

def show
  @template = Template.find(params[:id])

  respond_to do |format|
    format.html
    format.pdf do
      pdf = ReportPdf.new(template: @template)
      send_data pdf.render, filename: 'report.pdf', type: 'application/pdf', 
      disposition: "inline"
    end
  end
end

Then you can add the template params you need

class ReportPdf < Prawn::Document
def initialize(template)
    @template = template
    super()
    header
    text_content
end

def header
    #This inserts an image in the pdf file and sets the size of the image
    image "#{Rails.root}/app/assets/images/logo.jpg", width: 230, height: 75
  end

  def text_content
    # The cursor for inserting content starts on the top left of the page. Here we move it down a little to create more space between the text and the image inserted above
    y_position = cursor - 50

    # The bounding_box takes the x and y coordinates for positioning its content and some options to style it
    bounding_box([0, y_position], :width => 270, :height => 300) do
      text "Name: #{@template.name}, Address: #{@template.address}, Ect...", size: 15, style: :bold
      text "Lorem ipsum dolor sit amet, consectetur adipiscing elit. ."
    end

    bounding_box([300, y_position], :width => 270, :height => 300) do
      text "Duis vel", size: 15, style: :bold
      text "Duis vel tortor elementum, ultrices tortor vel, accumsan dui. Nullam in dolor rutrum, gravida turpis eu, vestibulum lectus. Pellentesque aliquet dignissim justo ut fringilla. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut venenatis massa non eros venenatis aliquet. Suspendisse potenti. Mauris sed tincidunt mauris, et vulputate risus. Aliquam eget nibh at erat dignissim aliquam non et risus. Fusce mattis neque id diam pulvinar, fermentum luctus enim porttitor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos."
    end
    
    end

  end

Then in your show view you can just link to it or make a button to link to it

<%= link_to 'Download PDF', template_path(@template, format:"pdf"), class: "btn btn-primary" %>

Use the instance variable in the model. for example:

def text
  @user = User.all
end

So if the user define @user in the controller method then @user will available in the view.

By default instance variable is available in the whole requests cycle.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM