Liam Hurst

Liam Hurst

1558537150

Web Components Tutorial: Go from Zero to Hero

Web Components is a suite of different technologies allowing you to create reusable custom elements — with their functionality encapsulated away from the rest of your code — and utilize them in your web apps.

An introduction to writing raw web components

  • What are web components?
  • A Components lifecycle
  • Building our to-do app
  • Setting properties
  • Setting attributes
  • Reflecting properties to attributes
  • Events
  • Browser support and polyfills
  • Wrapping up

Web components are getting more and more traction. With the Edge team’s recent announcement of implementing Custom Elements and Shadow DOM, all major browsers will soon support web components natively. Companies like Github, Netflix, Youtube and ING are even already using web components in production. Neat! However, surprisingly enough, none of those huge, succesful companies have implemented a (you guessed it) to-do app!

So today, we’ll be making a to-do app, because the world doesn’t have enough implementations of to-do apps yet. You can take a look at what we’ll be making here.

Before we start, I’d like to add a little disclaimer that this blogpost is intended to get a better grasp of the basics of web components. Web components are low level, and should probably not be used to write full blown applications without the use of any helper libraries, nor should they be compared to full blown frameworks.

What are web components?

  • Make a demo
  • The boring stuff
  • Setting properties
  • Setting attributes
  • Reflecting properties to attributes
  • Events
  • Wrap it up

First things first: Web components are a set of standards that allow us to write modular, reusable and encapsulated HTML elements. And the best thing about them: since they’re based on web standards, we don’t have to install any framework or library to start using them. You can start writing web components using vanilla javascript, right now!

But before we start getting our hands dirty, lets take a look at the specifications that let us write web components.

 

Custom Elements

The Custom Elements api allows us to author our own DOM elements. Using the api, we can define a custom element, and inform the parser how to properly construct that element and how elements of that class should react to changes. Have you ever wanted your own HTML element, like <my-cool-element>? Now you can!

 

Shadow DOM

Shadow DOM gives us a way to encapsulate the styling and markup of our components. It’s a sub DOM tree attached to a DOM element, to make sure none of our styling leaks out, or gets overwritten by any external styles. This makes it great for modularity.

 

ES Modules

The ES Modules specification defines the inclusion and reuse of JS documents in a standards based, modular, performant way.

 

HTML Templates

The HTML <a href="https://html.spec.whatwg.org/multipage/scripting.html#the-template-element/" target="_blank"><template></a> tag allows us to write reusable chunks of DOM. Inside a template, scripts don’t run, images don’t load, and styling/mark up is not rendered. A template tag itself is not even considered to be in the document, until it’s activated. HTML templates are great, because for every instance of our element, only 1 template is used.

Now that we know which specifications web components leverage, let’s take a look at a custom element’s lifecycle. I know, I know, we’ll get to the code soon!

A component’s lifecycle

Let’s take a look at a custom element’s lifecycle. Consider the following element:

class MyElement extends HTMLElement {
    constructor() {
        // always call super() first
        super(); 
        console.log('constructed!');
    }
	
    connectedCallback() {
        console.log('connected!');
    }
	
    disconnectedCallback() {
        console.log('disconnected!');
    }
	
    attributeChangedCallback(name, oldVal, newVal) {
        console.log(`Attribute: ${name} changed!`);
    }
	
    adoptedCallback() {
        console.log('adopted!');
    }
}

window.customElements.define('my-element', MyElement);

 

constructor()

The constructor runs whenever an element is created, but before the element is attached to the document. We’ll use the constructor for setting some initial state, event listeners, and creating the shadow DOM.

connectedCallback()

The connectedCallback is called when the element is inserted to the DOM. It’s a good place to run setup code, like fetching data, or setting default attributes.

disconnectedCallback()

The disconnectedCallback is called whenever the element is removed from the DOM. Clean up time! We can use the disconnectedCallback to remove any event listeners, or cancel intervals.

attributeChangedCallback(name, oldValue, newValue)

The attributeChangedCallback is called any time your element’s observed attributes change. We can observe an element’s attributes by implementing a static observedAttributes getter, like so:

static get observedAttributes() {
    return ['my-attr'];
}

In this case, any time the my-attr attribute is changed, the attributeChangedCallback will run. We’ll go more in-depth on this later this blog post.

Hey! Listen!> Only attributes listed in the observedAttributes getter are affected in the attributeChangedCallback.### adoptedCallback()

The adoptedCallback is called each time the custom element is moved to a new document. You’ll only run into this use case when you have <iframe> elements in your page.

registering our element

And finally, though not part of the lifecycle, we register our element to the CustomElementRegistry like so:

window.customElements.define('my-element', MyElement);

The CustomElementRegistry is an interface that provides methods for registering custom elements and querying registered elements. The first argument of the registries’ define method will be the name of the element, so in this case it’ll register <my-element>, and the second argument passes the class we made.

Hey! Listen!> It’s important to note how we name our web components. Custom element names must always contain a hyphen. For example: <my-element> is correct, and <myelement> is not. This is done deliberately to avoid clashing element names, and to create a distinction between custom elements and regular elements.> Custom elements also cannot be self-closing because HTML only allows a few elements to be self-closing. These are called void elements, like <br/> or <img/>, or elements that don’t allow children nodes.> Allowing self-closing elements would require a change in the HTML parser, which is a problem since HTML parsing is security sensitive. HTML producers need to be able to rely on how a given piece of HTML parses in order to be able to implement XSS-safe HTML generation.##

Building our to do app

  • Make a demo
  • The boring stuff
  • Setting properties
  • Setting attributes
  • Reflecting properties to attributes
  • Events
  • Wrap it up

Now that we’re done with all the boring stuff, we can finally get our hands dirty and start building our to do app! Click here to see the end result.

Let’s start with an overview of what we’re going to build.

  • A <to-do-app> element:
    Contains an array of to-do’s as propertyAdds a to-doRemoves a to-doToggles a to-do* A <to-do-item> element:
    Contains a description attributeContains an index attributeContains a checked attribute
    Great! Let’s lay out the groundwork for our to-do-app:

to-do-app.js:

const template = document.createElement('template');
template.innerHTML = `
<style>
    :host {
	display: block;
	font-family: sans-serif;
	text-align: center;
    }

    button {
	border: none;
	cursor: pointer;
    }

    ul {
	list-style: none;
	padding: 0;
    }
</style>
<h1>To do</h1>

<input type="text" placeholder="Add a new to do"></input>
<button>✅</button>

<ul id="todos"></ul>
`;

class TodoApp extends HTMLElement {
    constructor() {
        super();
        this._shadowRoot = this.attachShadow({ 'mode': 'open' });
        this._shadowRoot.appendChild(template.content.cloneNode(true));
        this.$todoList = this._shadowRoot.querySelector('ul');
    }
}

window.customElements.define('to-do-app', TodoApp);

We’re going to take this step by step. We first create a <template> by calling const template = document.createElement('template');, and then we set some HTML in it. We only set the innerHTML on the template once. The reason we’re using a template is because cloning templates is much cheaper than calling .innerHTML for all instances of our component.

Next up, we can actually start defining our element. We’ll use our constructor to attach our shadowroot, and we’ll set it to open mode. Then we’ll clone our template to our shadowroot. Cool! We’ve now already made use of 2 web components specifications, and succesfully made an encapsulated sub DOM tree.

What this means is that we now have a DOM tree that will not leak any styles, or get any styles overwritten. Consider the following example:

We have a global h1 styling that makes any h1 in the light DOM a red color. But because we have our h1 in a shadow-root, it does not get overwritten by the global style.

Note how in our to-do-app component, we’ve used a :host pseudo class, this is how we can add styling to the component from the inside. An important thing to note is that the display is always set to display: inline;, which means you can’t set a width or height on your element. So make sure to set a :host display style (e.g. block, inline-block, flex) unless you prefer the default of inline.

Hey! Listen!> Shadow DOM can be a little confusing. Allow me to expand a little bit on terminology:###

Light DOM:

The light DOM lives outside the component’s shadow DOM, and is basically anything that is not shadow DOM. For example, the <h1>Hello world</h1> up there lives in the light DOM. The term light DOM is used to distinguish it from the Shadow DOM. It’s perfectly fine to make web components using light DOM, but you miss out on the great features of shadow DOM.

Open shadow DOM:

Since the latest version (V1) of the shadow DOM specification, we can now use open or closed shadow DOM. Open shadow DOM allows us to create a sub DOM tree next to the light DOM to provide encapsulation for our components. Our shadow DOM can still be pierced by javascript like so: document.querySelector('our-element').shadowRoot. One of the downsides of shadow DOM is that web components are still relatively young, and many external libraries don’t account for it.

Closed shadow DOM:

Closed shadow roots are not very applicable, as it prevents any external javascript from piercing the shadowroot. Closed shadow DOM makes your component less flexible for yourself and your end users and should generally be avoided.

Some examples of elements that do use a closed shadow DOM are the <video> element.

Setting properties

Cool. We’ve made our first web component, but as of now, it’s absolutely useless. It would be nice to be able to pass some data to it and render a list of to-do’s.

Let’s implement some getters and setters.

to-do-app.js:

class TodoApp extends HTMLElement {
    ...
 		
    _renderTodoList() {
        this.$todoList.innerHTML = '';

        this._todos.forEach((todo, index) => {
            let $todoItem = document.createElement('div');
            $todoItem.innerHTML = todo.text; 
            this.$todoList.appendChild($todoItem);
        });
    }

    set todos(value) {
        this._todos = value;
        this._renderTodoList();
    }

    get todos() {
        return this._todos;
    }
}

Now that we have some getters and setters, we can pass some rich data to our element! We can query for our component and set the data like so:

