{"id":37902365,"url":"https://github.com/loadingio/ldview","last_synced_at":"2026-01-16T17:05:35.642Z","repository":{"id":44740661,"uuid":"189699556","full_name":"loadingio/ldview","owner":"loadingio","description":"A headless, logic-less HTML template engine.","archived":false,"fork":false,"pushed_at":"2026-01-14T08:42:27.000Z","size":1688,"stargazers_count":2,"open_issues_count":5,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-14T12:20:06.917Z","etag":null,"topics":["dom","template-engine","view"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/loadingio.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2019-06-01T06:38:11.000Z","updated_at":"2026-01-14T08:42:20.000Z","dependencies_parsed_at":"2024-04-28T17:23:57.384Z","dependency_job_id":"4754d816-8ccc-4322-aa5d-7ef43e80c309","html_url":"https://github.com/loadingio/ldview","commit_stats":{"total_commits":135,"total_committers":1,"mean_commits":135.0,"dds":0.0,"last_synced_commit":"d5a49676abcac2b5bee591c4ae54c53b039d207c"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/loadingio/ldview","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loadingio%2Fldview","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loadingio%2Fldview/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loadingio%2Fldview/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loadingio%2Fldview/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/loadingio","download_url":"https://codeload.github.com/loadingio/ldview/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loadingio%2Fldview/sbom","scorecard":{"id":596468,"data":{"date":"2022-08-15","repo":{"name":"github.com/loadingio/ldview","commit":"9f3acaecbf7c2f80271cc47fb8e46a1d46ae9a5d"},"scorecard":{"version":"v4.5.0-26-g10b6052","commit":"10b6052acfb4f0b8136bc4876cb6f5b6f26bfe93"},"score":4.8,"checks":[{"name":"Maintained","score":4,"reason":"5 commit(s) out of 30 and 0 issue activity out of 4 found in the last 90 days -- score normalized to 4","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/10b6052acfb4f0b8136bc4876cb6f5b6f26bfe93/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"no reviews found","details":["Warn: no reviews found for commit: 9f3acaecbf7c2f80271cc47fb8e46a1d46ae9a5d","Warn: no reviews found for commit: 034edc74c2da49d35c305fd92191f59e71f571c1","Warn: no reviews found for commit: b012c1b725f4ed7d38547547e20d71339c3eef3b","Warn: no reviews found for commit: 5e22248736a804f7134c42ab17ec1b4da67a1a70","Warn: no reviews found for commit: 8b7b572a9d1b1320a4fba253881730233cced2f1","Warn: no reviews found for commit: c323b86773ec61695aa1055ed6667fb3a492b2fc","Warn: no reviews found for commit: 51f5d5bf3ebe558fb36dca7ec3d3f55830708680","Warn: no reviews found for commit: 968bb8f1d5dbed01972aa3f1d4668c26201c2523","Warn: no reviews found for commit: 017f9c8ac832e2d060ce60b4017c9a7d61e44cb9","Warn: no reviews found for commit: 7158b62a1893afa26333c6107e6c47dab8695071","Warn: no reviews found for commit: 5451d4c723b2ab58487424628828ef032b56d6a8","Warn: no reviews found for commit: 1f6fea5352ad3528c37db00343f34356baf16543","Warn: no reviews found for commit: bc1f3b0fd4cab34f8adf5dd2c28d70ba84c01877","Warn: no reviews found for commit: 083806c82b40f447a911d3afe3b1444ca304e775","Warn: no reviews found for commit: 84e9693110fc2747d69b2bfbf7e6957de01bddbb","Warn: no reviews found for commit: 69b3863db94b623cbffa65c6627bf7520cccb4f4","Warn: no reviews found for commit: 7a0d3422ccd16b66ca5e52e1204d4303a3131836","Warn: no reviews found for commit: e06c7e252dcfd1fc08d07b3e0f083a51ac013919","Warn: no reviews found for commit: 6072784ad56c312db8b1d7aa020b757ea94172ee","Warn: no reviews found for commit: b89c4f37ab25e1f152d86ee4b95481f05af858a7","Warn: no reviews found for commit: 76c7fbf219dae7bbea78a8df7744df68a5fe195f","Warn: no reviews found for commit: 367b716e1d9d70937059faae1175243b827487f4","Warn: no reviews found for commit: ed930ccdfd02df15f94635b0cdc3788fd85af8e5","Warn: no reviews found for commit: 31edc6ffaa338c3780699cf7167de8bcb14a7209","Warn: no reviews found for commit: 642c08693ee53efd047258039b657c5af82ea455","Warn: no reviews found for commit: 4ca89ec43a3200afea90f819a8b0b703c4e1c351","Warn: no reviews found for commit: 9aad63707437169e95ef455f2430d67a9d222ec1","Warn: no reviews found for commit: 5a6291d6cb247541daf3fdef0bd918696c87c7af","Warn: no reviews found for commit: 2f49ece84a5331c33971b453911aca4e6190f44e","Warn: no reviews found for commit: 93faef730401e32209bc4bbcfc76b91d48621bf6"],"documentation":{"short":"Determines if the project requires code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/10b6052acfb4f0b8136bc4876cb6f5b6f26bfe93/docs/checks.md#code-review"}},{"name":"CII-Best-Practices","score":0,"reason":"no badge detected","details":null,"documentation":{"short":"Determines if the project has a CII Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/10b6052acfb4f0b8136bc4876cb6f5b6f26bfe93/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"no vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/10b6052acfb4f0b8136bc4876cb6f5b6f26bfe93/docs/checks.md#vulnerabilities"}},{"name":"Signed-Releases","score":0,"reason":"0 out of 5 artifacts are signed or have provenance","details":["Warn: release artifact v1.2.1 does not have provenance: https://api.github.com/repos/loadingio/ldview/releases/70456446","Warn: release artifact v1.2.1 not signed: https://api.github.com/repos/loadingio/ldview/releases/70456446","Warn: release artifact v1.2.0 does not have provenance: https://api.github.com/repos/loadingio/ldview/releases/70455875","Warn: release artifact v1.2.0 not signed: https://api.github.com/repos/loadingio/ldview/releases/70455875","Warn: release artifact v1.1.1 does not have provenance: https://api.github.com/repos/loadingio/ldview/releases/59700241","Warn: release artifact v1.1.1 not signed: https://api.github.com/repos/loadingio/ldview/releases/59700241","Warn: release artifact v1.1.0 does not have provenance: https://api.github.com/repos/loadingio/ldview/releases/57813301","Warn: release artifact v1.1.0 not signed: https://api.github.com/repos/loadingio/ldview/releases/57813301","Warn: release artifact v1.0.0 does not have provenance: https://api.github.com/repos/loadingio/ldview/releases/57464015","Warn: release artifact v1.0.0 not signed: https://api.github.com/repos/loadingio/ldview/releases/57464015"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/10b6052acfb4f0b8136bc4876cb6f5b6f26bfe93/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/10b6052acfb4f0b8136bc4876cb6f5b6f26bfe93/docs/checks.md#branch-protection"}},{"name":"Packaging","score":-1,"reason":"no published package detected","details":["Warn: no GitHub publishing workflow detected"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/10b6052acfb4f0b8136bc4876cb6f5b6f26bfe93/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/10b6052acfb4f0b8136bc4876cb6f5b6f26bfe93/docs/checks.md#dangerous-workflow"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: : LICENSE:1"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/10b6052acfb4f0b8136bc4876cb6f5b6f26bfe93/docs/checks.md#license"}},{"name":"Token-Permissions","score":10,"reason":"tokens are read-only in GitHub workflows","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/10b6052acfb4f0b8136bc4876cb6f5b6f26bfe93/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":10,"reason":"all dependencies are pinned","details":["Info: GitHub-owned GitHubActions are pinned","Info: Third-party GitHubActions are pinned","Info: Dockerfile dependencies are pinned","Info: no insecure (not pinned by hash) dependency downloads found in Dockerfiles","Info: no insecure (not pinned by hash) dependency downloads found in shell scripts"],"documentation":{"short":"Determines if the project has declared and pinned its dependencies.","url":"https://github.com/ossf/scorecard/blob/10b6052acfb4f0b8136bc4876cb6f5b6f26bfe93/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/10b6052acfb4f0b8136bc4876cb6f5b6f26bfe93/docs/checks.md#binary-artifacts"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":null,"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/10b6052acfb4f0b8136bc4876cb6f5b6f26bfe93/docs/checks.md#security-policy"}},{"name":"Dependency-Update-Tool","score":0,"reason":"no update tool detected","details":["Warn: dependabot config file not detected in source location.\n\t\t\tWe recommend setting this configuration in code so it can be easily verified by others.","Warn: renovatebot config file not detected in source location.\n\t\t\tWe recommend setting this configuration in code so it can be easily verified by others."],"documentation":{"short":"Determines if the project uses a dependency update tool.","url":"https://github.com/ossf/scorecard/blob/10b6052acfb4f0b8136bc4876cb6f5b6f26bfe93/docs/checks.md#dependency-update-tool"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":null,"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/10b6052acfb4f0b8136bc4876cb6f5b6f26bfe93/docs/checks.md#fuzzing"}}]},"last_synced_at":"2025-08-20T23:15:43.167Z","repository_id":44740661,"created_at":"2025-08-20T23:15:43.167Z","updated_at":"2025-08-20T23:15:43.167Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28480081,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T11:59:17.896Z","status":"ssl_error","status_checked_at":"2026-01-16T11:55:55.838Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dom","template-engine","view"],"created_at":"2026-01-16T17:05:35.171Z","updated_at":"2026-01-16T17:05:35.635Z","avatar_url":"https://github.com/loadingio.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ldview\n\nA headless, logic-less HTML template engine.\n\n\n## Usage\n\nInstall via npm:\n\n    npm install --save ldview\n\nand include in your page:\n\n    \u003cscript src=\"path-to-ldview/index.min.js\"\u003e\u003c/script\u003e\n\nconstruct a ldview base on how you'd like to render. For example, this render `name` tag with username:\n\n    view = new ldview do\n      root: someDomRoot\n      ctx: -\u003e user or {}\n      handler: name: ({node, ctx}) -\u003e node.textContent = ctx.user?name or 'unnamed'\n\nwhich corresponds to HTML (written in Pug) such as:\n\n    #someDomRoot: div(ld=\"name\")\n\n\nYou can merge multiple configs by constructing ldview with multiple object:\n\n    view = new ldview({root: document.body}, {handler: {...}}, {text: {...}});\n\nYou can also use `ldview.merge` to manually combine the configuration object. see `Class API` for detail.\n\n\n### Basic Concept\n\nldview works by defining the binding between JS functions and element by `ld` attribute - consider it as the concept of `JS Selector`, like `CSS Selector`. We name elements and assign processors in JavaScript according to their names.\n\nFor example, following code names three DIVs with \"ld\" attributes in \"plan free\", \"plan month\", and \"plan year\":\n\n    body\n      div(ld=\"plan free\")\n      div(ld=\"plan month\")\n      div(ld=\"plan year\")\n\n\nTo bind the corresponding processor, create a new ldview object with a handler object:\n\n    view = new ldview do\n      root: document.body\n      handler:\n        # this example actually demonstrates how to do a if/else or switch/case statement.\n        plan: ({node, names, name, idx, ctx, local, views}) -\u003e\n          node.style.display = (if currentPlan in names =\u003e 'block' else 'none')\n\nview by default will be rendered after initialized, but you can render it again with `render` api:\n\n    view.render!\n\nTo separate `init` from `render`, use `init` handler:\n\n    view = new ldview do\n      root: document.body\n      init: plan: -\u003e /* do\n\ninitialization by default will be done once the view is created unless `initRender` option is false; in this case, you can initialize manually with `init` function:\n\n    view.init!then -\u003e /* run after initialized */\n\nThere are other types of handler, such as `action`, `text`, `style` and `attr`, See below for more information.\n\n    view = new ldview do\n      root: someDomNode\n      init: someSelector: (-\u003e), ...\n      text: someSelector: (-\u003e), ...\n      style: someSelector: (-\u003e), ...\n      attr: someSelector: (-\u003e), ...\n      handle: someSelector: (-\u003e), ...\n      action: /* unlike above handlers, first level of action contains different event names */\n        click: someSelector: (-\u003e),\n        mousedown: someSelector: (-\u003e),\n        ....\n\nin `handler` you can also loop a node with selector defined `ld-each` attribute, which is described in the next section.\n\n\n### Loop with ld-each\n\nldview supports looping too. Declare an element to be looped with \"ld-each\" attribute:\n\n    .shelf: div(ld-each=\"book\")\n\nthe element with \"book\" ld-each attribute will be replaced by a comment node. Then, you can bind it with an array of elements to automatically generate a list of similar book elements with a slightly different handler config:\n\n    new ldview do\n      handler:\n        # instead of a simple handler function,\n        # here we have an object containing a list function and a handler function\n        book:\n          # tell ldview to map book elements to myBookList\n          list: -\u003e myBookList\n\n          # optional key getter for stable update\n          # key: -\u003e it.key\n\n          # node is one of the nodes cloned from the original book element\n          # and the data is entry bound to node from myBookList.\n          handler: ({node,data,name}) -\u003e\n\nin list config, you can use all configs available for a generic items. for example,\n\n    book:\n      list: -\u003e ...\n      init: ({node, data, name, idx}) -\u003e\n      handler: ({node, data, name, idx}) -\u003e ...\n      text: -\u003e ...\n      action: click: ({node, name, evt, idx}) -\u003e ...\n\n\n### Loop with Nested View\n\nUsually you will want to render nodes below the looped element:\n\n  .shelf\n    div(ld-each=\"book\")\n      .name(ld=\"name\") Sample Name\n      .author(ld=\"author\") Sample Author\n\nThis requires a nested ldview construction like below if done manually:\n\n    new ldview do\n      handler:\n        book:\n          list: -\u003e myBookList\n          # manual approach - use init to create nested view.\n          # for illustration only, don't replicate.\n          init: ({node,data}) -\u003e\n            (new ldview do\n               root: node,\n               handler:\n                 name: (.node.textContent = data.name)\n                 author: (.node.textContent = data.author)\n            ).render!\n\nhowever, ldview already does this for you. Simply move the view config under `view` field after `list`:\n\n    new ldview do\n      handler:\n        book:\n          list: -\u003e myBookList\n          # here is the trick\n          view:\n            handler:\n              name: ({node, ctx}) -\u003e node.textContent = ctx.name\n              author: ({node, ctx}) -\u003e node.textContent = ctx.author\n\nIn nested case, following constructor fields inherits the parent view configs unless you overwrite them explicitly:\n\n - `initRender`\n - `root`\n - `baseViews`\n - `ctx`\n - `ctxs`\n\nWhen you apply nested config with circular reference to itself, you can then construct a recursive view:\n\n    viewcfg = {}\n    viewcfg.handler = book:\n      list: ({ctx}) -\u003e ctx.child\n      view: viewcfg\n\nCheck `Recursive Views and Template` section below.\n\n\n### Partial Rendering\n\nAfter initialization, You probably will want to update some elements instead of updating every node. Just pass target names into render function:\n\n    view = new ldview( ... );\n    view.render!\n    # after some updates ... only update ld=\"name\" elements.\n    view.render \u003c[name]\u003e\n\nFor updating partial entries in `ld-each`, use following syntax with keys:\n\n    view.render {name: 'some-ld-each-name', key: [key1, key2, ... ]}\n\nBe sure to make sure keys here matches the return value of `key` accessor, in case of matching failure.\n\n\n### Customize ld-each Behavior / List Optimization\n\nYou can also specify `host` parameter to tell ldview how to process child elements. For example, with a large list of data, we may want to use `@loadingio/vscroll` for virtual scrolling, which effectively reduces amount of elements in the DOM tree:\n\n    new ldview({\n      handler:\n        item:\n          host: vscroll.fixed\n          list: -\u003e ...\n          ...\n    })\n\nwhere the `host` parameter should be a constructor that mimic basic DOM element interface. For more information, check `@loadingio/vscroll`.\n\n\n### Nested View and Scoping\n\nYou can use nested view on a non-looping selector, which makes you possible to reuse configs and provide better modularization:\n\n    viewcfg = text: name: ({ctx}) -\u003e ctx.name\n    new ldview(\n      {\n        handler:\n          userInfo: viewcfg\n          classInfo: viewcfg\n      },\n      viewcfg\n    )\n\nhowever, this also introduces name conflict if selectors with the same name are used across modules, so you may want to prevent ldview from selecting nodes that belongs to nested views, such as `name` in below DOM:\n\n    div(ld=\"name\")\n    div(ld=\"userInfo\"): div(ld=\"name\")\n    div(ld=\"classInfo\"): div(ld=\"name\")\n\nTo separate the scope of interest, use `ld-scope` tag to scope the DOM fragment:\n\n    div(ld=\"name\")\n    div(ld-scope=\"scope-name-1\",ld=\"userInfo\"): div(ld=\"name\")\n    div(ld-scope=\"scope-name-2\",ld=\"classInfo\"): div(ld=\"name\")\n\nldview also provides a `scope` pug mixin and `scope` function in ldview's `index.pug`:\n\n    include /path-to-ldview/index.pug\n    +scope(\"scope-name\")\n      div(ld=\"node-name\") my element.\n\n`ld-scope` prevents other views to look up elements inside it.\n\n\n### Using Scope with Prefix for Mixed Views\n\nIf you want to mix views, you can set the scope to `naked` by adding a `naked-scope` class:\n\n    +scope(\"scope-name\").naked-scope\n      div(ld=\"node-name\") my element\n\nThis will output following:\n\n    \u003cdiv ld=\"node-name\"\u003e my element \u003c/div\u003e\n\nWhile this seems to equal to doing nothing, you can prefix `ld` attribute by `scope-name` with `prefix` function in order to distinguish elements for different views:\n\n    +scope(\"scope-name\").naked-scope\n      div(ld=prefix(\"node-name\")) my element\n      div(ld=\"global-name\") global element\n\nbecomes\n\n    \u003cdiv ld=\"scope-name$node-name\"\u003e my element \u003c/div\u003e\n    \u003cdiv ld=\"global-name\"\u003e global element \u003c/div\u003e\n\nTo access prefix-ed node, adding `prefix` option when initializing ldview:\n\n    var localView = new ldview({prefix: 'scope-name', handler: {\n      \"node-name\": function(node) { ... }\n    });\n    var localNode = localView.get(\"node-name\");\n\n    var globalView = new ldview({handler: {\n      \"node-name\": function(node) { ... }\n    });\n    var globalNode = globalView.get(\"global-name\");\n\n\nBasically `Scope` and `Prefix` are mutual exclusive; with `scope` you don't have to prefix since only you can access `ld` elements within this scope.\n\n\n## Configurations\n\nConstruct a ldview object with one (or more) configuration object with following fields:\n\n * `root` - view root\n * `handler` - object containing name / handler pairs.\n   - name will be used when querying DOM in `ld` attribute.\n   - handler accept an object as argument:\n   - node: the target node\n * `action` - action handler. object containing event names such as click, mousemove, etc.\n   - each member contains a handler object similar to the root handler.\n   - example:\n\n    action: {\n      click: {\n        buy: ({node, evt}) -\u003e ...\n      }\n      change: {\n        name: ({node, evt}) -\u003e ...\n        title: ({node, evt}) -\u003e ...\n      }\n\n * `prefix` - prefix name for this view. view will be global if scope name is not defined.\n   this should be used along with the scope pug mixin.\n * `initRender` - if set to true, ldview automatically calls render immediately after initialized. default true\n * `global` - set true to use `pd` and `pd-each` for access nodes globally beyond ld-scope. default false.\n * `ctx` - default data accessible with in handler functions. can be set later with `setContext` api.\n   - `context` is used as `ctx` before `0.1.0`, and it's now `ctx`.\n * `template` - template DOM for replacing root content. It's for supporting recursive views.\n * `ctxs` and `baseViews` - these config are used internally. Don't use this unless you know what's your doing.\n\n\n## API\n\n * new ldview({root, handler , ...})\n   handler: hash with node-names as key and function as value.\n   - function: ({node}) which node is the element matched with this node-name.\n * view.getAll(\"node-name\") - return a list of nodes in the name of node-name.\n * view.get(\"node-name\") - return the first node with the name of node-name. shorthand for getAll(...)[0]\n * view.init(cfg) - return a Promise that resolves after all init resolves.\n * view.render(cfg)\n * view.ctx(v) - set a custom context object for using in handler functions.\n   - return current context if `v` is not defined.\n   - `setCtx()` is used before `2.0.0`. use `ctx()` now.\n   - `setContext()` is used before `0.1.0`. use `ctx()` now.\n * view.bindEachNode({container, name, node, idx})\n   - ldview keeps track of nodes once they are created as in ld-each.\n     If for some reason we need a node to be removed from ld-each list but use in other place ( e.g.,\n     when dragging outside we need the dragged node to exist for better user experience ), we can\n     unbind it and rebind it later.\n   - while node is removed from / inserted into ld-each node list, these functions wont update data.\n     User should update data themselves otherwise inserted node will be deleted / removed node will\n     be re-created in the next render call.\n   - parameters:\n     - container: container of these ld-each nodes.\n     - idx: index to insert this node.\n     - node: node to be inserted.\n     - name: name of ld-each.\n * view.unbindEachNode({container, name, node, idx})\n   - counterpart of bindEachNode.\n   - parameters:\n     - container: container of these ld-each nodes.\n     - idx: if provided, remove node in this position and return it.\n     - node: if idx is not provided, user can use the node itself to hint ldview.\n     - name: name of ld-each.\n\n\n## Handler Parameters\n\nWhen handlers for each ld node is called, it contains following parameters:\n\n * `node` - current node\n * `names` - all name in ld/pd for current node, space separated.\n * `name` - matched name for current handler of this node.\n * `idx` - index of current node, if this rule matches multiple times.\n * `local` - local data for storing information along with the node, in its life cycle.\n * `ctx` - view-wise data, set via `setCtx` API. default null.\n   - `context` is used before 0.1.0. use `ctx` now.\n * `data` - only for `ld-each` node. bound data for this node.\n * `evt` - event object if this handler is an event handler.\n * `evts` - hash for listing all bound events.\n * `ctxs` - contexts in all parent view when using nested view feature.\n * `views` - list of views (including views built recursively) for invoking this handler\n   - `views[0]` is always the current view. larger number gets ancestor views.\n\n\n## Recursive Views and Template\n\nIt's possible to define view recursively - simply refer a view config in itself:\n\n    viewcfg = {}\n    viewcfg.handler = someDirective:\n      list: -\u003e ...\n      view: viewcfg\n\nPlease note that the first `viewcfg = {}` is necessary since cfg won't be available for recursive definition when we initialize it, such as:\n\n    viewcfg = handler: someDirective: {\n      list: -\u003e ...\n      # incorrect: cfg is not available here.\n      view: viewcfg\n    }\n\nAdditionally, we need also a recursively defined DOM, which is only possible with `template` option:\n\n    div(data-name=\"template\"): ...\n    script(type=\"text/livescript\").\n      cfg = {}\n      new ldview cfg \u003c\u003c\u003c {\n        template: document.querySelector('div[data-name=template]')\n        handler: someDirective: {\n          list: ({ctx}) -\u003e ...\n          view: cfg\n        }\n      }\n\nIn this case, the div named `template` will be cloned, attached and used as inner DOM of this view, recursively applied according to the return content of `list`. For a working example, check `web/src/pug/recurse/index.ls`.\n\nAlso please note that `ctx` should not be defined in the reused `cfg`, otherwise it may cause infinite recursive calls, leading to maximal callstack exceeded exception. Following is a correct example:\n\n    viewcfg = {}\n    viewcfg.handler = myselector: view: viewcfg\n    rootcfg = ldview.merge({ctx: mydata}, viewcfg)\n\nor\n    viewcfg = {}\n    rootcfg = {ctx: mydata} \u003c\u003c\u003c viewcfg \u003c\u003c\u003c handler: myselector: view: viewcfg\n\n\nA workable, complete example with both JS and HTML (written in Pug) is as below:\n\n    //- HTML\n    .template #[div(ld=\"title\")]#[div(ld-each=\"child\")]\n    #root\n\n    # JS - DATA\n    ctx = name: \"root\", list: [\n    * name: \"entry1\", list: [{name: \"sub entry 1.1\"}]\n    * name: \"entry2\", list: [{name: \"sub entry 2.1\"}, {name: \"sub entry 2.2\"}]\n    * name: \"entry3\", list: [{name: \"sub entry 3.1\"}, {name: \"sub entry 3.2\"}]\n    ]\n    # JS - View Code\n    cfg = {}\n    cfg \u003c\u003c\u003c\n      text: title: ({ctx}) -\u003e ctx.name\n      handler: child:\n        view: cfg\n        list: ({ctx}) -\u003e ctx.list or []\n    view = new ldview({root: root, ctx: -\u003e list} \u003c\u003c\u003c cfg)\n\n\n## Nested Views and Template\n\nAs described above, it's possible to nest a view in a selector:\n\n    new ldview({\n      handler: localView:\n        handler: ...\n    });\n\nAnd we use `ld-scope` to prevent selectors inside localView to be accessed by parent view:\n\n    div(ld-scope,ld=\"localView\"): ...\n\n\nIt can also be accomplished via `template`, which is by default scoped:\n\n    new ldview({\n      handler: localView:\n        template: ...\n        handler: ...\n    });\n\nYou can also define a ctx function, with parent ctx in its parameter:\n\n    new ldview({\n      ctx: -\u003e { node: { name: \"something\" }}\n      handler: localView:\n        template: ...\n        ctx: ({ctx}) -\u003e ctx.node\n        handler: ...\n    });\n\n    \ncheck `web/src/pug/scope/index.ls` for a working example of template-based local views.\n\n\n## Class API\n\n - `merge(a, b, ...)`: merge view config objects and returns the merged result.\n\n\n## License\n\nMIT\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Floadingio%2Fldview","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Floadingio%2Fldview","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Floadingio%2Fldview/lists"}