Every entry on this page has the same shape: the build is green, the audit is clean, the console is quiet, and the screen is empty or not what you meant. The compiler cannot see these mistakes because each one is a valid program that happens to describe a different component. Find the heading that sounds like what you see; under it is the cause and the fix.
The name in view.tree and the name of the method in the class differ, most often by case: exchange_form in the tree, exchangeForm in the class. Those are two properties. The class one is never called, the tree one keeps its default, and nothing reports it.Names are snake_case everywhere and must match letter for letter. After editing a .view.ts, check that every override still names an existing property; the generated -view.tree/*.view.tree.d.ts next to the tree is the list to compare against.
These are properties of $mol_string: hint for the placeholder, enabled for the disabled state, type for the input type. Set them where the input is used:1<= Password $mol_string2 hint \Password3 type \password4 enabled <= can_edit true5 value?<=> password?\Any other property of any other component is found the same way: open its .view.tree, as How to read the sources shows.attr* is for real DOM attributes the component does not already model, and it has a trap of its own: a block without ^ as its first line replaces the whole attribute dictionary of the base, so a $mol_button written that way loses disabled, role and tabindex. Start the block with ^ to inherit, then add your keys:1attr *2^3 data_kind \primary
A string was passed where a list is expected, and the string was spread character by character. Strings start with \, lists start with /. The usual case is sub, which is a list:1sub /<= label \Hello
An override fell out of the .view.ts. The tree gives every property a default, so when the class stops redefining rows(), TypeScript is satisfied and the list renders empty. Tests that only check the model stay green because the model is fine.For every override the screen depends on, write a test that reads what the user sees: rows(), sub(), title() of the sub-views, or the DOM. Testing shows both.
A @ $mol_mem method returned a promise as its value: fetch(uri).then(...), an async method, or the result of $mol_wire_async(this).load(). A promise in the cell reads as "still computing" to everyone, and when it resolves the cell recomputes and produces a fresh promise. The network works; the view never sees a result.The right shape is synchronous: call this.$.$mol_fetch.json(uri) inside the cell and return the parsed value. The fiber suspends until the response is in and then reruns the cell, so the promise never appears in your code. Where asynchronous work has to happen elsewhere, put its result in a separate state cell and have the effect return a flag or a key, never the promise.A second cause is a swallowed error: a try/catch inside a cell that catches the suspension along with real failures. Rethrow anything that is a Promise, or use $mol_fail_catch, which does that check for you.