document.querySelector('to-do-app').todos = [
    {text: "Make a to-do list", checked: false}, 
    {text: "Finish blog post", checked: false}
];

We’ve now succesfully set some properties on our component, and it should currently look like this:

Great! Except it’s still useless because we cannot interact with anything without using the console. Let’s quickly implement some functionality to add new to-do’s to our list.

class TodoApp extends HTMLElement {
    ...

    constructor() {
        super();
        this._shadowRoot = this.attachShadow({ 'mode': 'open' });
        this._shadowRoot.appendChild(template.content.cloneNode(true));

        this.$todoList = this._shadowRoot.querySelector('ul');
        this.$input = this._shadowRoot.querySelector('input');

        this.$submitButton = this._shadowRoot.querySelector('button');
        this.$submitButton.addEventListener('click', this._addTodo.bind(this));
    }

    _addTodo() {
        if(this.$input.value.length > 0){
            this._todos.push({ text: this.$input.value, checked: false })
            this._renderTodoList();
            this.$input.value = '';
        }
    }

    ...
}

This should be easy enough to follow, we set up some querySelectors and addEventListeners in our constructor, and on a click event we want to push the input to our to-do’s list, render it, and clear the input again. Ez .

Setting attributes

  • Make a demo
  • The boring stuff
  • Setting properties
  • Setting attributes
  • Reflecting properties to attributes
  • Events
  • Wrap it up

This is where things will get confusing, as we’ll be exploring the differences between attributes and properties, and we’ll also be reflecting properties to attributes. Hold on tight!

First, let’s create a <to-do-item> element.

to-do-item.js:

const template = document.createElement('template');
template.innerHTML = `
<style>
    :host {
	display: block;
	font-family: sans-serif;
    }

    .completed {
	text-decoration: line-through;
    }

    button {
	border: none;
	cursor: pointer;
    }
</style>
<li class="item">
    <input type="checkbox">
    <label></label>
    <button>❌</button>
</li>
`;

class TodoItem extends HTMLElement {
    constructor() {
        super();
        this._shadowRoot = this.attachShadow({ 'mode': 'open' });
        this._shadowRoot.appendChild(template.content.cloneNode(true));

        this.$item = this._shadowRoot.querySelector('.item');
        this.$removeButton = this._shadowRoot.querySelector('button');
        this.$text = this._shadowRoot.querySelector('label');
        this.$checkbox = this._shadowRoot.querySelector('input');

        this.$removeButton.addEventListener('click', (e) => {
            this.dispatchEvent(new CustomEvent('onRemove', { detail: this.index }));
        });

        this.$checkbox.addEventListener('click', (e) => {
            this.dispatchEvent(new CustomEvent('onToggle', { detail: this.index }));
        });
    }

    connectedCallback() {
    	// We set a default attribute here; if our end user hasn't provided one,
    	// our element will display a "placeholder" text instead.
        if(!this.hasAttribute('text')) {
            this.setAttribute('text', 'placeholder');
        }

        this._renderTodoItem();
    }

    _renderTodoItem() {
        if (this.hasAttribute('checked')) {
            this.$item.classList.add('completed');
            this.$checkbox.setAttribute('checked', '');
        } else {
            this.$item.classList.remove('completed');
            this.$checkbox.removeAttribute('checked');
        }
        
        this.$text.innerHTML = this._text;
    }

    static get observedAttributes() {
        return ['text'];
    }

    attributeChangedCallback(name, oldValue, newValue) {
        this._text = newValue;
    }
}
window.customElements.define('to-do-item', TodoItem);

Note that since we’re using a ES Modules, we’re able to use const template = document.createElement('template'); again, without overriding the previous template we made.
And lets change our _renderTodolist function in to-do-app.js to this:

class TodoApp extends HTMLElement {

    	...

        _renderTodoList() {
            this.$todoList.innerHTML = '';

            this._todos.forEach((todo, index) => {
                let $todoItem = document.createElement('to-do-item');
                $todoItem.setAttribute('text', todo.text);
                this.$todoList.appendChild($todoItem);
            });
        }
        
        ...
		 
    }

Alright, a lot of different stuff is going on here. Let’s dive in. Previously, when passing some rich data (an array) to our <to-do-app> component, we set it like this:

document.querySelector('to-do-app').todos = [{ ... }];

We did that, because todos is a property of the element. Attributes are handled differently, and don’t allow rich data, in fact, they only allow a String type as a limitation of HTML. Properties are more flexible and can handle complex data types like Objects or Arrays.

The difference is that attributes are defined on HTML elements. When the browser parses the HTML, a corresponding DOM node will be created. This node is an object, and therefore it has properties. For example, when the browser parses: <to-do-item index="1">, a HTMLElement object will be created. This object already contains several properties, such as children, clientHeight, classList, etc, as well as some methods like appendChild() or click(). We can also implement our own properties, like we did in our to-do-app element, which we gave a todos property.

Here’s an example of this in action.

<img src="myimg.png" alt="my image"/>

The browser will parse this <img> element, create a DOM Element object, and conveniently set the properties for src and alt for us. It should be mentioned that this property reflection is not true for all attributes. (Eg: the value attribute on an <input> element does not reflect. The value property of the <input> will always be the current text content of the <input>, and the value attribute will be the initial text content.) We’ll go deeper into reflecting properties to attributes shortly.

So we now know that the alt and src attributes are handled as String types, and that if we’d want to pass our array of to-do’s to our <to-do-app> element like this:

<to-do-app todos="[{...}, {...}]"></to-do-app>

We would not get the desired result; we’re expecting an array, but actually, the value is simply a String that looks like an array.

Hey! Listen!* Aim to only accept rich data (objects, arrays) as properties.

  • Do not reflect rich data properties to attributes.

Setting attributes works differently from properties as well, notice how we didn’t implement any getters or setters. We added our text attribute to the static get observedAttributes getter, to allow us to watch for changes on the text attribute. And we implemented the attributesChangedCallback to react to those changes.

Our app should look like this, at this moment in time:

 

Boolean attributes

We’re not done with attributes just yet. It would be nice to be able to check off some of our to-do’s when we’re done with them, and we’ll be using attributes for that as well. We have to handle our Boolean attributes a little differently though.

The presence of a boolean attribute on an element represents the True value, and the absence of the attribute represents the False value.> If the attribute is present, its value must either be the empty string or a value that is an ASCII case-insensitive match for the attribute’s canonical name, with no leading or trailing whitespace.> The values “true” and “false” are not allowed on boolean attributes. To represent a false value, the attribute has to be omitted altogether. <div hidden="true"> is incorrect.
This means that only the following examples are acceptable for a true value:

<div hidden></div>
<div hidden=""></div>
<div hidden="hidden"></div>

And one for false:

<div></div>

So let’s implement the checked attribute for our <to-do-item> element!

Change your to-do-app.js to this:

_renderTodoList() {
    this.$todoList.innerHTML = '';

    this._todos.forEach((todo, index) => {
        let $todoItem = document.createElement('to-do-item');
        $todoItem.setAttribute('text', todo.text);

	// if our to-do is checked, set the attribute, else; omit it.
        if(todo.checked) {
            $todoItem.setAttribute('checked', '');                
        }

        this.$todoList.appendChild($todoItem);
    });
}

And change to-do-item to this:

 class TodoItem extends HTMLElement {

    ...

    static get observedAttributes() {
        return ['text', 'checked'];
    }

    attributeChangedCallback(name, oldValue, newValue) {
        switch(name){
            case 'text':
                this._text = newValue;
                break;
            case 'checked':
                this._checked = this.hasAttribute('checked');
                break;
        }
    }

    ...

}

Nice! Our application should look like this:

 

Reflecting properties to attributes

  • Make a demo
  • The boring stuff
  • Setting properties
  • Setting attributes
  • Reflecting properties to attributes
  • Events
  • Wrap it up

Cool, our app is coming along nicely. But it would be nice if our end user would be able to query for the status of checked of our to-do-item component. We’ve currently set it only as an attribute, but we would like to have it available as a property as well. This is called reflecting properties to attributes.

All we have to do for this is add some getters and setters. Add the following to your to-do-item.js:

get checked() {
    return this.hasAttribute('checked');
}

set checked(val) {
    if (val) {
        this.setAttribute('checked', '');
    } else {
        this.removeAttribute('checked');
    }
}

Now, every time we change the property or the attribute, the value will always be in sync.

Events

  • Make a demo
  • The boring stuff
  • Setting properties
  • Setting attributes
  • Reflecting properties to attributes
  • Events
  • Wrap it up

Phew, now that we’re done with the hard bits, it’s time to get to the fun stuff. Our application currently handles and exposes the data in a way we want to, but it doesn’t actually remove or toggle the to-do’s yet. Let’s take care of that.

First, we’re going to have to keep track of the index of our to-do-items. Let’s set up an attribute!

to-do-item.js:

static get observedAttributes() {
    return ['text', 'checked', 'index'];
}

attributeChangedCallback(name, oldValue, newValue) {
    switch(name){
        case 'text':
            this._text = newValue;
            break;
        case 'checked':
            this._checked = this.hasAttribute('checked');
            break;
        case 'index':
            this._index = parseInt(newValue);
            break;
    }
}

Note how we’re parsing the String type value to an integer here, since attributes only allow a String type, but we’d like the end user to be able to get the index property as an integer. And we also now have a nice example of how to deal with string/number/boolean attributes and how to handle attributes and properties as their actual type.

So let’s add some getters and setters to to-do-item.js:

set index(val) {
    this.setAttribute('index', val);
}

get index() {
    return this._index;
}

And change our _renderTodoList function in to-do-app.js to:

