{"id":13798248,"url":"https://github.com/tobiasdiez/EasyBind","last_synced_at":"2025-05-13T05:31:56.377Z","repository":{"id":39878680,"uuid":"261222209","full_name":"tobiasdiez/EasyBind","owner":"tobiasdiez","description":"Custom JavaFX bindings made easy with lambdas.","archived":false,"fork":true,"pushed_at":"2024-07-22T20:15:30.000Z","size":357,"stargazers_count":30,"open_issues_count":9,"forks_count":6,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-08-04T00:03:00.223Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"TomasMikula/EasyBind","license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tobiasdiez.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["tobiasdiez"]}},"created_at":"2020-05-04T15:24:12.000Z","updated_at":"2024-07-28T15:36:16.000Z","dependencies_parsed_at":"2023-02-10T08:31:51.047Z","dependency_job_id":null,"html_url":"https://github.com/tobiasdiez/EasyBind","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobiasdiez%2FEasyBind","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobiasdiez%2FEasyBind/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobiasdiez%2FEasyBind/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobiasdiez%2FEasyBind/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tobiasdiez","download_url":"https://codeload.github.com/tobiasdiez/EasyBind/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225183675,"owners_count":17434143,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2024-08-04T00:00:40.940Z","updated_at":"2024-11-18T13:30:52.260Z","avatar_url":"https://github.com/tobiasdiez.png","language":"Java","funding_links":["https://github.com/sponsors/tobiasdiez"],"categories":["Community"],"sub_categories":["Libraries"],"readme":"# EasyBind\n\nEasyBind leverages lambdas to reduce boilerplate when creating custom bindings, provides a type-safe alternative to `Bindings.select*` methods and adds provides enhanced bindings support for `Optional`.\n\nSee below for [how to install EasyBind in your project](#use-easybind-in-your-project).\n\nThis is a maintained fork of the [EasyBind library by Tomas Mikula](https://github.com/TomasMikula/EasyBind), which sadly is dormant at the moment.\n\n## Getting started\n\nThe simplest way is to use the `EasyBind.wrap*` methods to create wrappers around standard JavaFX observable values or lists.\nThe wrapper then gives you access to all the features of EasyBind.\n\nFor example,\n\n```java\nObservableStringValue str = ...;\nBinding\u003cInteger\u003e length = EasyBind.wrap(str)\n                                  .map(String::length);\n```\n\ncreates a `Binding` that holds the length of `str`.\nSimilarly,\n\n```java\nObservableList\u003cString\u003e list = ...;\nBinding\u003cBoolean\u003e allMatch = EasyBind.wrap(list)\n                                    .allMatch(String:isEmpty) \n```\n\nyields a `Binding` that reflects whether all items in the list are empty strings.\nIn addition to the `wrap*` methods, EasyBind also provides direct shortcuts for common functionality.\nFor example, the above binding could be more shortly written as `EasyBind.map(String::length)`.\n\n## Features\n\n### Map observables\n\nCreates a binding whose value is a mapping of some observable value.\n\n```java\nObservableStringValue str = ...;\nBinding\u003cInteger\u003e length = EasyBind.map(str, String::length);\n```\n\n### Combine observables\n\nCreates a binding whose value is a combination of two or more observable values.\n\n```java\nObservableStringValue str = ...;\nObservableValue\u003cInteger\u003e start = ...;\nObservableValue\u003cInteger\u003e end = ...;\nBinding\u003cString\u003e substring = EasyBind.combine(str, start, end, String::substring);\n```\n\n### Select properties\n\nType-safe alternative to `Bindings.select*` methods.\n\n```java\nBinding\u003cBoolean\u003e showing = EasyBind.select(control.sceneProperty()) \n                                   .select(scene -\u003e scene.windowProperty()) \n                                   .selectObject(window -\u003e window.showingProperty());\n```\n\nThe resulting binding is updated whenever one of the properties in the selection graph changes.\n\n### Map items in a lists\n\nReturns a mapped view of an `ObservableList`.\n\n```java\nObservableList\u003cString\u003e items = ...;\nObservableList\u003cInteger\u003e lengths = EasyBind.map(items, String::getLength);\n```\n\nIn the above example, `lengths` is updated as elements are added and removed from `items`.\nBy design, the elements of the new observable are calculated on the fly whenever they are needed (e.g. if `get` is called).\nThus, this is prefect for light-weight operations.\nIf the conversion is a cost-intensive operation or if the elements of the list are often accessed, then using `mapBacked` is a better option.\nHere the elements of the list are converted once and then stored in memory.\n\n### Reduce observable lists\n\nUsing `reduce` you can aggregate an observable list of items into a single observable value.\n\n```java\nObservableList\u003cString\u003e items = ...;\nObservableValue\u003cBoolean\u003e totalLength = EasyBind.reduce(items, stream -\u003e stream.mapToInt(String::length).sum());\n```\n\n### Reduce observable lists of observables\n\nMore advanced, the `combine` method turns an _observable list_ of _observable values_ into a single observable value. The resulting observable value is updated whenever elements are added or removed to or from the list, as well as when element values change.\n\n```java\nProperty\u003cInteger\u003e a = new SimpleObjectProperty\u003c\u003e(5);\nProperty\u003cInteger\u003e b = new SimpleObjectProperty\u003c\u003e(10);\nObservableList\u003cProperty\u003cInteger\u003e\u003e list = FXCollections.observableArrayList();\n\nBinding\u003cInteger\u003e sum = EasyBind.combine(\n        list,\n        stream -\u003e stream.reduce((a, b) -\u003e a + b).orElse(0));\n\nassert sum.getValue() == 0;\n\n// sum responds to element additions\nlist.add(a);\nlist.add(b);\nassert sum.getValue() == 15;\n\n// sum responds to element value changes\na.setValue(20);\nassert sum.getValue() == 30;\n\n// sum responds to element removals\nlist.remove(a);\nassert sum.getValue() == 10;\n```\n\nYou don't usually have an observable list of _observable_ values, but you often have an observable list of something that _contains_ an observable value. In that case, use the above `map` methods to get an observable list of observable values, as in the example below.\n\n\u003cdetails\u003e\n\u003csummary\u003e\nExample: Disable \"Save All\" button on no unsaved changes\n\u003c/summary\u003e\nAssume a tab pane that contains a text editor in every tab. The set of open tabs (i.e. open files) is changing. Let's further assume we use a custom `Tab` subclass `EditorTab` that has a boolean `savedProperty()` indicating whether changes in its editor have been saved.\n\n**Task:** Keep the _\"Save All\"_ button disabled when there are no unsaved changes in any of the editors.\n\n```java\nObservableList\u003cObservableValue\u003cBoolean\u003e\u003e individualTabsSaved =\n        EasyBind.map(tabPane.getTabs(), tab -\u003e ((EditorTab) tab).savedProperty());\n\nObservableValue\u003cBoolean\u003e allTabsSaved = EasyBind.combine(\n        individualTabsSaved,\n        stream -\u003e stream.allMatch(saved -\u003e saved));\n\nButton saveAllButton = new Button(...);\nsaveAllButton.disableProperty().bind(allTabsSaved);\n```\n\n\u003c/details\u003e\n\n### Concat lists (of observable lists)\n\nThe `concat` method combines two or more observable lists into one big list containing all items.\n\n```java\nObservableList\u003cString\u003e listA = ...;\nObservableList\u003cString\u003e listB = ...;\nObservableList\u003cString\u003e combinedList = EasyBind.concat(listA, listB);\n```\n\nSimilarly, an observable list of observable lists can be combined into one big list containing all items of all lists as follows:\n\n```java\nObservableList\u003cObservableList\u003cString\u003e\u003e listOfLists = ...;\nObservableList\u003cString\u003e allItems = EasyBind.flatten(listOfLists);\n```\n\n### Subscribe to values\n\nOften one wants to execute some code for _each_ value of an `ObservableValue`, that is for the _current_ value and _each new_ value. This typically results in code like this:\n\n```java\nthis.doSomething(observable.getValue());\nobservable.addListener((obs, oldValue, newValue) -\u003e this.doSomething(newValue));\n```\n\nThis can be expressed more concisely using the `subscribe` helper method:\n\n```java\nEasyBind.subscribe(observable, this::doSomething);\n```\n\nIn case `doSomething` should not be invoked immediately, `EasyBind.listen(observable, this::doSomething)` should be used instead.\n\n### Conditional bindings\n\nUsing `when` you can create bindings that should only be realized if a given observable boolean is true.\n\n#### Conditional collection membership\n\nThe method `includeWhen` includes an element in a collection based on a boolean condition.\n\nSay that you want to draw a line and highlight it when its hovered over. To achieve this, let's add `.highlight` CSS class to the line node when it is hovered over and remove it when it is not:\n\n```java\nEasyBind.includeWhen(edge.getStyleClass(), \"highlight\", line.hoverProperty());\n```\n\n### Optional observable values\n\nOne often faces the situation that observables take `null` values.\nThe `wrapNullable` provides a wrapper around the observable that provides convenient helper methods similar to the `Optional` class.\n\n```java\ninterface ObservableOptionalValue\u003cT\u003e {\n    BooleanBinding isPresent();\n    BooleanBinding isEmpty();\n    Subscription listenToValues(SimpleChangeListener\u003c? super T\u003e listener);\n    Subscription subscribeToValues(Consumer\u003c? super T\u003e subscriber);\n    EasyBinding\u003cT\u003e orElseOpt(T other);\n    OptionalBinding\u003cT\u003e orElseOpt(ObservableValue\u003cT\u003e other);\n    OptionalBinding\u003cT\u003e filter(Predicate\u003c? super T\u003e predicate);\n    OptionalBinding\u003cU\u003e mapOpt(Function\u003c? super T, ? extends U\u003e mapper);\n    OptionalBinding\u003cU\u003e flatMapOpt(Function\u003cT, Optional\u003cU\u003e\u003e mapper);\n    PropertyBinding\u003cU\u003e selectProperty(Function\u003c? super T, O\u003e mapper);\n    ...\n}\n```\n\nExample:\n\n```java\nBooleanBinding currentTabHasContent = EasyBind.wrapNullable(tabPane.getSelectionModel().selectedItemProperty())\n                                              .map(Tab::contentProperty)\n                                              .isPresent();\n```\n\nThe `EasyBind.valueAt(list, index)` and `EasyBind.valueAt(map, key)` methods return a binding containing the item at the given position in the list or map.\nThe returned binding will be empty if the index/key points behind the list (or at a `null` item).\n\nUse EasyBind in your project\n----------------------------\n\n### Stable release\n\nCurrent stable release is `2.2.0`.\nIt contains many new features, but also breaks backwards compatibility to the `1.x` versions as many methods have been renamed; see the [Changelog](CHANGELOG.md) for details.\nIn case you are upgrading from the `EasyBind` library developed by by Tomas Mikula, then the easiest option is to use version `1.2.2` which includes a few improvements and bug fixes while being compatible with older versions.\n\n#### Maven coordinates\n\n| Group ID            | Artifact ID | Version |\n| :-----------------: | :---------: | :-----: |\n| com.tobiasdiez      | easybind    | 2.2.0   |\n\n#### Gradle example\n\n```groovy\ndependencies {\n    compile group: 'com.tobiasdiez', name: 'easybind', version: '2.2.0'\n}\n```\n\n#### Sbt example\n\n```scala\nlibraryDependencies += \"com.tobiasdiez\" % \"easybind\" % \"2.2.0\"\n```\n\n#### Manual download\n\n[Download](https://github.com/tobiasdiez/EasyBind/releases) the JAR file and place it on your classpath.\n\n### Snapshot releases\n\nSnapshot releases are deployed to Sonatype snapshot repository.\n\n#### Maven coordinates\n\n| Group ID            | Artifact ID | Version        |\n| :-----------------: | :---------: | :------------: |\n| com.tobiasdiez      | easybind    | 2.2.1-SNAPSHOT |\n\n#### Gradle example\n\n```groovy\nrepositories {\n    maven {\n        url 'https://oss.sonatype.org/content/repositories/snapshots/' \n    }\n}\n\ndependencies {\n    compile group: 'com.tobiasdiez', name: 'easybind', version: '2.2.1-SNAPSHOT'\n}\n```\n\n#### Sbt example\n\n```scala\nresolvers += \"Sonatype OSS Snapshots\" at \"https://oss.sonatype.org/content/repositories/snapshots\"\n\nlibraryDependencies += \"com.tobiasdiez\" % \"easybind\" % \"2.2.1-SNAPSHOT\"\n```\n\n#### Manual download\n\n[Download](https://oss.sonatype.org/content/repositories/snapshots/com/tobiasdiez/easybind/) the latest JAR file and place it on your classpath.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftobiasdiez%2FEasyBind","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftobiasdiez%2FEasyBind","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftobiasdiez%2FEasyBind/lists"}