servicestackservicestack-razor

Passing parameters to HTML (view) outside of DTO


I've got a service that is used both for JSON and HTML. For the HTML rendering, I need to pass some extra parameters that is not suitable to include in the "common DTO". E.g. which CSS file to use, or other things.

For now I've been injecting it into the Request.Items array. Is there a better way, or is this good enough?


Solution

  • The Request.Items Dictionary is what you can use to pass additional data throughout ServiceStack's Request and Response pipeline.

    But you don't need to pass all the data the views need as you can retrieve any additional data you need inside the View itself, e.g. The Email Contacts example shows different ways of accessing data in Razor Views:

    Accessing data in views

    As Razor Pages provide full access to framework features, it enables a few different ways to accesss data from within your pages, shown in info.cshtml:

    Accessing Db Directly

    If you register a DB Factory in your IOC you can use ADO.NET's base.Db IDbConnection property available in Pages and Services and take advantage of the convenience extension methods offered by Micro ORMs like OrmLite and Dapper. E.g you can view all the Contacts inserted in the AppHost using OrmLite's typed APIs with:

    <ul>
        @foreach (var contact in Db.Select<Contact>())
        {
            <li>@contact.Name @contact.Email (@contact.Age)</li>
        }
    </ul>
    

    Accessing Services and Dependencies

    Rather than querying the DB directly another option is to query Services or Repositories which you can resolve from the IOC using Get<T>, e.g:

    <ul>
        @using (var service = Get<ContactsServices>())
        {
            var contacts = service.Any(new FindContacts());
            foreach (var contact in contacts)
            {
                <li>@contact.Name @contact.Email (@contact.Age)</li>
            }
        }
    </ul>
    

    This works because Services are themselves just registered dependencies that you can resolve from the IOC and execute as-is. The one caveat is if your services makes use of the HTTP Request object it will need to be either injected manually or instead of Get<T>, call ResolveService<T> which does it.

    Embedded JSON

    Often using JavaScript ends up being an easier and more flexible alternative to generating HTML than C#. One way to do this is to serialize C# models into JSON which as it's also valid JavaScript, can be accessed directly as a native JS Object. In ServiceStack this is as easy as using the T.AsRawJson() extension method:

    <ul id="embedded-json"></ul>
    
    <script>
    $("#embedded-json").append(
        contactsHtml(@(Db.Select<Contact>().AsRawJson())));
    
    function contactsHtml(contacts) {
        return contacts.map(function (c) {
            return "<li>" + c.Name + " " + " (" + c.Age + ")" + "</li>";
        }).join('');
    }
    </script>
    

    In this example AsRawJson() converts the C# collection into a JSON Array which is automatically inferred as a native JavaScript array when loaded by the browser. It's then passed to the contactsHtml(contacts) JavaScript function that converts it into a HTML string that's injected into the #embedded-json UL HTML element using jQuery's $.append().

    Loaded via Ajax

    The popular alternative to using JavaScript to generate HTML is to load the JSON via Ajax, which as ServiceStack returns pure DTOs serialized into JSON (and respects the HTTP Accept: application/json) becomes as simple as calling your service via its published /route and traversing the resultset directly in JavaScript:

    $.getJSON("/contacts", addContacts);
    
    function addContacts(contacts) {
        $("#ajax").append(contactsHtml(contacts));
    }
    

    Generating HTML via Ajax is effectively the same as Embedded JSON in which we're able to re-use the contactsHtml() method to generate the HTML, the only difference is the JSON is a result of an $.getJSON() ajax call instead of calling the method directly.

    View Model

    A more traditional approach to access data from within a Razor page that is familiar to MVC developers is to have it passed in as the ViewModel into the page. In ServiceStack, you don't need a separate Controller because your existing Services also serves as the Controller for views where its response is used as the ViewModel, in which case the syntax is exactly the same as it is in ASP.NET MVC, i.e:

    @model Contact
    
    <h3>View Model</h3>
    <ul>
        <li>@Model.Name @Model.Email (@Model.Age)</li>
    </ul>