_renderTodoList() {
    this.$todoList.innerHTML = '';

    this._todos.forEach((todo, index) => {
        let $todoItem = document.createElement('to-do-item');
        $todoItem.setAttribute('text', todo.text);

        if(todo.checked) {
            $todoItem.setAttribute('checked', '');                
	}

        $todoItem.setAttribute('index', index);
        
        $todoItem.addEventListener('onRemove', this._removeTodo.bind(this));

        this.$todoList.appendChild($todoItem);
    });
}

Note how we’re setting $todoItem.setAttribute('index', index);. We now have some state to keep track of the index of the to-do. We’ve also set up an event listener to listen for an onRemove event on the to-do-item element.

Next, we’ll have to fire the event when we click the remove button. Change the constructor of to-do-item.js to the following:

constructor() {
    super();
    this._shadowRoot = this.attachShadow({ 'mode': 'open' });
    this._shadowRoot.appendChild(template.content.cloneNode(true));

    this.$item = this._shadowRoot.querySelector('.item');
    this.$removeButton = this._shadowRoot.querySelector('button');
    this.$text = this._shadowRoot.querySelector('label');
    this.$checkbox = this._shadowRoot.querySelector('input');

    this.$removeButton.addEventListener('click', (e) => {
        this.dispatchEvent(new CustomEvent('onRemove', { detail: this.index }));
    });
}

Hey! Listen!> We can set { detail: this.index, composed: true, bubbles: true } to let the event bubble out of our components shadow DOM.
And add the _removeTodo function in to-do-app.js:

_removeTodo(e) {
    this._todos.splice(e.detail, 1);
    this._renderTodoList();
}

Sweet! We’re able to delete to-do’s:

And finally, let’s create a toggle functionality as well.

to-do-app.js:

class TodoApp extends HTMLElement {
    ...
   
    _toggleTodo(e) {
        const todo = this._todos[e.detail];
        this._todos[e.detail] = Object.assign({}, todo, {
            checked: !todo.checked
        });
        this._renderTodoList();
    }


    _renderTodoList() {
        this.$todoList.innerHTML = '';

        this._todos.forEach((todo, index) => {
            let $todoItem = document.createElement('to-do-item');
            $todoItem.setAttribute('text', todo.text);

            if(todo.checked) {
                $todoItem.setAttribute('checked', '');                
            }

            $todoItem.setAttribute('index', index);
            $todoItem.addEventListener('onRemove', this._removeTodo.bind(this));
            $todoItem.addEventListener('onToggle', this._toggleTodo.bind(this));

            this.$todoList.appendChild($todoItem);
        });
    }
	
    ...

}

And to-do-item.js:

class TodoItem extends HTMLElement {

    ...
	
    constructor() {
        super();
        this._shadowRoot = this.attachShadow({ 'mode': 'open' });
        this._shadowRoot.appendChild(template.content.cloneNode(true));

        this.$item = this._shadowRoot.querySelector('.item');
        this.$removeButton = this._shadowRoot.querySelector('button');
        this.$text = this._shadowRoot.querySelector('label');
        this.$checkbox = this._shadowRoot.querySelector('input');

        this.$removeButton.addEventListener('click', (e) => {
            this.dispatchEvent(new CustomEvent('onRemove', { detail: this.index }));
        });

        this.$checkbox.addEventListener('click', (e) => {
            this.dispatchEvent(new CustomEvent('onToggle', { detail: this.index }));
        });
    }
    
    ...

}

Success! We can create, delete, and toggle to-do’s!

Browser support and polyfills

  • Make a demo
  • The boring stuff
  • Setting properties
  • Setting attributes
  • Reflecting properties to attributes
  • Events
  • Wrap it up

The last thing I’d like to address in this blog post is browser support. At time of writing, the Microsoft Edge team has recently announced that they’ll be implementing custom elements as well as shadow DOM, meaning that all major browsers will soon natively support web components.

Until that time, you can make use of the webcomponentsjs polyfills, maintained by Google. Simply import the polyfill:

<script src="https://unpkg.com/@webcomponents/webcomponentsjs@2.0.0/webcomponents-bundle.js"></script>

I used unpkg for simplicity’s sake, but you can also install webcomponentsjs with NPM. To make sure the polyfills have succesfully loaded, we can wait for the WebComponentsReady event to be fired, like so:

window.addEventListener('WebComponentsReady', function() {
    console.log('Web components ready!');
    // your web components here
});

 

Wrapping up

  • Make a demo
  • The boring stuff
  • Setting properties
  • Setting attributes
  • Reflecting properties to attributes
  • Events
  • Wrap it up

Done!
If you’ve made it all the way down here, congratulations! You’ve learned about the web components specifications, (light/open/closed) shadow DOM, templates, the difference between attributes and properties, and reflecting properties to attributes.

But as you can probably tell, a lot of the code that we’ve written may feel a little clunky, we’ve written quite a lot of boiler plate (getters, setters, queryselectors, etc), and a lot of things have been handled imperatively. Our updates to the to do list aren’t very performant, either.

Web components are neat, but I don’t want to spend all this time writing boiler plate and setting stuff imperatively, I want to write declarative code!”, you cry.

Supercharging Web Components with lit-html

Supercharging web components

  • Lit-html
  • Lit-html in practice
  • Supercharging our component
  • Attributes, properties, and events
  • Wrapping up

You’ll know the basics of web components by now. If you haven’t, I suggest you go back to part one and catch up, because we’ll be revisiting, and building on top of a lot of the concepts

We’ll be supercharging our to-do application with a rendering library called lit-html. But before we dive in, there’s a couple of things we need to discuss. If you’ve paid close attention, you’ll have noticed that I referred to our web component as being a raw web component before. I did that, because web components are low level, and don’t include templating or other features by design. Web components were always intended as a collection of standards that do very specific things that the platform didn’t allow yet.

I’d like to quote Justin Fagnani by saying that all web components do is give the developer a when and a where. The when being element creation, instantiation, connecting, disconnecting, etc. The where being the element instance and the shadowroot. What you do with that is up to you.

Additionally, lit-html is not a framework. It’s simply a javascript library that leverages standard javascript language features. The difference between libraries and frameworks is often a controversial subject, but I’d like to define it as this analogy by Dave Cheney:

Frameworks are based on the Hollywood Pattern; don’t call us, we’ll call you.
Lit-html is also extremely lightweight at <2kb, and renders fast.

Now that we’ve got that out of the way, let’s see how lit-html works.

Lit-html

  • Learn about Lit-html
  • Lit-html in practice
  • Supercharge our web component
  • Attributes, properties, and events
  • Wrapping up

Lit-html is a rendering library that lets you write HTML templates with javascript template literals, and efficiently render and re-render those templates to DOM. Tagged template literals are a feature of ES6 that can span multiple lines, and contain javascript expressions. A tagged template literal could look something like this:

const planet = "world";

html`hello ${planet}!`;

Tagged template literals are just standard ES6 syntax. And these tags are actually just functions! Consider the following example:

function customFunction(strings) {
    console.log(strings); // ["Hello universe!"]
}

customFunction`Hello universe!`;

They can also handle expressions:

const planet = "world";

function customFunction(strings, ...values) {
    console.log(strings); // ["Hello ", "! five times two equals "]
    console.log(values); // ["world", 10]
}

customFunction`Hello ${planet}! five times two equals ${ 5 * 2 }`;

And if we look in the source code we can see this is exactly how lit-html works as well:

/**
 * Interprets a template literal as an HTML template that can efficiently
 * render to and update a container.
 */
export const html = (strings: TemplateStringsArray, ...values: any[]) =>
    new TemplateResult(strings, values, 'html', defaultTemplateProcessor);

Now if we’d write something like this:

const planet = "world";

function customFunction(strings, ...values) {
    console.log(strings); // ["<h1>some static content</h1><p>hello ", "</p><span>more static content</span>"]
    console.log(values); // ["world"]
}

customFunction`
	<h1>some static content</h1>
	<p>hello ${planet}</p>
	<span>more static content</span>	
`;

You’ll notice that when we log our strings and values to the console, we’ve already separated the static content of our template, and the dynamic parts. This is great when we want to keep track of changes, and update our template with the corresponding data, because it allows us to only watch the dynamic parts for changes. This is also a big difference with how VDOM works because we already know the <h1> and the <span> are static, so we don’t have to do anything with them. We’re only interested in the dynamic parts, which can be any javascript expression.

So lit-html takes your template, replaces all the expressions with generic placeholders called Parts, and makes a <template> element out of the result. So we now have a HTML template, that knows where it has to put any data it will receive.

<template>
	<h1>some static content</h1>
	<p>hello {{}}</p> <-- here's our placeholder, or `Part`
	<span>more static content</span>	
</template>

Lit remembers where these placeholders are, which allows for easy and efficient updates. Lit will also efficiently reuse <template>s:

const sayHello = (name) => html`
	<h1>Hello ${name}</h1>
`;

sayHello('world');
sayHello('universe');

Both these templates will share the exact same <template> for efficiency, the only thing that’s different is the data we’re passing it. And if you paid close attention, you’ll remember that we used the same technique in part one of this blog series.

The dynamic Parts of our template can be any javascript expression. Lit-html doesn’t even have to do any magic to evaluate our expressions, javascript just does this for us. Here are some examples:

Simple:

customFunction`<p>${1 + 1}</p>`; // 2

Conditionals:

customFunction`<p>${truthy ? 'yes' : 'no'}</p>`; // 'yes'

And we can even work with arrays and nesting:

customFunction`<ul>${arr.map(item => customFunction`<li>${item}</li>`)}</ul>`;

