A ViewResult loads a view from a UFViewEngine, executes it with the correcct TemplatingEngine, optionally wraps it in a layout, and writes the result to the HttpResponse.

It is designed to work with different templating engines, to be intelligent with guessing the correct view, and to work seamlessly on asynchronous platforms.

It writes the final output to the client HttpResponse with a text/html content type.

Choosing a view

A ViewResult will attempt to guess which view to use, based on the current ActionContext, that is, based on which controller you are in and which method was executed for the request. It feels a bit like magic, but it's not: let's step through how it works.

Let's look at an example:

class AdminController extends Controller {
@:route("/dashboard/")
function doDashboard() {
  return new ViewResult();
}

@:route("/camera/")
function takePhoto() {
  return new ViewResult();
}
}
  • If you visit /dashboard/, it is going to use a template at /view/admin/dashboard.html by default.
  • If you visit /camera/, it is going to use a template at /view/admin/takePhoto.html by default.

How does it know to look there?

  1. /view/ is your viewPath, set in UfrontConfiguration.viewPath
  2. admin/ is guessed based on the name AdminController. We lower-case the first letter, and ignore the "Controller" part of the name. Another example is BlogPostController or just BlogPost looking for views in "/blogPost/".
  3. dashboard.html and takePhoto.html are guessed based on the action name / method name. If the name begins with "do" followed by an uppercase letter, we ignore the "do" letters. So function doDefault() will look for a view called default.html. We also make sure the first letter is lower-case.

How do we change it?

Well you can use metadata.

To change the default folder that views in this controller are found in, use the @viewFolder metadata:

@viewFolder("/admin-templates/")
class AdminController extends Controller {
// Views will be in the `view/admin-templates/` folder.
}

You can also set a default layout for every action on the controller:

@viewFolder("/admin-templates/")
@layout("layout.html") // Will look in `view/admin-templates/layout.html`
class AdminController extends Controller {
// Views will be in the `view/admin-templates/` folder.
// Layout will be "layout.html".
}

// If you would prefer to use a layout that isn't inside the controller's `viewFolder`, add a leading slash to `@layout`:
@viewFolder("/admin-templates/")
@layout("/layout.html")
class AdminController extends Controller {
// Views will be in the `view/admin-templates/` folder.
// Layout will be `view/layout.html`.
}

If you want to change the template used for one of our actions, you can use the @template metadata:

@:route("/camera/")
@template("camera.html") // Will look in `view/admin-templates/camera.html`
@layout("cameraLayout.html") // Will look in `view/admin-templates/cameraLayout.html`
function takePhoto() {
return new ViewResult();
}

To specify a template to use manually in your code:

return new ViewResult({}, "myView.html");
return new ViewResult({}, "myView.html").withLayout("layout.html");
return new ViewResult({}, "myView.html").withoutLayout();

This gives you a fair amount of flexibility:

  1. Do nothing, and let Ufront guess.
  2. Be more specific, and use metadata, which is still nice and clean.
  3. Be very specific and flexible, specifying it in your code.

What about file extensions?

I've used ".html" views in all these examples, but you could leave this out.

If the viewPath does not include an extension, any view matching one of the extensions supported by our templating engines will be used. You can optionally specify a TemplatingEngine to use also. See UFViewEngine.getTemplate() for a detailed description of how a template is chosen.

Setting data

When you create the view, you can specify some data to execute the template with:

new ViewResult({ name: "jason", age: 26 });

You can add to this data using ViewResult.setVar() and ViewResult.setVars().

You can also specify some global data that will always be included for your app:

ViewResult.globalValues["copyright"] = "© 2014 Haxe Foundation, all rights reserved.";

Helpers (dynamic functions) can be included in your ViewResult also.

Wrapping your view with a layout

Most websites will have a layout that is used on almost all of your pages, and then individual views for each different kind of page.

In Ufront, a layout is just another ufront.view.UFTemplate which has a variable called "viewContent". The result of the current view will be inserted into the "viewContent" field of the layout. All of the same data mappings and helpers will be available to the layout when it renders.

