Render after getting props - lit-element

I can't see a proper reason for a LitElement WebComponent to be rendered before having its props/attributes available. If the template needs a prop, you are forced to render a second time the component, after the first useless one. Worse than that, if your component looks like:
class MyComp extends LitElement
static get properties() {
return {
myBigProp: {
type: Object
}
render() {
return html`<p> ${this.myBigProp.nestedProp}</p>`
}
you get an error for accessing nestedProp when myBigProp is undefined. Is there a clean way to avoid rendering twice this component?

If you already are able to set the properties you can simply define them in the constructor. Then it should be accessible at the first render.
constructor() {
super();
this.myBigProp = {nestedProp:'nested value'}
}
However if you have to wait for them I usually make it clear that the component is loading visually by using an "initialized" property then handling it in the render function.
render() {
if (!this.initialized) {
return html`<div class='loading-reserved-zone'></div>`;
} else {
return html`<p>${this.myBigProp.nestedProp}</p>`
}
}
Otherwise, "Optional Chaining Operator" are coming up to javascript in august 2020 which could handle your second scenario or you could technically use this babel plugin if you want to use it earlier.

There is a lifecycle callback shouldUpdate. Return false until you want to render the component.
shouldUpdate(changedProps) {
return this.myBigProp != null;
}

Related

child property from parent Promise

I want to create a navigation component for my project. The shell fetches a json with chapter info, these are passed to nav-element, which recursively calls itself to render the navigation tree.
shell.js
import { LitElement, html, css } from 'lit-element';
import {until} from 'lit-html/directives/until.js';
import './nav-element.js';
export class Shell extends LitElement {
static get properties() {
return {
configjson : { type: Array }
};
}
constructor() {
super();
this.configjson = fetch('./src/convertjson_test.json').then(res => res.json());
}
render() {
return html`
<main>
some content
<nav-element .chapters=${until(this.configjson, [])} root></nav-element>
</main>
`;
}
}
customElements.define('shell', Shell);
nav-element.js
import { LitElement, html, css } from 'lit-element';
import {until} from 'lit-html/directives/until.js';
import {repeat} from 'lit-html/directives/repeat.js';
export class NavElement extends LitElement {
static get properties() {
return {
chapters: {type: Array},
root: {type: Boolean} //to mark the root node
};
}
static get styles() {
return css`
.navheader {
display: none;
}
.navheader[active] {
display: block;
}
`;
}
render() {
return html`
<div class="navHeader" ?active="${this.root}">header</div>
${until(repeat(this.chapters, (chapter) => chapter.pos, (chapter) => html`<div>${chapter.n}<nav-element .chapters=${chapter.c}></nav-element></div>`))}
`;
}
}
customElements.define('nav-element', NavElement);
The problem is, that the configjson Promise is passed as property and not yet resolved by the time the nav-element is called, so i get the error:
Uncaught (in promise) TypeError: this.chapters is undefined
Searched all lit-element and lit-html documentation, the until directive resolved the issue in the shell, but not in the nav-element.
The same coding pattern worked fine in Polymer 2 (&3, although with ajax instead of fetch). Does anyone know how to solve this using lit-element only?
There is a time frame between the construction of NavElement and the assignment of the chapters property where chapters is undefined. It might be safe to initialize chapters in the component itself rather than in Shell's until directive, or at least provide a fallback value in the template:
export class NavElement extends LitElement {
static get properties() {
return {
chapters: {type: Array},
// ...
};
}
constructor() {
super();
this.chapters = [];
}
// or
render() {
return html`
...
${repeat(this.chapters || [], chapter => ...)}
`;
}
}
Also, you've (correctly) declared the chapters property as an Array but you're wrapping the repeat directive in an until (as if it was a Promise I guess?). Here there are two things going on:
the repeat() call returns a DirectiveFn and not a Promise like until expects. If chapters was a Promise, the correct way to combine until and repeat would have been:
until(
this.chaptersPromise.then(chapters => repeat(chapters, chapter => html`...`)),
html`Loading...`,
)
but...
chapters is not a Promise: the until call in the parent component resolves it and passes the result.
As for the Shell component: until used in this way should work, however its intended use is to
Render placeholder content until the final content is available
[from lit-html docs]
To make the most of it, use it to temporarily render a loading template instead of <nav-element>:
render() {
return html`
${until(
this.configJson.then(chapters => html`<nav-element .chapters=${chapters}></nav-element>`),
html`Loading chapters`,
)}
`;
}
Also, not a big deal, but here the configJson property is declared as an Array but is actually a Promise.

Get result of function in Laravel and add to another controller

I have this 1st controller.
class ValidatePassController extends Controller
{
protected function doShow(Post $post, Hash $hash)
{
return view('auth.cab.pcab');
}
}
I need to add if view was returned or something like that in the controller below.
class EditController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function xuibomja()
{
if (// in past controller view was returned))
{
return ('stassik');
}
return ('not stassik');
}
}
Any ideas? I have tried to set some vars, but it didn't work, so im out of ideas. Btw can't summary controllers to each other, codes needs to be in different controllers.
It's not the best practice to have a controller to call another controller.
what I recommend you to do is to create a class and move the functionality there, and create a facade to map to this class, and set both controllers to use that common functionality

How do you access a lit-element render root after overriding createRenderRoot?

I've been playing around with lit-element, and I want to grab my custom element to run a getElementById. The only examples I can find use the shadow root (since that's the recommended way to use lit-element). How do you get access to your custom element to run a query on just your element?
import { LitElement, html }
from 'https://unpkg.com/lit-element/lit-element.js?module';
class RenderRootTest extends LitElement {
constructor() {
super();
}
render () {
const renderRoot = this.shadowRoot; //Won't work, because I'm overriding the shadowroot
return html`
<div>Rendered</div>
${renderRoot ?
html`<div>Render root found</div>` :
html``
}
`;
}
createRenderRoot() {
return this;
}
}
customElements.define('render-root-test', RenderRootTest);
I found the answer myself after enough tinkering. You can either use this.renderRoot or just this. However, note that certain methods such as .getElementById don't seem to exist. If anyone has any additional details on this topic, I would appreciate it.
ex.
import { LitElement, html }
from 'https://unpkg.com/lit-element/lit-element.js?module';
class RenderRootTest extends LitElement {
constructor() {
super();
}
render () {
const renderRoot = this.renderRoot;
return html`
<div>Rendered</div>
${renderRoot ?
html`<div>Render root found</div>` :
html``
}
`;
}
createRenderRoot() {
return this;
}
}
customElements.define('render-root-test', RenderRootTest);
Just reference this which is the instance of the custom element. The shadowRoot is created and returned by LitElement's createRenderRoot() so if you don't create one and instead of this.shadowRoot return this--which is the node itself--that is what the content is rendered into, there is no shadowRoot.

AdonisJs: Using Hashids

Had already someone used Hashids in AdonisJs?
Being more specific, in a Model, to return a property hashid in an object
I'm working on a migration from Laravel to Adonis.
In Laravel, it's possible just with a couple of code lines in each Model, like this:
use Hashids;
class Menu extends Model
{
use \OwenIt\Auditing\Auditable;
protected $appends = ['hashid'];
public function getHashidAttribute()
{
return Hashids::encode($this->attributes['id']);
}
}
I installed this NPM package: https://www.npmjs.com/package/adonis-hashids, and I tried to figure out how to use like the Laravel way
I'd used Computed Properties (https://adonisjs.com/docs/4.1/database-getters-setters#_computed_properties)
class Menu extends Model {
static get computed () {
return ['hashids']
}
getHashids({ id }) {
return Hashids.encode(id)
}
}

can't use withFrame in page class content

I am using geb-spock. I am trying to validate content of the page class, in the page itsself so that I just call the variable or functions. Using functions. I did something like this
class BasePage extends Page {
static content = {
verifyheader { withFrame ("myFrame") { assert $("h1").text() == "Header1" }
}
}
}
...
then:
to BasePage
and:
verifyheader
I am getting an error that the test failed and withFrame is null.
This does not occuer when I put the withFrame in the test case
then:
to BasePage
and:
withFrame('myFrame') {...}
This worked perfectly, but I am looking to use it in the page class. Is it possible? How can I go about it? or in other words, what is wrong with my code
Yes, the withFrame call in your content definition returns null because the last statement inside of the block passed to it is an assertion which always returns null. You should not assert inside of your content definition but in your test instead:
class BasePage extends Page {
static content = {
headerText { withFrame("myFrame") { $("h1").text() } }
}
}
and:
when:
to BasePage
then:
headerText == "Header1"

Resources