Lit-html in practice

  • Learn about Lit-html
  • Lit-html in practice
  • Supercharge our web component
  • Attributes, properties, and events
  • Wrapping up

So let’s see how this works in practice:

You can see the full demo here or on github.

import { html, render } from 'lit-html';

class DemoElement extends HTMLElement {
  constructor() {
    super();
    this._counter = 0;
    this._title = "Hello universe!";
    this.root = this.attachShadow({ mode: "open"});
    setInterval(() => {this.counter++}, 1000);
  }

  get counter() {
    return this._counter;
  }

  set counter(val) {
    this._counter = val;
    render(this.template(), this.root);
  }

  template() {
    return html`
      <p>Some static DOM</p>
      <h1>${this.counter}</h1>
      <h2>${this._title}</h2>
      <p>more static content</p>
    `;
  }
}

window.customElements.define('demo-element', DemoElement);

If you’ve read the first blog post in this series, this should look familiar. We’ve made a simple web component, that increments a counter every second, and we’ve implemented lit-html to take care of our rendering for us.

The interesting bits are here:

    return html`
      <p>Some static DOM</p>
      <h1>${this.counter}</h1>
      <h2>${this._title}</h2>
      <p>more static content</p>
    `;

And the ouput in the DOM:

We can now see how lit only updates the part of our code that has changed (this.counter), and doesn’t even bother with the static parts. And it does all this without any framework magic or VDOM, and at less than 2kb library size! You also might notice a bunch of HTML comments in the output; Fear not, this is how lit-html keeps track of where static and dynamic parts are.

Supercharging our component

  • Learn about Lit-html
  • Lit-html in practice
  • Supercharge our web component
  • Attributes, properties, and events
  • Wrapping up

Now that we know how lit-html renders, lets put it in practice. You can see the full code here and on github. We’ll be walking through this step by step, but lets get an overview of our supercharged component first:

to-do-app.js:

import { html, render } from 'lit-html';
import './to-do-item.js';

class TodoApp extends HTMLElement {
    constructor() {
        super();
        this._shadowRoot = this.attachShadow({ 'mode': 'open' });

        this.todos = [
	    { text: 'Learn about Lit-html', checked: true },
	    { text: 'Lit-html in practice', checked: false },
	    { text: 'Supercharge our web component', checked: false },
	    { text: 'Attributes, properties, and events', checked: false },
	    { text: 'Wrapping up', checked: false }
	];

        render(this.template(), this._shadowRoot, {eventContext: this});

        this.$input = this._shadowRoot.querySelector('input');
    }

    _removeTodo(e) {
      this.todos = this.todos.filter((todo,index) => {
          return index !== e.detail;
      });
    }

    _toggleTodo(e) {
      this.todos = this.todos.map((todo, index) => {
          return index === e.detail ? {...todo, checked: !todo.checked} : todo;
      });
    }

    _addTodo(e) {
      e.preventDefault();
      if(this.$input.value.length > 0) {
          this.todos = [...this.todos, { text: this.$input.value, checked: false }];
          this.$input.value = '';
      }
    }

    template() {
        return html`
            <style>
                :host {
                    display: block;
                    font-family: sans-serif;
                    text-align: center;
                }
                button {
                    border: none;
                    cursor: pointer;
                    background-color: Transparent;
                }
                ul {
                    list-style: none;
                    padding: 0;
                }
            </style>
            <h3>Raw web components + lit-html</h3>
            <br>
            <h1>To do</h1>
            <form id="todo-input">
                <input type="text" placeholder="Add a new to do"></input>
                <button @click=${this._addTodo}>✅</button>
            </form>
            <ul id="todos">
              ${this.todos.map((todo, index) => html`
                    <to-do-item 
                        ?checked=${todo.checked}
                        .index=${index}
                        text=${todo.text}
                        @onRemove=${this._removeTodo}
                        @onToggle=${this._toggleTodo}>    
                    </to-do-item>
                  `
              )}
            </ul>
        `;
    }

    set todos(value) {
        this._todos = value;
        render(this.template(), this._shadowRoot, {eventContext: this});
    }

    get todos() {
        return this._todos;
    }
}

window.customElements.define('to-do-app', TodoApp);

Got the general overview? Great! You’ll find quite a lot things have changed in our code, so let’s take a closer look.

The first thing you might have noticed is that the way we handled the rendering of our component has completely changed. In our old app we had to imperatively create a template element, set its innerHTML, clone it, and append it to our shadowroot. When we wanted to update our component, we had to create a bunch of elements, set their attributes, add their event listeners and append them to the DOM. All by hand. I’m getting a headache just reading that. What we’ve done instead is delegate all the rendering to lit-html.

Now we only declare our template once, we can set attributes, properties and events declaratively in the template, and just call lit-html’s render function when we need to. The great thing about lit-html is that it’s fast and efficient at rendering; It looks only at the dynamic expressions, and changes only what needs to be updated. And all this without the overhead of a framework!

You’ll also notice we changed our _addTodo, _removeTodo and _toggleTodo methods to some immutable update patterns instead. This is nice because every time we set the value of todos, we’ll trigger a render of our component. This is an important concept that we’ll explore more in the third and final part of this blog series.

Attributes, properties, and events

  • Learn about Lit-html
  • Lit-html in practice
  • Supercharge our web component
  • Attributes, properties, and events
  • Wrapping up

Let’s continue and take a look at how lit-html handles attributes, properties, and events.

${this.todos.map((todo, index) => {
    return html`
        <to-do-item 
            ?checked=${todo.checked}
            .index=${index}
            text=${todo.text}
            @onRemove=${this._removeTodo}
            @onToggle=${this._toggleTodo}>    
        </to-do-item>
    `;
})}

You might have seen this weird syntax in the updated version of our component, and wonder what it means. Lit-html allows us to declaratively set our attributes, properties and event handlers in our templates, as opposed to setting them imperatively. Since we learned all about attributes, properties and events in part one of this series, this should be easy enough to follow. If you need a refresher, I got you covered.

Let’s walk through all of this step by step.

Attributes

text=${todo.text}

We set attributes in lit-html… Exactly like you’d set an attribute in standard HTML. The only difference is the fact that we’re using a dynamic value in a template string. Very anticlimactic, I know. We previously had to set our attributes imperatively like this: el.setAttribute('text', todo.text);.

Hey! Listen!> Regular attributes are still only limited to String types!###

Boolean attributes

?checked=${todo.checked}

As you’ll remember from the last blog post, Boolean attributes are generally handled a bit differently…

Flashback

…> This means that only the following examples are acceptable for a true value:

<div hidden></div>
<div hidden=""></div>
<div hidden="hidden"></div>

And one for false:

<div></div>

Conveniently enough, lit-html allows us to easily specify our attribute as a Boolean attribute by prefixing the attribute name with a ?, and then makes sure the attribute is either present on the element, or not.

Previously we set our boolean attributes as:

if(todo.checked){
	el.setAttribute('checked', '');
}

and omitted it altogether when our conditional was falsy.

Properties

.index=${index}

If we want to pass down some rich data like arrays or objects, or in this case, a number value, we can simply use the dot prefix.

Previously, to set properties on our components, we had to imperatively query for the component, and set the property. Thanks to lit-html, we can handle all this in our template instead.

Previously we set properties as:

el.index = index;

Events

@onRemove=${this._removeTodo}

And finally, we can declaratively specify our event listeners by prefixing them with an @. Whenever the to-do-item component fires an onRemove event, this._removeTodo is called. Easy peasy.

Just to give you another example, here’s how we could handle a click event:

<button @click=${this._handleClick}></button>

Hey! Listen!> Notice how we specified an eventContext in our render() function: render(this.template(), this._shadowRoot, {eventContext: this});. This makes sure we always have the correct reference to this in our event handlers, and makes it so we don’t have to manually .bind(this) our event handlers in the constructor.##

Wrapping up

  • Learn about Lit-html
  • Lit-html in practice
  • Supercharge our web component
  • Attributes, properties, and events
  • Wrapping up

If you made it all the way here, you’re on your way to becoming a real Web Components hero. You’ve learned about lit-html, how lit-html renders, how to use attributes, properties and events, and how to implement lit-html to take care of the rendering of your Web Component.

Great job! We supercharged our web component, and it now efficiently renders to-do’s, but we still have a bunch of boilerplate code, and a lot of property and attribute management to take care of. It would be great if there would be an easier way to handle all this…

Web Components hero with LitElement

Web Components hero with LitElement

  • Recap
  • Properties and attributes
  • Lifecycle and rerendering
  • Conclusion

Lit-html and LitElement finally got their official (1.0 and 2.0 respectively) releases, and that makes it a great time to wrap up the Web Components: from Zero to Hero blog series. I hope you’ve found these blogs useful as you’ve read them; they’ve been a blast to write, and I very much appreciate all the feedback and response I’ve gotten!

Huh? A 2.0 release? Lit-element has moved away from the @polymer/lit-element namespace, to simply: lit-element. The lit-element npm package was previously owned by someone else and had already had a release, hence the 2.0 release.
Let’s get to it!

In the last blog post we learned how to implement lit-html to take care of templating for our web component. Let’s quickly recap the distinction between lit-html and lit-element:

  • Lit-html is a render library. It provides the what and the how.
  • LitElement is a web component base class. It provides the when and the where.

I also want to stress that LitElement is not a framework. It is simply a base class that extends HTMLElement. We can look at LitElement as an enhancement of the standard HTMLElement class, that will take care of our properties and attributes management, as well as a more refined rendering pipeline for us.

Lets take a quick look at our to-do-item component, rewritten with LitElement. You can find the full demo here, and on the github page:

