Search code examples
htmlprologswi-prolog

How to add CSS (w/ query results) into dynamic reply_html_page in Prolog?


I'm really having a hard time transitioning to coding in Prolog. When a user submits data from a web page, then I try to return a new page showing the results of a query based on what the user submitted. Here is what I have so far.

Edit: (Replaced code snipped for complete source)

:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).
:- use_module(library(http/http_error)).
:- use_module(library(http/html_write)).
:- use_module(library(http/http_client)).
:- use_module(library(http/http_parameters)).
:- use_module(library(http/html_head)).
:- use_module(library(http/http_server_files)).

:- use_module(parts, [part/4]).

:- http_handler(root(.), main_query, []).
:- http_handler(root(search), search, []).
:- http_handler(root(search), part_results, []).

http:location(css, root(css), []).

server(Port) :-
    http_server(http_dispatch, [port(Port)]).

:- multifile
    user:body//2.

user:body(part_style, Body) -->
    html(body([
        div([id(top)], ['Parts']),
        div(id(content), Body)
        ])).

main_query(Request) :-
    reply_html_page(
        part_style,
        title('Beta Query'),
        [\page_content(Request)]).

page_content(_Request) -->
    {  http_link_to_id(search, [], Ref) },
    html([ \html_requires(css('part-style.css')),
        form([id(search), action='/search', method='POST'], [
        div([id(search_table)], [
            div([class(search_cell)], [
                label([for=part], ' '),
                select([name=part], \parts)
            ]),
            div([class(search_cell)], [
                label([for=major], ' '),
                select([name=major], \majors)
            ]),
            div([class(search_cell)], [
                label([for=minor], ' '),
                select([name=minor], \minors)
            ]),
            p([], [
                input([name=submit, class="button", type=submit,
                    value='Search'], [])
            ])
        ])
        ])
    ]).

css(URL) -->
    html_post(css,
        link([ type('text/css'),
        rel('stylesheet'),
        href(URL)
        ])).

part_results(Request) :-
    reply_html_page(part_style,
        [title('Part Results'),
        \css('part-style.css'),
        \html_receive(css)]).
%       \query_part).
% having trouble truing query_part into DCG.
query_part(Request) :-
    member(method(post), Request), !,
    http_read_data(Request, Data, []),
    format('Content-type: text/html~n~n', []),
    format('<p>', []),                                        
    memberchk(part=Part, Data),
    memberchk(major=M, Data),
    memberchk(minor=I, Data),
    atom_number(M, Major),
    atom_number(I, Minor),
    findall(p(Part, Major, Minor, Desc),
        part(Part, Major, Minor, Desc), Descriptions),
    maplist(desc, Descriptions),  
    format('</p>'),
    format('<a href="http://localhost:8000" class="button">Back</a>').

desc(p(P,M,I,D)) :- format("~q ~q:~q - ~q</br>", [P,M,I,D]).

parts -->
    { findall(P, part(P, Major, Minor, Desc), Parts),
        sort(Parts, Sorted) },
        options(Sorted).

majors -->
    { findall(M, part(Part, M, Minor, Desc), Majors),
        sort(Majors, Sorted) },
        options(Sorted).

minors -->
    { findall(I, part(Part, Major, I, Desc), Minors),
        sort(Minors, Sorted) },
        options(Sorted).

options([]) --> [].
options([O|Os]) -->
    html(option([value=O], O)),
    options(Os).

Trying to define this query_part/1 into a DCG is giving me lots of headaches. Originally the handler directly called the part_query/1, but now there's a part_results rule that calls DCG's and query_part us passed a Request arg. I've been trying to read up on DCG's, but still not quite understanding them other than they are supposed to make writing rules easier, nor do I understand how to use {}'s with them, which I think I need to do. (However, the DCG itself is out of the scope of this question since it covers so much and I can continue trying to understand it elsewhere). So I'm still trying to get the CSS with query results into the reply page.

Currently, the CSS is showing up in the body of the HTML.

Any help much appreciated.


Solution

  • I have several comments about this:

    Immediate solution

    First, a solution for your task: To post the resource, simply use the css//1 nonterminal in part_results/1:

    part_results(Request) :-
        reply_html_page(part_style,
                        [title('Part Results'),
                         \css('myfile.css'),
                         \html_receive(css)],
                        \query_part).
    

    This is enough to obtain what you want. You use css//1 in exactly the same way you use any other nonterminal in the HTTP framework, using the (\)/1 espace syntax.

    Different solution

    A different solution to obtain almost the same result is to use html_requires//1:

    part_results(Request) :-
        reply_html_page(part_style,
                        [title('Part Results'),
                         \html_requires(css('my-style.css'))],
                        \query_part).
    

    This is a library predicate from library(http/html_head), and does essentially what you are doing manually currently.

    Note that you need to use library(http/http_server_files) to define the required locations to resource files. In the case of CSS files, the predefined location is root(css), so you need to put your css file in css/*, which is different from what you are doing manually.

    Why only body?

    In your original example, why was only the body emitted? Answer: Because you did not emit anything else.

    Note that instead of using reply_html_page/3, you can simply emit the whole page yourself on current output. This of course requires that you emit everything you want, not only the body.

    General advice

    Next time, please post a self-contained example, not only partial snippets, to make it easier for others to try your code.

    This means:

    • include all necessary libraries
    • give instructions how to start the server
    • show how to access the server
    • etc.