You can set a default layout to be used with all ViewResults using the static method ViewResult.setDefaultLayout(), or by injecting a String named "defaultLayout" into the app's dependency injector. You can set a default layout for a controller using @layout("layout.html") style metadata. You can set a layout for an individual result using ViewResult.withLayout(). Finally if you have a default layout, but want to NOT use a layout, you can use ViewResult.withoutLayout()

Where does it get the views from?

Short answer: by default, it gets them from the filesystem in the "view/" folder relative to the script directory.

Long answer:

Ufront supports different view engines. (See UFViewEngine). For example, you could have a view engine that loads templates from a database, rather than from the FileSystem. Or one that loads them over HTTP from a server somewhere.

ViewResult will use dependency injection to get the correct UFViewEngine four our app. You can set this by setting UfrontConfiguration.viewEngine when you start your Ufront app. By default, it is configured to use the FileViewEngine, loading views from the "view/" directory relative to your script directory, so "www/view/".

What if I want a different templating engine?

We use a UFViewEngine to load our templates, and these support multiple templating engines. You can view some available engines in TemplatingEngines, and it will be fairly easy to create a new templating engine if needed. You can use UfrontApplication.addTemplatingEngine() to add a new engine, which will then be available to your view results.

Constructor

new (?data:TemplateData, ?viewPath:String, ?templatingEngine:TemplatingEngine)

Create a new ViewResult, with the specified data.

Parameters:

data

(optional) Some initial template data to set. If not supplied, an empty {} object will be used.

viewPath

(optional) A specific view path to use. If not supplied, it will be inferred based on the ActionContext in this.executeResult().

templatingEngine

(optional) A specific templating engine to use for the view. If not supplied, it will be inferred based on the viewPath in this.executeResult().

Fields

data:TemplateData

The data to pass to the template during executeResult. This will be combined with the helpers and globalData before being passed to the templates execute function. This is set during the constructor, and you can add to it using the setVar and setVars helper methods.

read only finalOutput:Future<String>

A Future that will eventually hold the final compiled output for the ViewResult.

helpers:TemplateData

Any helpers (dynamic functions) to pass to the template when it is executed.

read only layoutSource:TemplateSource

The source used for loading a layout template. Set in this.withLayout(), this.withoutLayout() this.usingTemplateString(), or inferred during this.executeResult().

read only templateSource:TemplateSource

The source used for loading a view template. Set in the constructor or with this.usingTemplateString(), or inferred during this.executeResult().

Methods

executeResult (actionContext:ActionContext):Surprise<Noise, Error>

Execute the given view, wrap it in a layout, and write it to the response.

In detail:

  • Figure out which template and which layout to use. (See the documentation at the top of this class for more details.)
  • Load the template and layout.
  • Once loaded, execute the view template with all of our data (a combination of globalValues, helpers and data).
  • If a layout is used, execute the layout with the same data, inserting our view into the viewContent variable of the layout.
  • Write the final output to the ufront.web.context.HttpResponse with a text/html content type.

setVar (key:String, val:Dynamic):ViewResult

Add a key=>value pair to our TemplateData

setVars (?map:Map<String, Dynamic>, ?obj:{}):ViewResult

Add an object or map with key=>value pairs to our TemplateData

usingTemplateString (template:String, ?layout:String, ?templatingEngine:TemplatingEngine):ViewResult

Use a static string as the templates, rather than loading from a UFViewEngine.

If template or layout is not supplied or null, the usual rules will apply for loading a view using the UFViewEngine.

Parameters:

template

The template string for the view.

layout

(optional) The template string for the layout. If not supplied, the layout will be unaffected.

templatingEngine

(optional) The templating engine to render the given view and layout with. If not specified, TemplatingEngine.haxe will be used.

withLayout (layoutPath:String, ?templatingEngine:TemplatingEngine):ViewResult

Specify a layout to wrap this view.

Parameters:

layoutPath
templatingEngine

(optional) A templating engine to use with this layout. If none is specified, the first templating engine matching the layoutPath's extension will be used.

withoutLayout ():ViewResult

Prevent a default layout from wrapping this view - this view will appear standalone, not wrapped by a layout.

Static fields

static globalValues:TemplateData

Global values that should be made available to every view result.

Static methods

staticcreate (data:{}):ViewResult

A shortcut to create a new ViewResult.

This is useful when you are waiting for a Future: return getFutureContent() >> ViewResult.create;