import { LitElement, html, css } from 'https://unpkg.com/lit-element@latest/lit-element.js?module';

class TodoItem extends LitElement {
    static get properties() {
        return {
            text: { 
                type: String,
                reflect: true
            },
            checked: { 
                type: Boolean, 
                reflect: true 
            },
            index: { type: Number }
        }
    }

    constructor() {
        super();
        // set some default values
        this.text = '';
        this.checked = false;
    }

    _fire(eventType) {
        this.dispatchEvent(new CustomEvent(eventType, { detail: this.index }));   
    }
    
    static get styles() {
      return css`
	  :host {
	    display: block;
	    font-family: sans-serif;
	  }
			
	  .completed {
	    text-decoration: line-through;
	  }
			
	  button {
	    cursor: pointer;
	    border: none;
	  }
      `;
    }

    render() {
        return html`
            <li class="item">
                <input 
                    type="checkbox" 
                    ?checked=${this.checked} 
                    @change=${() => this._fire('onToggle')}>
                </input>
                <label class=${this.checked ? 'completed' : ''}>${this.text}</label>
                <button @click=${() => this._fire('onRemove')}>❌</button>
            </li>
        `;
    }
}

 

Properties and attributes

  • Recap
  • Properties and attributes
  • Lifecycle and rerendering
  • Conclusion

Let’s get straight into it. The first thing you might notice is that all of our setters and getters are gone, and have been replaced with LitElement’s static properties getter. This is great, because we’ve abstracted away a lot of boiler plate code and instead let LitElement take care of it.

So lets see how this works:

static get properties() {
    return {
        text: { 
            type: String,
            reflect: true
        },
        checked: { 
            type: Boolean, 
            reflect: true 
        },
        index: { type: Number }
    }
}

We can use the static properties getter to declare any attributes and properties we might need, and even pass some options to them. In this code, we’ve set a text, checked, and index property, and we’ll reflect the text and checked properties to attributes as well. Just like that. Remember how much work that was before? We had a whole chapter dedicated to reflecting properties to attributes!

We can even specify how we want attributes to be reflected:

static get properties() {
    return {
        text: { 
            type: String,
            reflect: true,
            attribute: 'todo'
        }
    }
}

Will reflect the text property in our DOM as the following attribute:

<to-do-item todo="Finish blog"></to-do-item>

Are you still confused about how reflecting properties to attributes works? Consider re-visiting part one of this blog series to catch up.
Additionally, and perhaps most importantly, the static properties getter will react to changes and trigger a rerender when a property has changed. We no longer have to call render functions manually to update, we just need to update a property, and LitElement will do all the work for us.
Hey! Listen!> You can still use custom getters and setters, but you’ll have to manually call this.requestUpdate() to trigger a rerender. Custom getters and setters can be useful for computed properties.###

Lifecycle and rerendering

  • Recap
  • Properties and attributes
  • Lifecycle and rerendering
  • Conclusion

Finally, let’s take a look at our to-do-app component:

import { LitElement, html } from 'lit-element';
import { repeat } from 'lit-html/directives/repeat';
import './to-do-item.js';

class TodoApp extends LitElement {
    static get properties() {
        return {
            todos: { type: Array }
        }
    }

    constructor() {
        super();
        this.todos = [];
    }

    firstUpdated() {
        this.$input = this.shadowRoot.querySelector('input');
    }

    _removeTodo(e) {
        this.todos = this.todos.filter((todo, index) => {
            return index !== e.detail;
        });
    }

    _toggleTodo(e) {
        this.todos = this.todos.map((todo, index) => {
            return index === e.detail ? {...todo, checked: !todo.checked} : todo;
        });
    }

    _addTodo(e) {
        e.preventDefault();
        if(this.$input.value.length > 0) {
            this.todos = [...this.todos, { text: this.$input.value, checked: false }];
            this.$input.value = '';
        }
    }

	static get styles() {
	  return css`
	     :host {
	         display: block;
	         font-family: sans-serif;
	         text-align: center;
	     }
	     button {
	         border: none;
	         cursor: pointer;
	     }
	     ul {
	         list-style: none;
	         padding: 0;
	     }
          `;
	}

    render() {
        return html`
            <h1>To do</h1>
            <form id="todo-input">
                <input type="text" placeholder="Add a new to do"></input>
                <button @click=${this._addTodo}>✅</button>
            </form>
            <ul id="todos">
                ${repeat(this.todos, 
                   (todo) => todo.text, 
                   (todo, index) => html`
                     <to-do-item 
                       .checked=${todo.checked}
                       .index=${index}
                       .text=${todo.text}
                       @onRemove=${this._removeTodo}
                       @onToggle=${this._toggleTodo}>    
                    </to-do-item>`
                  )}
            </ul>
        `;
    }
}

window.customElements.define('to-do-app', TodoApp);

You’ll notice that we’ve changed our functions up a little bit. We did this, because in order for LitElement to pick up changes and trigger a rerender, we need to immutably set arrays or objects. You can still use mutable patterns to change nested object properties or objects in arrays, but you’ll have to request a rerender manually by calling this.requestUpdate(), which could look like this:

_someFunction(newValue) {
	this.myObj.value = newValue;
	this.requestUpdate();
}

Which brings us to LitElement’s lifecycle. It’s important to note that LitElement extends HTMLElement, which means that we’ll still have access to the standard lifecycle callbacks like connectedCallback, disconnectedCallback, etc.

Additionally, LitElement comes with some lifecycle callbacks of it’s own. You can see a full example of LitElement’s lifecycle here.

 

shouldUpdate()

You can implement shouldUpdate() to control if updating and rendering should occur when property values change or requestUpdate() is called. This can be useful for when you don’t want to rerender.

firstUpdated()

firstUpdated is called when… well, your element has been updated the first time. This method can be useful for querying dom in your component.

 

updated()

Called right after your element has been updated and rerendered. You can implement this to perform post-updating tasks via DOM APIs, for example, focusing an element. Setting properties inside this method will not trigger another update.

And as I mentioned before, you can still implement connectedCallback() and disconnectedCallback().

Conclusion

  • Recap
  • Properties and attributes
  • Lifecycle
  • Conclusion

If you’ve made it all this way; congratulations! You are now a web components super hero. I hope this blog series was helpful and informative to you, and that it may function as a reference for when you need to remember something about web components.

If you’re interested in getting started with Web Components, make sure to check out open-wc. Open-wc provides recommendations including anything betwixt and between: developing, linting, testing, tooling, demoing, publishing and automating, and will help you get started in no time.

#webdevelopment #javascript #webdev #webcomponents

What is GEEK

Buddha Community

Web Components Tutorial: Go from Zero to Hero

Kao Candy

1558575131

good job

Jack Chou

1667610729

nice job

Perl Critic: The Leading Static analyzer for Perl

BUILD STATUS

NAME

Perl::Critic - Critique Perl source code for best-practices.

SYNOPSIS

use Perl::Critic;
my $file = shift;
my $critic = Perl::Critic->new();
my @violations = $critic->critique($file);
print @violations;

DESCRIPTION

Perl::Critic is an extensible framework for creating and applying coding standards to Perl source code. Essentially, it is a static source code analysis engine. Perl::Critic is distributed with a number of Perl::Critic::Policy modules that attempt to enforce various coding guidelines. Most Policy modules are based on Damian Conway's book Perl Best Practices. However, Perl::Critic is not limited to PBP and will even support Policies that contradict Conway. You can enable, disable, and customize those Polices through the Perl::Critic interface. You can also create new Policy modules that suit your own tastes.

For a command-line interface to Perl::Critic, see the documentation for perlcritic. If you want to integrate Perl::Critic with your build process, Test::Perl::Critic provides an interface that is suitable for test programs. Also, Test::Perl::Critic::Progressive is useful for gradually applying coding standards to legacy code. For the ultimate convenience (at the expense of some flexibility) see the criticism pragma.

If you'd like to try Perl::Critic without installing anything, there is a web-service available at http://perlcritic.com. The web-service does not yet support all the configuration features that are available in the native Perl::Critic API, but it should give you a good idea of what it does.

Also, ActivePerl includes a very slick graphical interface to Perl-Critic called perlcritic-gui. You can get a free community edition of ActivePerl from http://www.activestate.com.

PREREQUISITES

Perl::Critic runs on Perl back to Perl 5.6.1. It relies on the PPI module to do the heavy work of parsing Perl.

INTERFACE SUPPORT

The Perl::Critic module is considered to be a public class. Any changes to its interface will go through a deprecation cycle.

CONSTRUCTOR

new( [ -profile => $FILE, -severity => $N, -theme => $string, -include => \@PATTERNS, -exclude => \@PATTERNS, -top => $N, -only => $B, -profile-strictness => $PROFILE_STRICTNESS_{WARN|FATAL|QUIET}, -force => $B, -verbose => $N ], -color => $B, -pager => $string, -allow-unsafe => $B, -criticism-fatal => $B)

new()

Returns a reference to a new Perl::Critic object. Most arguments are just passed directly into Perl::Critic::Config, but I have described them here as well. The default value for all arguments can be defined in your .perlcriticrc file. See the "CONFIGURATION" section for more information about that. All arguments are optional key-value pairs as follows:

-profile is a path to a configuration file. If $FILE is not defined, Perl::Critic::Config attempts to find a .perlcriticrc configuration file in the current directory, and then in your home directory. Alternatively, you can set the PERLCRITIC environment variable to point to a file in another location. If a configuration file can't be found, or if $FILE is an empty string, then all Policies will be loaded with their default configuration. See "CONFIGURATION" for more information.

