Is static pages controller required for every static page? - html

I'm working on a Rails app and was wondering if a controller function is required for every static page even if it just contains HTML and CSS? I know I have to create a view and change routes.rb, but why do you have to define a controller function for each page?
class StaticPagesController < ApplicationController
def home
end
def about
end
end
So here do I need to define "about" even if it is just static content?

No, it's not required. If the method that routes maps to is not defined in the controller specified and there is no view file with that name, then you'll get an error that the method is not defined in the controller. But if the view is present and the method is not, it will go ahead and accept it as if the method was defined.

Have a look at high_voltage gem if you don't want to define those empty actions

Related

A way to precompile slim templates on server side and pass HTML to VueJS in Rails app?

I'm trying to integrate Vue js in existing rails app with lots of slim templates. It would be nice if I could use vue directives in slim templates
(yes, it's possible), get an HTML and pass it to Vue instance for further development. I know that there are slim-lang loaders that compile slim to html, or that you can use slim syntax inside <template lang="slim">.
But I don't want to separately send the result of every single ruby/rails method via AJAX. I want Rails to do its job and give the resulting HTML to Vue instance. Any thoughts and suggestions about this?
The solution I've found is quite simple. All you need to do is wrap your slim markup inside of vue component tag in your .slim views, and add inline-template attribute to it.
Example:
# header.html.slim
<v-header inline-template>
header
nav
- categories.each do |category|
= link_to category.name, category, '#click.prevent': 'doSomething'
</v-header>
or
v-header inline-template=""
header
nav
- categories.each do |category|
= link_to category.name, category, '#click': 'doSomething'
Ruby code will be executed first, template engine will convert everything to html with vue directives, then Vue will recognize its directives and take control of the markup. After I implemented this, I got rid of jquery and asset pipeline. But the views are the same. I did not migrate any of html files to vue components. With this feature, you can partially apply Vue js to your existing rails project and start writing modern javascript code.
There is no silver bullet to combine server-side and client-side templating. Even if you can render vue templates on the server the key context is missing (the interactive state of the page in the client).
There are some rather simple but flawed techniques that you could use to combine server-side and client rendering:
Create a controller that serves up your views
Rails.application.routes.draw do
scope 'templates' do
get '*path', to: 'templates#show'
end
end
class TemplatesController < ApplicationController
def show
if lookup_context.exists?(params[:path])
render template: params[:path]
else
head :not_found
end
end
end
require 'rails_helper'
RSpec.describe "Templates", type: :request do
describe "GET /template/*path" do
it "fetches template if it exists" do
get '/templates/foo/bar.html.erb'
expect(response).to have_http_status(200)
expect(response.body).to match "TEST"
end
it "returns 404 if it does not exist" do
get '/templates/foo/non_existant.html.erb'
expect(response).to have_http_status(:not_found)
end
end
end
However the devil is in details - this would only really work if your views are completely static and do not need any input data.
Render views without a layout
If you instead want to render normally from your controllers but not include the entire layout you can register a custom format:
# config/initializers/mime_types.rb
Mime::Type.register "text/x-html-template", :template
Sometimes you may need to stop spring in order for configuration changes to be picked up.
You can then disable the layout for this format:
class ApplicationController < ActionController::Base
before_action :add_html_to_template_lookup!, if: -> { request.format.template? }
layout :is_template_request?
private
def is_template_request?
request.format.template? ? false : "application"
end
# this will make rails fall back to rendering the "normal" html views
def add_html_to_template_lookup!
request.formats << Mime::Type.lookup("text/html")
end
end

How to have multiple HTML static pages on a ASP.NET MVC project using the Layout page?

On my MVC project I have to incorporate 40 static pages.
I want these pages to use the Layout page.
What is the best way to do that?
I know this question was asked before but I didn't find any good answer.
Any advise?
I don't relly know ASP, but I try to give a generic answer.
So I think if you have a lot of similar static pages, somehow you could make a controller action that handles all these pages. For example the action gets the name of the page as a path variable in the URL, and return the view according to that.
But if that is not possible in the language you are using, you can just make simple separate actions for these pages. Maybe you could group the related ones into the same controller, so you would have a few controllers that handle these pages, and they are not stuffed in one controller.
Basically the solution is very simple, you have to create views for you static HTML (cshtml), then you should add a Route to your Route.Config like this:
routes.MapRoute(
"OrdeForm",
"OrderForm/{file}",
new { controller = "MyController", action = "Page", file} = "" }
);
Where "File" is a dynamic parameter that gets the View name from the URL and renders the right View.
The global controller should be something like this:
public class OrderFormController : Controller
{
public ActionResult Index(string file)
{
return View(file);
}
}
That works perfectly!
Thank you #Erik Philips for the excellant answer!

