0

I wonder whether there is a way how to share html code snippets in eXist-db. I have two different (more expected later) functions returning the same big html form for different results. It is annoying to maintain the same code when I change something in one of these. I have tried:

  1. Saving it like html file and load it with doc() function (eXist complains it is not an xml file, it is binary.
  2. Saving it like global variable into a separate module (eXist complains there is a problem with contexts). I don’t know how to pass such a variable without the namespace prefix.
  3. Saving it like a function returning its own huge variable (eXist complains there is a problem with contexts).

What is the best practice?

UPDATE

Well, I have tried to put the snippet into a variable insinde a function loaded as a module. For me, it seems reasonable. However, I got an error when try to return that:

err:XPDY0002 Undefined context sequence for 'child::snip:snippet' [at line 62, column 13, source: /db/apps/karolinum-apps/modules/app.xql]
In function:a pp:book-search(node(), map, xs:string?) [34:9:/db/apps/karolinum-apps/modules/app.xql]

I am calling it like so:

declare function app:list-books($node as node(), $model as map(*)) {
    for $resource in collection('/db/apps/karolinum-apps/data/mono')
    let $author := $resource//tei:titleStmt/tei:author/text()
    let $bookName := $resource//tei:titleStmt/tei:title/text()
    let $bookUri := base-uri($resource)
    let $imgPath := replace($bookUri, '[^/]*?$', '')
    let $fileUri := ( '/exist/rest' || $bookUri )
    let $fileName := replace($bookUri, '.*?/', '')
    return
        if ($resource//tei:titleStmt/tei:title)
        then
            snip:snippet
        else ()
};

Any ideas, please?

UPDATE II

Here I have the function in the module:

module namespace snip = "http://46.28.111.241:8081/exist/db/apps/karolinum-apps/modules/snip";

declare function snip:snippet($node as node(), $model as map(*), $author as xs:string, $bookTitle as xs:string, $bookUri as xs:anyURI, $fileUri as xs:anyURI) as element()* {
    let $snippet := 
        (
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title"><a href="{$fileUri}">{$bookTitle}</a> ({$author})</h3>
                </div>
                <div class="panel-body">
                ...
                </div>
        )
        return $snippet
};

Here I am trying to call it:

declare function app:list-books($node as node(), $model as map(*)) {
    for $resource in collection('/db/apps/karolinum-apps/data/mono')
    let $author := $resource//tei:titleStmt/tei:author/text()
    let $bookTitle := $resource//tei:titleStmt/tei:title/text()
    let $bookUri := base-uri($resource)
    let $fileUri := ('/exist/rest' || $bookUri)
    let $fileName := replace($bookUri, '.*?/', '')
    where not(util:is-binary-doc($bookUri))
    order by $bookTitle, $author
    return
        snip:snippet($author, $bookTitle, $bookUri, $fileUri)
};

It throws:

err:XPST0017 error found while loading module app: Error while loading module app.xql: Function snip:snippet() is not defined in namespace 'http://46.28.111.241:8081/exist/db/apps/karolinum-apps/modules/snip' [at line 35, column 9]

When I tried to put the snippet into a variable, it was not possible to pass there those local variables used (it threw $fileUri is not set). Besides that I tried to change the returned type element()* but nothing helped.

Honza Hejzl
  • 874
  • 8
  • 23

2 Answers2

1

All of your approaches should work. Let me address each one:

  1. Is the HTML snippet well-formed XML? If so, save it as, e.g., form.xml or form.html (since by default eXist assumes files with the .html extension are well-formed; see mime-types.xml in your eXist installation folder) and refer to it with doc($path). If it is not well-formed, you can save it as form.txt and pull it in with util:binary-to-string(util:binary-doc($path)). Or make the HTML well-formed and use the first alternative.

  2. This too is valid, so you must not be properly declaring or referring to the global variable. What is the exact error you are getting? Can you post a small example snippet that we could run to reproduce your results?

  3. See #2.

Joe Wicentowski
  • 5,159
  • 16
  • 26
  • Thanks a lot. In general, I mean something like `
    ...
    ` In the `div`, there is the whole big form. I am not sure whether it is strictly valid XML (eXist does not complain). But it uses a couple of variables. The function simply does not return it (for now). Ah, yep, and those variable are not active (pass like literals).
    – Honza Hejzl Jan 27 '16 at 14:05
  • If eXist doesn't complain, then it's valid XML. But don't expect "variables" (by which I think you must mean XQuery) to "work" if stored inside a .xml or .html file; such files are treated as static documents, not evaluated like .xq files. If you need your form to be dynamic, you definitely need to use methods #2 or #3. Or, a fourth option: use eXist's templating facility: http://exist-db.org/exist/apps/doc/templating.xml. This could be quite well suited for your task. – Joe Wicentowski Jan 27 '16 at 14:15
  • Well, I use the templating, but don’t know if it is possible to nest functions. In the template, I call `app:list-books`, the result is a list of books, for every one of them there is a form. Is it possible to use template function call inside another template function? – Honza Hejzl Jan 27 '16 at 14:22
1

I was very close. It was necessary to somehow pass parameters to the nested function and omit eXist’s typical $node as node(), $model as map(*) as arguments.

Templating function:

declare function app:list-books($node as node(), $model as map(*)) {
    for $resource in collection('/db/apps/karolinum-apps/data/mono')
    let $author := $resource//tei:titleStmt/tei:author/text()
    let $bookTitle := $resource//tei:titleStmt/tei:title/text()
    let $bookUri := base-uri($resource)
    let $bookId := xs:integer(util:random() * 10000)
    let $fileUri := ('/exist/rest' || $bookUri)
    let $fileName := replace($bookUri, '.*?/', '')
    where not(util:is-binary-doc($bookUri))
    order by $bookTitle, $author
    return
        snip:snippet($author, $bookTitle, $bookUri, $bookId, $fileUri)
};

Snippet function:

declare function snip:snippet($author as xs:string, $bookTitle as xs:string, $bookUri as xs:anyURI, $bookId as xs:string, $fileUri as xs:anyURI) as element()* {
    let $snippet := 
        (
            <div class="panel panel-default">
            ...
           </div>
       )
   return $snippet
};
Honza Hejzl
  • 874
  • 8
  • 23