-severity is the minimum severity level. Only Policy modules that have a severity greater than $N will be applied. Severity values are integers ranging from 1 (least severe violations) to 5 (most severe violations). The default is 5. For a given -profile, decreasing the -severity will usually reveal more Policy violations. You can set the default value for this option in your .perlcriticrc file. Users can redefine the severity level for any Policy in their .perlcriticrc file. See "CONFIGURATION" for more information.

If it is difficult for you to remember whether severity "5" is the most or least restrictive level, then you can use one of these named values:

  SEVERITY NAME   ...is equivalent to...   SEVERITY NUMBER
  --------------------------------------------------------
  -severity => 'gentle'                     -severity => 5
  -severity => 'stern'                      -severity => 4
  -severity => 'harsh'                      -severity => 3
  -severity => 'cruel'                      -severity => 2
  -severity => 'brutal'                     -severity => 1

The names reflect how severely the code is criticized: a gentle criticism reports only the most severe violations, and so on down to a brutal criticism which reports even the most minor violations.

-theme is special expression that determines which Policies to apply based on their respective themes. For example, the following would load only Policies that have a 'bugs' AND 'pbp' theme:

  my $critic = Perl::Critic->new( -theme => 'bugs && pbp' );

Unless the -severity option is explicitly given, setting -theme silently causes the -severity to be set to 1. You can set the default value for this option in your .perlcriticrc file. See the "POLICY THEMES" section for more information about themes.

-include is a reference to a list of string @PATTERNS. Policy modules that match at least one m/$PATTERN/ixms will always be loaded, irrespective of all other settings. For example:

  my $critic = Perl::Critic->new(-include => ['layout'], -severity => 4);

This would cause Perl::Critic to apply all the CodeLayout::* Policy modules even though they have a severity level that is less than 4. You can set the default value for this option in your .perlcriticrc file. You can also use -include in conjunction with the -exclude option. Note that -exclude takes precedence over -include when a Policy matches both patterns.

-exclude is a reference to a list of string @PATTERNS. Policy modules that match at least one m/$PATTERN/ixms will not be loaded, irrespective of all other settings. For example:

  my $critic = Perl::Critic->new(-exclude => ['strict'], -severity => 1);

This would cause Perl::Critic to not apply the RequireUseStrict and ProhibitNoStrict Policy modules even though they have a severity level that is greater than 1. You can set the default value for this option in your .perlcriticrc file. You can also use -exclude in conjunction with the -include option. Note that -exclude takes precedence over -include when a Policy matches both patterns.

-single-policy is a string PATTERN. Only one policy that matches m/$PATTERN/ixms will be used. Policies that do not match will be excluded. This option has precedence over the -severity, -theme, -include, -exclude, and -only options. You can set the default value for this option in your .perlcriticrc file.

-top is the maximum number of Violations to return when ranked by their severity levels. This must be a positive integer. Violations are still returned in the order that they occur within the file. Unless the -severity option is explicitly given, setting -top silently causes the -severity to be set to 1. You can set the default value for this option in your .perlcriticrc file.

-only is a boolean value. If set to a true value, Perl::Critic will only choose from Policies that are mentioned in the user's profile. If set to a false value (which is the default), then Perl::Critic chooses from all the Policies that it finds at your site. You can set the default value for this option in your .perlcriticrc file.

-profile-strictness is an enumerated value, one of "$PROFILE_STRICTNESS_WARN" in Perl::Critic::Utils::Constants (the default), "$PROFILE_STRICTNESS_FATAL" in Perl::Critic::Utils::Constants, and "$PROFILE_STRICTNESS_QUIET" in Perl::Critic::Utils::Constants. If set to "$PROFILE_STRICTNESS_FATAL" in Perl::Critic::Utils::Constants, Perl::Critic will make certain warnings about problems found in a .perlcriticrc or file specified via the -profile option fatal. For example, Perl::Critic normally only warns about profiles referring to non-existent Policies, but this value makes this situation fatal. Correspondingly, "$PROFILE_STRICTNESS_QUIET" in Perl::Critic::Utils::Constants makes Perl::Critic shut up about these things.

-force is a boolean value that controls whether Perl::Critic observes the magical "## no critic" annotations in your code. If set to a true value, Perl::Critic will analyze all code. If set to a false value (which is the default) Perl::Critic will ignore code that is tagged with these annotations. See "BENDING THE RULES" for more information. You can set the default value for this option in your .perlcriticrc file.

-verbose can be a positive integer (from 1 to 11), or a literal format specification. See Perl::Critic::Violation for an explanation of format specifications. You can set the default value for this option in your .perlcriticrc file.

-unsafe directs Perl::Critic to allow the use of Policies that are marked as "unsafe" by the author. Such policies may compile untrusted code or do other nefarious things.

-color and -pager are not used by Perl::Critic but is provided for the benefit of perlcritic.

-criticism-fatal is not used by Perl::Critic but is provided for the benefit of criticism.

-color-severity-highest, -color-severity-high, -color-severity- medium, -color-severity-low, and -color-severity-lowest are not used by Perl::Critic, but are provided for the benefit of perlcritic. Each is set to the Term::ANSIColor color specification to be used to display violations of the corresponding severity.

-files-with-violations and -files-without-violations are not used by Perl::Critic, but are provided for the benefit of perlcritic, to cause only the relevant filenames to be displayed.

METHODS

critique( $source_code )

Runs the $source_code through the Perl::Critic engine using all the Policies that have been loaded into this engine. If $source_code is a scalar reference, then it is treated as a string of actual Perl code. If $source_code is a reference to an instance of PPI::Document, then that instance is used directly. Otherwise, it is treated as a path to a local file containing Perl code. This method returns a list of Perl::Critic::Violation objects for each violation of the loaded Policies. The list is sorted in the order that the Violations appear in the code. If there are no violations, this method returns an empty list.

add_policy( -policy => $policy_name, -params => \%param_hash )

Creates a Policy object and loads it into this Critic. If the object cannot be instantiated, it will throw a fatal exception. Otherwise, it returns a reference to this Critic.

-policy is the name of a Perl::Critic::Policy subclass module. The 'Perl::Critic::Policy' portion of the name can be omitted for brevity. This argument is required.

-params is an optional reference to a hash of Policy parameters. The contents of this hash reference will be passed into to the constructor of the Policy module. See the documentation in the relevant Policy module for a description of the arguments it supports.

policies()

Returns a list containing references to all the Policy objects that have been loaded into this engine. Objects will be in the order that they were loaded.

config()

Returns the Perl::Critic::Config object that was created for or given to this Critic.

statistics()

Returns the Perl::Critic::Statistics object that was created for this Critic. The Statistics object accumulates data for all files that are analyzed by this Critic.

FUNCTIONAL INTERFACE

For those folks who prefer to have a functional interface, The critique method can be exported on request and called as a static function. If the first argument is a hashref, its contents are used to construct a new Perl::Critic object internally. The keys of that hash should be the same as those supported by the Perl::Critic::new() method. Here are some examples:

use Perl::Critic qw(critique);

# Use default parameters...
@violations = critique( $some_file );

# Use custom parameters...
@violations = critique( {-severity => 2}, $some_file );

# As a one-liner
%> perl -MPerl::Critic=critique -e 'print critique(shift)' some_file.pm

None of the other object-methods are currently supported as static functions. Sorry.

CONFIGURATION

Most of the settings for Perl::Critic and each of the Policy modules can be controlled by a configuration file. The default configuration file is called .perlcriticrc. Perl::Critic will look for this file in the current directory first, and then in your home directory. Alternatively, you can set the PERLCRITIC environment variable to explicitly point to a different file in another location. If none of these files exist, and the -profile option is not given to the constructor, then all the modules that are found in the Perl::Critic::Policy namespace will be loaded with their default configuration.

The format of the configuration file is a series of INI-style blocks that contain key-value pairs separated by '='. Comments should start with '#' and can be placed on a separate line or after the name-value pairs if you desire.

Default settings for Perl::Critic itself can be set before the first named block. For example, putting any or all of these at the top of your configuration file will set the default value for the corresponding constructor argument.

severity  = 3                                     #Integer or named level
only      = 1                                     #Zero or One
force     = 0                                     #Zero or One
verbose   = 4                                     #Integer or format spec
top       = 50                                    #A positive integer
theme     = (pbp || security) && bugs             #A theme expression
include   = NamingConventions ClassHierarchies    #Space-delimited list
exclude   = Variables  Modules::RequirePackage    #Space-delimited list
criticism-fatal = 1                               #Zero or One
color     = 1                                     #Zero or One
allow-unsafe = 1                                  #Zero or One
pager     = less                                  #pager to pipe output to

The remainder of the configuration file is a series of blocks like this:

[Perl::Critic::Policy::Category::PolicyName]
severity = 1
set_themes = foo bar
add_themes = baz
maximum_violations_per_document = 57
arg1 = value1
arg2 = value2

Perl::Critic::Policy::Category::PolicyName is the full name of a module that implements the policy. The Policy modules distributed with Perl::Critic have been grouped into categories according to the table of contents in Damian Conway's book Perl Best Practices. For brevity, you can omit the 'Perl::Critic::Policy' part of the module name.

severity is the level of importance you wish to assign to the Policy. All Policy modules are defined with a default severity value ranging from 1 (least severe) to 5 (most severe). However, you may disagree with the default severity and choose to give it a higher or lower severity, based on your own coding philosophy. You can set the severity to an integer from 1 to 5, or use one of the equivalent names:

SEVERITY NAME ...is equivalent to... SEVERITY NUMBER
----------------------------------------------------
gentle                                             5
stern                                              4
harsh                                              3
cruel                                              2
brutal                                             1

The names reflect how severely the code is criticized: a gentle criticism reports only the most severe violations, and so on down to a brutal criticism which reports even the most minor violations.

