zng/data_context.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
//! Data context service and properties.
//!
//! The [`data`](fn@data) property can be set on a widget to any type that can be used in variables ([`VarValue`]). The
//! [`DATA`] service can then be used on the widget or descendant widgets to retrieve the data and to set validation annotations
//! about the data.
//!
//! The example below demonstrates a simple [MVVM] implementation using the data context to share the view-model instance
//! with all widgets in the view. The example also uses the data annotations API to show data validation errors.
//!
//! [MVVM]: https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel
//!
//! ```
//! # fn main() { }
//! mod view {
//! use crate::view_model::*;
//! use zng::{data_context, prelude::*, window::WindowRoot};
//!
//! pub fn window() -> WindowRoot {
//! Window! {
//! // set data context for entire window, using `var` to be read-write.
//! data = var(ViewModel::new(crate::model::connect()));
//!
//! // bind title from data context.
//! title = DATA.req::<ViewModel>().map(|vm| vm.title());
//! child = content();
//! }
//! }
//!
//! fn content() -> impl UiNode {
//! // `req` panics if context is not set to the same type.
//! let vm = DATA.req::<ViewModel>();
//! Container! {
//! child = TextInput! {
//! txt = vm.map_ref_bidi(|vm| vm.new_item(), |vm| vm.new_item_mut());
//!
//! // FieldStyle shows data errors.
//! style_fn = style_fn!(|_| zng::text_input::FieldStyle!());
//! data_context::data_error = vm.map_ref(|vm| vm.new_item_error());
//! };
//! child_bottom = Button! {
//! child = Text!("Submit");
//! widget::enabled = vm.map(|vm| !vm.new_item().is_empty());
//! on_click = hn!(|_| vm.modify(|vm| vm.to_mut().submit()).unwrap());
//! }, 5;
//! padding = 5;
//! }
//! }
//! }
//!
//! mod view_model {
//! use crate::model::Model;
//! use zng::text::*;
//!
//! #[derive(Clone, Debug, PartialEq)]
//! pub struct ViewModel {
//! model: Model,
//! new_item: Txt,
//! new_item_error: Txt,
//! }
//! impl ViewModel {
//! pub fn new(model: Model) -> Self {
//! Self {
//! model,
//! new_item: Txt::from(""),
//! new_item_error: Txt::from(""),
//! }
//! }
//!
//! pub fn title(&self) -> Txt {
//! formatx!("App - {} entries", self.model.read().len())
//! }
//!
//! pub fn new_item(&self) -> &Txt {
//! &self.new_item
//! }
//! pub fn new_item_mut(&mut self) -> &mut Txt {
//! self.new_item_error = Txt::from("");
//! &mut self.new_item
//! }
//!
//! pub fn new_item_error(&self) -> &Txt {
//! &self.new_item_error
//! }
//!
//! pub fn submit(&mut self) {
//! match self.new_item.parse::<u32>() {
//! Ok(item) => {
//! self.model.write().push(item);
//! self.new_item_mut().clear();
//! }
//! Err(e) => self.new_item_error = e.to_txt(),
//! }
//! }
//! }
//! }
//!
//! mod model {
//! use zng::{task::parking_lot::RwLock, var::ArcEq};
//!
//! pub type Model = ArcEq<RwLock<Vec<u32>>>;
//!
//! pub fn connect() -> ArcEq<RwLock<Vec<u32>>> {
//! ArcEq::new(RwLock::new(vec![]))
//! }
//! }
//! ```
//!
//! Note that vars clone the value when modify is requested, so the view-model should probably use shared
//! references to the model data, overall this cloning has no noticeable impact as it only happens once
//! per user interaction in the worst case.
//!
//! [`data`]: fn@data
//! [`VarValue`]: crate::var::VarValue
//!
//! # Full API
//!
//! See [`zng_wgt_data`] for the full API.
pub use zng_wgt_data::{
data, data_error, data_error_color, data_info, data_info_color, data_note, data_warn, data_warn_color, extend_data_note_colors,
get_data_error, get_data_error_txt, get_data_info, get_data_info_txt, get_data_notes, get_data_notes_top, get_data_warn,
get_data_warn_txt, has_data_error, has_data_info, has_data_notes, has_data_warn, replace_data_note_colors, with_data_note_color,
DataNote, DataNoteHandle, DataNoteLevel, DataNoteValue, DataNotes, DATA,
};