I have a particular controller action that I want to render without any layout.
I tried to render without a plug at the controller level, but it didn't work.
defmodule Hello.PageController do
use Hello.Web, :controller
plug :put_layout, nil
def landing(conn, _params) do
render conn, "landing.html"
end
end
How can I do this?
The reason plug :put_layout, nil does not work is because the put_layout plug only considers false to mean "don't use any layout". nil is treated just like any other atom and Phoenix tries to render nil.html:
Could not render "nil.html" for MyApp.LayoutView, please define a matching clause for render/2 or define a template at "web/templates/layout". The following templates were compiled:
app.html
The fix is to use false:
plug :put_layout, false
If you want to restrict the plug to certain actions, you can pass when:
plug :put_layout, false when action in [:index, :show]
You just need to invoke put_layout and pass it the conn and false.
def landing(conn, _params) do
conn = put_layout conn, false
render conn, "landing.html"
end
If you are running a LiveVeiw app, you'll likely have a plug :put_root_layout, {MyAppWeb.LayoutView, :root} in your browser pipeline.
In which case put_layout(false) will only disable the app layout, so you'll have to use conn |> put_root_layout(false) to disable root layout.
You are probably looking to disable both, so you'll need:
conn
|> put_layout(false) # disable app.html.eex layout
|> put_root_layout(false) # disable root.html.eex layout
|> render(...)
or shorter:
conn
|> put_root_layout(false)
|> render(..., layout: false)
Related
I try to set transparent walls, using pyRevit. I do the following:
categories = List[ElementId]()
wallCatId = ElementId(BuiltInCategory.OST_Walls)
categories.Add(wallCatId)
ogs = OverrideGraphicSettings()
ogs.SetSurfaceTransparency(70)
t = Transaction(doc, "New parameter filter")
t.Start()
filter = ParameterFilterElement.Create(doc, "Walls filter", categories)
t.Commit()
all_views = FilteredElementCollector(doc).OfClass(View).ToElements()
for i in all_views:
if (i.ViewType == ViewType.ThreeD) or (i.ViewType == ViewType.FloorPlan):
views_to_treat.append(i)
t = Transaction(doc, "New visibility filter")
t.Start()
for i in views_to_treat:
i.AddFilter(filter.Id)
i.SetFilterOverrides(filter.Id, ogs)
t.Commit()
Nothing happens, I don't know why. Is it my "categories" that is wrongly defined (how can I know what kind of ElementId it expects? Is it the Id of the Wall Category? In that case, it should be ok here)? Or is it when applying the filter override to the view?
Any help would be greatly appreciated!
Arnaud.
I can see that you are applying the transparency filter to Walls. I am not 100% sure that this is the most efficient way to achieve this since we can override transparency via Category override. Please keep in mind that Filters are limited as we can apply only a handful of them to the view. There is a max number. I don't remember from top of my head, but there is. Also, order of filters matters, as they can potentially override each other's rules based on order. Either way overriding transparency can be achieved by changing it on a Category like so:
catId = ElementId(BuiltInCategory.OST_Walls)
all_views = FilteredElementCollector(doc).OfClass(View).ToElements()
overrides = OverrideGraphicSettings()
overrides.SetSurfaceTransparency(70)
t = Transaction(doc, "Override Categories")
t.Start()
for i in all_views:
if ((i.ViewType == ViewType.ThreeD) or (i.ViewType == ViewType.FloorPlan)) and (i.IsCategoryOverridable(catId)):
try:
i.SetCategoryOverrides(catId, overrides)
except:
# print out error?
pass
t.Commit()
Also, just a few generic comments. Try to minimize the amount of times you iterate over lists, especially if they are the same items. If you can do what you need to do in the first loop, then that's the best. The above can be simplified even more with list comprehension but I wanted to keep it "obvious" for educational purposes.
I am also checking if Category is overridable before attempting to do so. Why? Because if view category overrides are controlled by a view template, it will not allow us to set the overrides. Also some categories don't have a surface transparency override ex. lines if I remember correctly.
Finally I like to put it all in a try/except statement so that i can catch any issues in my loop and still continue with other items. if I don't do that, and one view fails, we would have failed the whole operation.
This is what the result should be:
I want to change the name of the #browser variable because I'm tired of typing it.
In env.rb:
before do
#b = Watir::Browser.new :chrome
end
This throws error:
Unable to pick a platform for the provided browser or element: nil.
nil was passed to the PageObject constructor instead of a valid browser or element object. (RuntimeError)
before do
#browser = Watir::Browser.new :chrome
end
works as expected.
#browser = Watir::Browser.new :chrome
#b = #browser
doesn't work either. Is there any way to modify this variable name?
In general, you can store your browser instance in a variable of any name. Watir-Webdriver does not care, nor does Cheezy's page objects. The initialization of the page object only cares what the actual object passed in is - ie it must be a Watir::Browser or Watir::Element (or the Selenium-WebDriver equivalents).
However, this is not true when using the PageObject::PageFactory. In the on_page method, and implicitly visit_page and if_page, the code is hard-coded to look for a #browser variable.
def on_page(page_class, params={:using_params => {}}, visit=false, &block)
page_class = class_from_string(page_class) if page_class.is_a? String
return super(page_class, params, visit, &block) unless page_class.ancestors.include? PageObject
merged = page_class.params.merge(params[:using_params])
page_class.instance_variable_set("#merged_params", merged) unless merged.empty?
#current_page = page_class.new(#browser, visit)
block.call #current_page if block
#current_page
end
The initialization of the page object is always done with #browser. When you stored the browser instance in #b, it meant that #browser was nil. This then led to the exception you saw. If you want to use the PageFactory as written, you will need to stick to #browser.
If the variable really bothers you, there is always the option for monkey patching. Simply re-define the on_page and on method to use whatever variable you like. The following redefines the method to use #b:
require 'page-object'
module PageObject
module PageFactory
def on_page(page_class, params={:using_params => {}}, visit=false, &block)
page_class = class_from_string(page_class) if page_class.is_a? String
return super(page_class, params, visit, &block) unless page_class.ancestors.include? PageObject
merged = page_class.params.merge(params[:using_params])
page_class.instance_variable_set("#merged_params", merged) unless merged.empty?
#current_page = page_class.new(#b, visit)
block.call #current_page if block
#current_page
end
alias_method :on, :on_page
end
end
The issue is somewhere else in your code. If you run just the line you provided (after requiring watir)
require 'watir-webdriver'
#b = Watir::Browser.new :chrome
in a IRB session it will work just fine..
Some other code dependency (page objects library perhaps) needs a browser object and is expecting a specific name, is getting a nill (un-initialized) object instead and complaining.
Also, unless you want the overhead of opening and closing the browser for each test (can add a ton of time) you may want to initialize the browser earlier, such as in a before_all hook, or early in the startup of your test code. Then just do something like clear cache and cookies in the before hook
Would like to know is that possible to have filter with default value with active admin? This will be helpful for preloading the data for the admin user.
filter :country, :default=>'US'
You can do it by defining before_filter
before_filter :only => [:index] do
if params['commit'].blank?
#country_contains or country_eq .. or depending of your filter type
params['q'] = {:country_eq => 'US'}
end
end
UPD:
in some cases you need to set filter if params[:q] is empty or params[:scope] empty
so this might work better
before_filter :only => [:index] do
if params['commit'].blank? && params['q'].blank? && params[:scope].blank?
#country_contains or country_eq .. or depending of your filter type
params['q'] = {:country_eq => 'US'}
end
end
Adapted Fivells answer to work correctly with scopes and downloads. Feels hacky but seems to do the job. Annotated intention in comments.
before_filter only: :index do
# when arriving through top navigation
if params.keys == ["controller", "action"]
extra_params = {"q" => {"country_eq" => "US"}}
# make sure data is filtered and filters show correctly
params.merge! extra_params
# make sure downloads and scopes use the default filter
request.query_parameters.merge! extra_params
end
end
Fixing issue with "Clear Filters" button breaking, updated answer building on previous answers:
Rails now uses before_action instead of before_filter. It belongs in the controller like so:
ActiveAdmin.register User do
controller do
before_action :set_filter, only: [:index]
def set_filter
# when arriving through top navigation
if params.keys == ["controller", "action"]
extra_params = {"q" => {"country_eq" => "US"}}
# make sure data is filtered and filters show correctly
params.merge! extra_params
# make sure downloads and scopes use the default filter
request.query_parameters.merge! extra_params
end
end
params.delete("clear_filters") #removes "clear_filters" if it exists to clear it out when not needed
end
end
Note, ActiveAdmin uses ransack for queries (ex. {"q" => {"country_eq" => "US"}}), check out https://activerecord-hackery.github.io/ransack/getting-started/search-matches for more matches if you need something more complex than "_eq".
Also, previous answers leave the "Clear Filters" button broken. It doesn't clear the filters, the filters set here are simply re-applied.
To fix the "Clear Filters" button, I used this post as a guide How to keep parameters after clicking clear filters in ActiveAdmin.
#app/assets/javascripts/active_admin.js
# Making clear filter button work even on pages with default filters
$ ->
$('.clear_filters_btn').click ->
if !location.search.includes("clear_filters=true")
location.search = "clear_filters=true"
This removes all search params (ie filters) and adds "clear_filters=true" so the controller can tell the request came from the "Clear Filters" button.
before_action only: [:index] do
if params['commit'].blank?
extra_params = {"country_eq" => "US"}
params['q'] = {} if params['q'].blank?
params['q'].merge! extra_params
request.query_parameters.merge! extra_params
end
end
symfony getLayout does not seem to work when layout is set via view.yml. Is there anyway to get this within the controller's action class method
I recently needed this. You can do it but you just need to return the entire view.yml contents as an array:
$view_array = sfViewConfigHandler::getConfiguration(array(sfConfig::get('sf_app_config_dir').'/view.yml'));
Just adjust the relative path from sf_app_config_dir (or use another marker) to get what you need.
It's not a trivial task. The view.yml, is not in the "scope" of the action.
Maybe, you can use setLayout in your action rather then in view.yml.
if you can't, for some reasons... you can try this method to reach datas in view.yml:
Is it possible to get a value from view.yml in an action
Execute the following code in the action. It works for both cases, layout set in the action or in the view.yml.
$controller = $this->getContext()->getController();
$view = $controller->getView($this->getModuleName(), $this->getActionName(), 'Success'); // viewName == 'Success' (default)
$layout_name = $view->getDecoratorTemplate(); // e.g expected: layout.php
Let us know if it works for you.
I have the answer for the Rails 2.X but not for Rails 3. How can I read the name of a current layout rendered inside a view.
My Rails2 question: Rails Layout name inside view
Thx.
Getting this to work in Rails 3.2 is a little more complicated than previously outlined. If your controller explicitly declares a layout, then the result of controller.send(:_layout) is a String, but otherwise it's an ActionView::Template. Try this:
module ApplicationHelper
def current_layout
layout = controller.send(:_layout)
if layout.instance_of? String
layout
else
File.basename(layout.identifier).split('.').first
end
end
end
in rails 5
This works for me:
def current_layout
layout = controller.class.send(:_layout)
if layout.nil?
default_layout
elsif layout.instance_of? String or layout.instance_of? Symbol
layout
else
File.basename(layout.identifier).split('.').first
end
end
For Rails 4:
controller.send(:_layout)
=> 'application'
For Rails 3.2:
controller.send(:_layout)
=> #<ActionView::Template:0x000000082bb788>
But controller.send(:_layout).identifier returns the fullpath:
/home/davidm/Documentos/Devel/myapp/app/views/layouts/application.haml
I think it should be in core, but for now you can make a helper method:
def current_layout
controller.send :_layout
end
it will return currently used layout name
I have used in Rails4 at view pages and got reuslt.
controller.send(:_layout)
I hope this help.
For rails 5:
controller.class.send(:_layout)
This does NOT work:
controller.send(:_layout)
You can do what I've done in my Ajax gem for Rails which is to wrap the _render_layout method:
ActionView::Base.class_eval do
def _render_layout_with_tracking(layout, locals, &block)
controller.instance_variable_set(:#_rendered_layout, layout)
_render_layout_without_tracking(layout, locals, &block)
end
alias_method_chain :_render_layout, :tracking
end
Then you can access the value that was set from your view (I'm pretty sure you have access to the controller there...) or in your controller in an after_filter, which is what I do.
I've written a custom RSpec 2 matcher which can be used to test layout rendering in Rails 3.
All the approaches in the previous answers try to guess the name via private methods, but there's no need to guess and can be easily accomplished with the public API:
class ApplicationController
layout :set_layout
attr_reader :layout_name
helper_method :layout_name
private
def set_layout
#layout_name = "application"
end
end
Override in any controller that won't use the standard layout:
class MyController < ApplicationController
private
def set_layout
#layout_name = "my_layout"
end
end
And now in your views:
<%= layout_name %>