set_themes sets the theme for the Policy and overrides its default theme. The argument is a string of one or more whitespace-delimited alphanumeric words. Themes are case-insensitive. See "POLICY THEMES" for more information.

add_themes appends to the default themes for this Policy. The argument is a string of one or more whitespace-delimited words. Themes are case- insensitive. See "POLICY THEMES" for more information.

maximum_violations_per_document limits the number of Violations the Policy will return for a given document. Some Policies have a default limit; see the documentation for the individual Policies to see whether there is one. To force a Policy to not have a limit, specify "no_limit" or the empty string for the value of this parameter.

The remaining key-value pairs are configuration parameters that will be passed into the constructor for that Policy. The constructors for most Policy objects do not support arguments, and those that do should have reasonable defaults. See the documentation on the appropriate Policy module for more details.

Instead of redefining the severity for a given Policy, you can completely disable a Policy by prepending a '-' to the name of the module in your configuration file. In this manner, the Policy will never be loaded, regardless of the -severity given to the Perl::Critic constructor.

A simple configuration might look like this:

#--------------------------------------------------------------
# I think these are really important, so always load them

[TestingAndDebugging::RequireUseStrict]
severity = 5

[TestingAndDebugging::RequireUseWarnings]
severity = 5

#--------------------------------------------------------------
# I think these are less important, so only load when asked

[Variables::ProhibitPackageVars]
severity = 2

[ControlStructures::ProhibitPostfixControls]
allow = if unless  # My custom configuration
severity = cruel   # Same as "severity = 2"

#--------------------------------------------------------------
# Give these policies a custom theme.  I can activate just
# these policies by saying `perlcritic -theme larry`

[Modules::RequireFilenameMatchesPackage]
add_themes = larry

[TestingAndDebugging::RequireTestLables]
add_themes = larry curly moe

#--------------------------------------------------------------
# I do not agree with these at all, so never load them

[-NamingConventions::Capitalization]
[-ValuesAndExpressions::ProhibitMagicNumbers]

#--------------------------------------------------------------
# For all other Policies, I accept the default severity,
# so no additional configuration is required for them.

For additional configuration examples, see the perlcriticrc file that is included in this examples directory of this distribution.

Damian Conway's own Perl::Critic configuration is also included in this distribution as examples/perlcriticrc-conway.

THE POLICIES

A large number of Policy modules are distributed with Perl::Critic. They are described briefly in the companion document Perl::Critic::PolicySummary and in more detail in the individual modules themselves. Say "perlcritic -doc PATTERN" to see the perldoc for all Policy modules that match the regex m/PATTERN/ixms

There are a number of distributions of additional policies on CPAN. If Perl::Critic doesn't contain a policy that you want, some one may have already written it. See the "SEE ALSO" section below for a list of some of these distributions.

POLICY THEMES

Each Policy is defined with one or more "themes". Themes can be used to create arbitrary groups of Policies. They are intended to provide an alternative mechanism for selecting your preferred set of Policies. For example, you may wish disable a certain subset of Policies when analyzing test programs. Conversely, you may wish to enable only a specific subset of Policies when analyzing modules.

The Policies that ship with Perl::Critic have been broken into the following themes. This is just our attempt to provide some basic logical groupings. You are free to invent new themes that suit your needs.

THEME             DESCRIPTION
--------------------------------------------------------------------------
core              All policies that ship with Perl::Critic
pbp               Policies that come directly from "Perl Best Practices"
bugs              Policies that that prevent or reveal bugs
certrec           Policies that CERT recommends
certrule          Policies that CERT considers rules
maintenance       Policies that affect the long-term health of the code
cosmetic          Policies that only have a superficial effect
complexity        Policies that specifically relate to code complexity
security          Policies that relate to security issues
tests             Policies that are specific to test programs

Any Policy may fit into multiple themes. Say "perlcritic -list" to get a listing of all available Policies and the themes that are associated with each one. You can also change the theme for any Policy in your .perlcriticrc file. See the "CONFIGURATION" section for more information about that.

Using the -theme option, you can create an arbitrarily complex rule that determines which Policies will be loaded. Precedence is the same as regular Perl code, and you can use parentheses to enforce precedence as well. Supported operators are:

Operator    Alternative    Example
-----------------------------------------------------------------
&&          and            'pbp && core'
||          or             'pbp || (bugs && security)'
!           not            'pbp && ! (portability || complexity)'

Theme names are case-insensitive. If the -theme is set to an empty string, then it evaluates as true all Policies.

BENDING THE RULES

Perl::Critic takes a hard-line approach to your code: either you comply or you don't. In the real world, it is not always practical (nor even possible) to fully comply with coding standards. In such cases, it is wise to show that you are knowingly violating the standards and that you have a Damn Good Reason (DGR) for doing so.

To help with those situations, you can direct Perl::Critic to ignore certain lines or blocks of code by using annotations:

require 'LegacyLibaray1.pl';  ## no critic
require 'LegacyLibrary2.pl';  ## no critic

for my $element (@list) {

    ## no critic

    $foo = "";               #Violates 'ProhibitEmptyQuotes'
    $barf = bar() if $foo;   #Violates 'ProhibitPostfixControls'
    #Some more evil code...

    ## use critic

    #Some good code...
    do_something($_);
}

The "## no critic" annotations direct Perl::Critic to ignore the remaining lines of code until a "## use critic" annotation is found. If the "## no critic" annotation is on the same line as a code statement, then only that line of code is overlooked. To direct perlcritic to ignore the "## no critic" annotations, use the --force option.

A bare "## no critic" annotation disables all the active Policies. If you wish to disable only specific Policies, add a list of Policy names as arguments, just as you would for the "no strict" or "no warnings" pragmas. For example, this would disable the ProhibitEmptyQuotes and ProhibitPostfixControls policies until the end of the block or until the next "## use critic" annotation (whichever comes first):

## no critic (EmptyQuotes, PostfixControls)

# Now exempt from ValuesAndExpressions::ProhibitEmptyQuotes
$foo = "";

# Now exempt ControlStructures::ProhibitPostfixControls
$barf = bar() if $foo;

# Still subjected to ValuesAndExpression::RequireNumberSeparators
$long_int = 10000000000;

Since the Policy names are matched against the "## no critic" arguments as regular expressions, you can abbreviate the Policy names or disable an entire family of Policies in one shot like this:

## no critic (NamingConventions)

# Now exempt from NamingConventions::Capitalization
my $camelHumpVar = 'foo';

# Now exempt from NamingConventions::Capitalization
sub camelHumpSub {}

The argument list must be enclosed in parentheses or brackets and must contain one or more comma-separated barewords (e.g. don't use quotes). The "## no critic" annotations can be nested, and Policies named by an inner annotation will be disabled along with those already disabled an outer annotation.

Some Policies like Subroutines::ProhibitExcessComplexity apply to an entire block of code. In those cases, the "## no critic" annotation must appear on the line where the violation is reported. For example:

sub complicated_function {  ## no critic (ProhibitExcessComplexity)
    # Your code here...
}

Policies such as Documentation::RequirePodSections apply to the entire document, in which case violations are reported at line 1.

Use this feature wisely. "## no critic" annotations should be used in the smallest possible scope, or only on individual lines of code. And you should always be as specific as possible about which Policies you want to disable (i.e. never use a bare "## no critic"). If Perl::Critic complains about your code, try and find a compliant solution before resorting to this feature.

THE Perl::Critic PHILOSOPHY

Coding standards are deeply personal and highly subjective. The goal of Perl::Critic is to help you write code that conforms with a set of best practices. Our primary goal is not to dictate what those practices are, but rather, to implement the practices discovered by others. Ultimately, you make the rules -- Perl::Critic is merely a tool for encouraging consistency. If there is a policy that you think is important or that we have overlooked, we would be very grateful for contributions, or you can simply load your own private set of policies into Perl::Critic.

EXTENDING THE CRITIC

The modular design of Perl::Critic is intended to facilitate the addition of new Policies. You'll need to have some understanding of PPI, but most Policy modules are pretty straightforward and only require about 20 lines of code. Please see the Perl::Critic::DEVELOPER file included in this distribution for a step-by-step demonstration of how to create new Policy modules.

If you develop any new Policy modules, feel free to send them to <team@perlcritic.com> and I'll be happy to consider putting them into the Perl::Critic distribution. Or if you would like to work on the Perl::Critic project directly, you can fork our repository at https://github.com/Perl-Critic/Perl-Critic.git.

The Perl::Critic team is also available for hire. If your organization has its own coding standards, we can create custom Policies to enforce your local guidelines. Or if your code base is prone to a particular defect pattern, we can design Policies that will help you catch those costly defects before they go into production. To discuss your needs with the Perl::Critic team, just contact <team@perlcritic.com>.

PREREQUISITES

Perl::Critic requires the following modules:

B::Keywords

Config::Tiny

Exception::Class

File::Spec

File::Spec::Unix

File::Which

IO::String

List::SomeUtils

List::Util

Module::Pluggable

Perl::Tidy

Pod::Spell

PPI

Pod::PlainText

Pod::Select

Pod::Usage

Readonly

Scalar::Util

String::Format

Task::Weaken

Term::ANSIColor

Text::ParseWords

version

CONTACTING THE DEVELOPMENT TEAM

You are encouraged to subscribe to the public mailing list at https://groups.google.com/d/forum/perl-critic. At least one member of the development team is usually hanging around in irc://irc.perl.org/#perlcritic and you can follow Perl::Critic on Twitter, at https://twitter.com/perlcritic.

SEE ALSO

There are a number of distributions of additional Policies available. A few are listed here:

Perl::Critic::More

Perl::Critic::Bangs

Perl::Critic::Lax

Perl::Critic::StricterSubs

Perl::Critic::Swift

Perl::Critic::Tics

These distributions enable you to use Perl::Critic in your unit tests:

Test::Perl::Critic

Test::Perl::Critic::Progressive

There is also a distribution that will install all the Perl::Critic related modules known to the development team:

Task::Perl::Critic

BUGS

Scrutinizing Perl code is hard for humans, let alone machines. If you find any bugs, particularly false-positives or false-negatives from a Perl::Critic::Policy, please submit them at https://github.com/Perl-Critic/Perl-Critic/issues. Thanks.

CREDITS

Adam Kennedy - For creating PPI, the heart and soul of Perl::Critic.

Damian Conway - For writing Perl Best Practices, finally :)