Yii2: exclude specific controller actions from '$this->goBack()'

I have views from various controller actions which are solely to be run from an iframe placed in another view.
Currently, when the iframe loads, and I go to the log in page to log in, on success the login controller (using yii2 user module) calls $this->goBack(), redirecting me to the iframe source URL (since it's the last page visited), rather than the original page containing the iframe.
Basically, I'd like to exclude specific controller actions from being set as the return URL when $this->goBack() is called. Bonus points if all actions loaded in iframes are automatically excluded from $this->goBack().
Ok, I'll have a go at this! This code is totally untested! Your problem is that the action has no way of knowing whether it's been called from an iframe or not, unless you give it one. So, the basis of my attempt at an answer is that all urls for iframes should have an additional get parameter. Lets call that caller. So each iframe should look something like
<iframe url="index.php?r=controller/action&caller=this-controller/action</iframe>
Now you can always test the request url to see if it was called from an iframe. In addition, every link within the iframe should have this parameter added to it's url.
So, now we have at least two problems. Firstly, how to automatically add caller as a get parameter, without having to re-write every url, and secondly, how to reconfigure the goBack() method so it knows the difference between the two types of request.
The first problem can be relatively easily resolved by adding another view layer in between the controller and the view you want I'll call it iframe. So in your controller action, add this;
$view = 'The name of the view you want to render';
$this->render('iframe', 'view' => $view);//Add in any other parameters you want to pass
Your iframe view file should contain something like this;
<iframe src="<?php Url::to(['however you generate the url for your iframe', 'caller' => Url::to($this->context->route)]); ?>">
<?php $this->render($view); ?>//Pass additional parameters to the view if needed
</iframe>
Now we have a way of testing a controller/action call to see if it being requested by am iframe. The caller parameter is important because it allows us to extract a string to use as the value for goBack() and other methods.
Next, we need to extend UrlManager, as all request, response, Url:to() and goBack() methods and classes ultimately use the UrlManager to complete the methods for generating urls.
So, create a new UrlManager. We'll copy most of the code from the existing UrlManager, just adding some spiciness of our own. I've stored mine in commands, but put your where you like and change the namespace accordingly.
<?php
namespace app\commands;
use Yii;
use yii\web\UrlManager;
class CustomUrlManager extends UrlManager {
public function createUrl($params){
$request = Yii::$app()->request;
$caller = $request->get('caller');
if ($caller && !$params['caller']){
$params['caller'] = $caller;
}
return parent::createUrl($params);
}
}
So now, the iframe generates a caller parameter, and every link within the iframe will also have caller appended as a parameter, as long ass you've used either Url::to() (or variants on that method) or Yii::$app->UrlManager to generate your links.
Now all we need to do is customise the goBack() method of your controller to send any goBack() requests to the original source iframe.
public function goBack($defaultUrl = null)
{
$caller = Yii::$app->request->get('caller');
if ($caller){
return Yii::$app->getResponse()->redirect($caller);
}
return Yii::$app->getResponse()->redirect(Yii::$app->getUser()->getReturnUrl($defaultUrl));
}
Finally you need to configure Yii to use your new UrlManager, in your config file;
'components' => [
'urlManager' => [
'class' => 'app/commands/CustomUrlManager'
]
]
I'd love to know if this works, it's been an interesting challenge!

Create Dynamic View with static HTML in MVC

I have been researching dynamic content for MVC views and partial views but have not successfully found an architecture to fit my needs.
Basically I am required to create a landing page based on parameters pass by the URL.
For basics
http://mydns.com/myconroller/myview/?landingpage=Param1
The controller will need to find the HTML that will be used to create the view.
The view is going to be different based on the landing page.
(for the sake of the question, I am using landingpage as an example)
My goal is to be able to deploy a Landing page and based on the URL use that HTML Landing page in the view based on the landingpage parameter that is passed.
There are other views that are working currently in the controller. I am trying to add functionality to be able to add a new one time page without having to recompile.
I have searched through various ideas on how to load dynamic views but cannot seem to find a solution that fits this need based on what I have read.
I can possibly RedirectToAction but I am still in the dark on where to deploy and I am getting several problems with Razor as it is not in the shared directory and then I am stuck with deployment issues as I want to organize the landing pages differently than I am organizing the views.
Solution:
I decided to take a different approach and use the ContentResult Action in the controller. I still have the Main View and I use the HTML extensions to render the HTML pages that I have deployed in my customer's directory.
#{
Html.RenderAction("LandingPageContent", "Controller", Model);
}
Then in the controller I load the HTML directly and return the ContentResult
public ContentResult LandingPageContent(object model, FormCollection collection)
{
MySRCHelper helper = new MySRCHelper();
ContentVariables variables = helper.getContentSRC(model.EntryCode);
model.ContentSRC = variables.LandingPageSRC;
return Content(System.IO.File.ReadAllText(Server.MapPath(model.ContentSRC)));
}
I can then configure the path to the raw HTML file to be used and it will be loaded into the View. The View can then house all of the paths to load jQuery, CSS and other necessary javascript to integrate with the raw HTML and allow me to deploy the HTML files into any directory structure that I want. The configuration XML file allows me to find XML elements and use those values for any HTML that I am looking for, like a welcome and thank you page. The helper object will open the XML and find the configuration based on the parameters passed to the View.
<ContentLandingItem entrycode="1" customerID="Cutomer1">
<ContentLandingPageSRC>~/Customers/Customer1/Customer1Landing.htm</ContentLandingPageSRC>
<ContentThankyouSRC>~/Content/Default/GenericThankyou.htm</ContentThankyouSRC>
</ContentLandingItem>
<ContentLandingItem entrycode="2" customerID="Cutomer2">
<ContentLandingPageSRC>~/Customers/Customer2/Customer2Landing.htm</ContentLandingPageSRC>
<ContentThankyouSRC>~/Customers/Customer2/Customer2Thankyou.htm</ContentThankyouSRC>
</ContentLandingItem>
The view still performs its duties and works independently on it own letting the raw HTML decorate the View. The model is still intact and can be used as I wish. The FormCollection is there in case a form submit posts the values to the view and provides some things that I omitted from this question as it did not pertain to this subject.
I don't want to answer my own question and I found the pieces that helped me on another site, so I am putting what I did here in case anyone needs this functionality.
This sounds like using the you can inherit from the virtual path provider view engine and decide based on the URL parameters (or other) which view to return. Some example that you can adjust to your needs:
public class CustomViewEngine : VirtualPathProviderViewEngine
{
public MyViewEngine()
{
this.ViewLocationFormats = new string[] { "~/Views/{1}/{2}.mytheme ", "~/Views/Shared/{2}.mytheme" };
this.PartialViewLocationFormats = new string[] { "~/Views/{1}/{2}.mytheme ", "~/Views/Shared/{2}. mytheme " };
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
var physicalpath = controllerContext.HttpContext.Server.MapPath(partialPath);
return new RazorView(controllerContext, physicalpath);
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
var physicalpath = controllerContext.HttpContext.Server.MapPath(viewPath);
return new RazorView(controllerContext, physicalpath);
}
}
In there you can return a RazorView or WebFormView and set your desired path for the view to use.