Chris Dolan - For contributing the best features and Policy modules.

Andy Lester - Wise sage and master of all-things-testing.

Elliot Shank - The self-proclaimed quality freak.

Giuseppe Maxia - For all the great ideas and positive encouragement.

and Sharon, my wife - For putting up with my all-night code sessions.

Thanks also to the Perl Foundation for providing a grant to support Chris Dolan's project to implement twenty PBP policies. http://www.perlfoundation.org/april_1_2007_new_grant_awards

Thanks also to this incomplete laundry list of folks who have contributed to Perl::Critic in some way: Gregory Oschwald, Mike O'Regan, Tom Hukins, Omer Gazit, Evan Zacks, Paul Howarth, Sawyer X, Christian Walde, Dave Rolsky, Jakub Wilk, Roy Ivy III, Oliver Trosien, Glenn Fowler, Matt Creenan, Alex Balhatchet, Sebastian Paaske Tørholm, Stuart A Johnston, Dan Book, Steven Humphrey, James Raspass, Nick Tonkin, Harrison Katz, Douglas Sims, Mark Fowler, Alan Berndt, Neil Bowers, Sergey Romanov, Gabor Szabo, Graham Knop, Mike Eldridge, David Steinbrunner, Kirk Kimmel, Guillaume Aubert, Dave Cross, Anirvan Chatterjee, Todd Rinaldo, Graham Ollis, Karen Etheridge, Jonas Brømsø, Olaf Alders, Jim Keenan, Slaven Rezić, Szymon Nieznański.

AUTHOR

Jeffrey Ryan Thalhammer jeff@imaginative-software.com

COPYRIGHT

Copyright (c) 2005-2018 Imaginative Software Systems. All rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The full text of this license can be found in the LICENSE file included with this module.


Download Details:

Author: Perl-Critic
Source Code: https://github.com/Perl-Critic/Perl-Critic

License: View license

#perl 

Fannie  Zemlak

Fannie Zemlak

1599854400

What's new in the go 1.15

Go announced Go 1.15 version on 11 Aug 2020. Highlighted updates and features include Substantial improvements to the Go linker, Improved allocation for small objects at high core counts, X.509 CommonName deprecation, GOPROXY supports skipping proxies that return errors, New embedded tzdata package, Several Core Library improvements and more.

As Go promise for maintaining backward compatibility. After upgrading to the latest Go 1.15 version, almost all existing Golang applications or programs continue to compile and run as older Golang version.

#go #golang #go 1.15 #go features #go improvement #go package #go new features

Go-web-workshop: Build Web Applications with Go on App Engine

Building Web Applications with Go

Welcome, gopher! You're not a gopher? Well, this workshop is for gophers, or people that use the Go programming language. But fear not if you've never written any Go before! I'd recommend you learn the basics for the language first with the Go tour.

This workshop has been run a couple of times with an instructor leading. The goal of this repo is to make it as easy as possible for individuals to follow the content by themselves. If you get stuck at any point, feel free to file issues asking questions.

Setting up your workspace

To go through this you will need the following:

  1. You have installed the Go Programming Language.
  2. You have set up a GOPATH by following the How to Write Go Code tutorial.
  3. You are somewhat familiar with the basics of Go. (The Go Tour is a pretty good place to start)
  4. You have a Google account and you have installed the Google Cloud SDK.

Contents

There's a lot to say about how to build web applications, in Go or any other language. But we only have one day so we won't try to cover too much. Instead we'll cover the basics, so you'll be able to explore other solutions and frameworks later.

The workshops is divided in eleven sections:

Resources

These are places where you can find more information for Go:

My favorite aspect of Go is its community, and you are now part of it too. Welcome!

As a newcomer to the Go community you might have questions or get blocked at some point. This is completely normal, and we're here to help you. Some of the places where gophers tend to hang out are:

Disclaimer

This is not an official Google product (experimental or otherwise), it is just code that happens to be owned by Google.

Author: Campoy
Source Code: https://github.com/campoy/go-web-workshop 
License: Apache-2.0 license

#go #golang #web 

Evolution in Web Design: A Case Study of 25 Years - Prismetric

The term web design simply encompasses a design process related to the front-end design of website that includes writing mark-up. Creative web design has a considerable impact on your perceived business credibility and quality. It taps onto the broader scopes of web development services.

Web designing is identified as a critical factor for the success of websites and eCommerce. The internet has completely changed the way businesses and brands operate. Web design and web development go hand-in-hand and the need for a professional web design and development company, offering a blend of creative designs and user-centric elements at an affordable rate, is growing at a significant rate.

In this blog, we have focused on the different areas of designing a website that covers all the trends, tools, and techniques coming up with time.

Web design
In 2020 itself, the number of smartphone users across the globe stands at 6.95 billion, with experts suggesting a high rise of 17.75 billion by 2024. On the other hand, the percentage of Gen Z web and internet users worldwide is up to 98%. This is not just a huge market but a ginormous one to boost your business and grow your presence online.

Web Design History
At a huge particle physics laboratory, CERN in Switzerland, the son of computer scientist Barner Lee published the first-ever website on August 6, 1991. He is not only the first web designer but also the creator of HTML (HyperText Markup Language). The worldwide web persisted and after two years, the world’s first search engine was born. This was just the beginning.

Evolution of Web Design over the years
With the release of the Internet web browser and Windows 95 in 1995, most trading companies at that time saw innumerable possibilities of instant worldwide information and public sharing of websites to increase their sales. This led to the prospect of eCommerce and worldwide group communications.

The next few years saw a soaring launch of the now-so-famous websites such as Yahoo, Amazon, eBay, Google, and substantially more. In 2004, by the time Facebook was launched, there were more than 50 million websites online.

Then came the era of Google, the ruler of all search engines introducing us to search engine optimization (SEO) and businesses sought their ways to improve their ranks. The world turned more towards mobile web experiences and responsive mobile-friendly web designs became requisite.

Let’s take a deep look at the evolution of illustrious brands to have a profound understanding of web design.

Here is a retrospection of a few widely acclaimed brands over the years.

Netflix
From a simple idea of renting DVDs online to a multi-billion-dollar business, saying that Netflix has come a long way is an understatement. A company that has sent shockwaves across Hollywood in the form of content delivery. Abundantly, Netflix (NFLX) is responsible for the rise in streaming services across 190 countries and meaningful changes in the entertainment industry.

1997-2000

The idea of Netflix was born when Reed Hastings and Marc Randolph decided to rent DVDs by mail. With 925 titles and a pay-per-rental model, Netflix.com debuts the first DVD rental and sales site with all novel features. It offered unlimited rentals without due dates or monthly rental limitations with a personalized movie recommendation system.

Netflix 1997-2000

2001-2005

Announcing its initial public offering (IPO) under the NASDAQ ticker NFLX, Netflix reached over 1 million subscribers in the United States by introducing a profile feature in their influential website design along with a free trial allowing members to create lists and rate their favorite movies. The user experience was quite engaging with the categorization of content, recommendations based on history, search engine, and a queue of movies to watch.

Netflix 2001-2005 -2003

2006-2010

They then unleashed streaming and partnering with electronic brands such as blu-ray, Xbox, and set-top boxes so that users can watch series and films straight away. Later in 2010, they also launched their sophisticated website on mobile devices with its iconic red and black themed background.

Netflix 2006-2010 -2007

2011-2015

In 2013, an eye-tracking test revealed that the users didn’t focus on the details of the movie or show in the existing interface and were perplexed with the flow of information. Hence, the professional web designers simply shifted the text from the right side to the top of the screen. With Daredevil, an audio description feature was also launched for the visually impaired ones.

Netflix 2011-2015

2016-2020

These years, Netflix came with a plethora of new features for their modern website design such as AutoPay, snippets of trailers, recommendations categorized by genre, percentage based on user experience, upcoming shows, top 10 lists, etc. These web application features yielded better results in visual hierarchy and flow of information across the website.

Netflix 2016-2020

2021

With a sleek logo in their iconic red N, timeless black background with a ‘Watch anywhere, Cancel anytime’ the color, the combination, the statement, and the leading ott platform for top video streaming service Netflix has overgrown into a revolutionary lifestyle of Netflix and Chill.

Netflix 2021

Contunue to read: Evolution in Web Design: A Case Study of 25 Years

#web #web-design #web-design-development #web-design-case-study #web-design-history #web-development

Willie  Beier

Willie Beier

1596728880

Tutorial: Getting Started with R and RStudio

In this tutorial we’ll learn how to begin programming with R using RStudio. We’ll install R, and RStudio RStudio, an extremely popular development environment for R. We’ll learn the key RStudio features in order to start programming in R on our own.

If you already know how to use RStudio and want to learn some tips, tricks, and shortcuts, check out this Dataquest blog post.

Table of Contents

#data science tutorials #beginner #r tutorial #r tutorials #rstats #tutorial #tutorials