Model Validation without the ValidationSummary?

I have a LoginModel for my Login Action, but I'm wanting to use just HTML.
Example...
public class LoginModel
{
[Required]
public string Email { get;set; }
}
in my HTML, I have
<input type="text" value="" name="Email">
This is because I'm going to be storing my HTML in my database, problem I'm having is, how do I get model validation without using Html.ValidationSummary()?
I was hoping I could just do <div class="validation-summary-errors"></div>
As this is what is in the HTML, but does not work..
Ideas?
Regardless of where you store your HTML the validation is done on the client side. There are various posts on how to use the virtual path provider to store your views somewhere else (DB) and then validation should still work fine. I think I'm missing why it's not working for you though so I have to imagine you aren't using the path provider to find your views.
Edit
Seems you want to inject messages into a Div. This wont happen automaticaly unless you work some magic in the path provider. Use your own helper method in the view to avoid hacks or just use what's provided by default. If you really want to do it render your view in your controlllet and search for your Div pattern to replace.
custom ValidationForMessage helper removing css element
Note Darin's method
var expression = ExpressionHelper.GetExpressionText(ex);
var modelName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expression);
var modelState = htmlHelper.ViewData.ModelState[modelName];
without access to ViewContext in your controller you can only render your html for your View. However, somewhere in your view you need (as far as I can tell) a helper method to stick your error collection into ViewData.
Your Virtual Path Provider may have to inject this helper method into your view text so it is there for Razor to parse. Actually - duh. This may be much easier. Your provider may be able to just simply read your html from the database, find the div, and inject the #Html.ValidationSummary into that div. I believe this would work. Why not just put the validation summary in there though if its going to end up there in the end anyways (essentially)