Laravel Livewire Shopping Cart Demo

Laravel Livewire Shopping Cart Demo

https://ift.tt/2yp0nPs


Repository: https://github.com/Light-it-labs/laravel-livewire-cart

Live Demo: https://livewire-shopping-cart.demo.lightit.io/products‌‌


Developers may have a hard time adapting to the frontend environment. There are several challenges, for example learning how to work with a new library for the construction of interfaces, such as ReactJs, VueJs or simply Javascript. It’s good for developers to have experience in these technologies, though they have a logarithmic learning curve.‌‌

It’s for this reason that this time I bring you a guide to a framework for Laravel that is trending at the moment: Livewire.

‌‌What’s Livewire?‌‌

It comes as an attractive solution for developers since it allows building user interfaces as PHP components without the need to use reactive javascript.‌‌

Livewire is defined as a full-stack framework for building dynamic interfaces. With this framework you can build user interfaces through PHP classes, which allows backend developers to work in an environment they are used to work in.‌‌

How does it work?

When we visit a page, in the first load of the interface, Livewire is responsible for rendering the component as a simple blade and injecting the HTML result to the requested page.‌‌

Then the magic happens, every time actions are triggered due to user interactions with the interface, requests (AJAX) are executed, which returns the rendered HTML with the corresponding changes. Then it is again injected into the page.‌‌

Finally, the library automatically detects the changes that need to be done to the DOM, updating only what’s necessary.‌‌

The challenge

We will venture into the implementation of a product catalog, which will have a simple shopping cart for checkout. It will also be able of adding and removing products from it. The objective is to understand how it works and the most important characteristics of this package.‌‌

Enough with theoretical aspects… let’s get to work!‌‌

First of all we will initialize a Laravel project from scratch. To do this, we execute:

composer create-project --prefer-dist laravel/laravel laravel-livewire-example

The next step is to configure the project. First, you should configure the .env file and then install and configure livewire.

composer require livewire/livewire

After finishing up these first two stages, we will create the product’s entity with its model, migration, factory and data seeder. This is how we obtain fake data to run tests with the project. Then, execute the following command:

php artisan make:model -msf Product 

– m | creates migration

– s | creates seeder

– f | creates factory      ‌

Now it’s time to update the content of the files created by the command:

<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateProductsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('products', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('description'); $table->float('price'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('products'); } }
database/migrations/xxxx_xx_xx_xxxxxx_create_products_table.php
<?php /** @var \Illuminate\Database\Eloquent\Factory $factory */ use App\Product; use Faker\Generator as Faker; $factory->define(Product::class, function (Faker $faker) { return [ 'name' => $faker->word, 'description' => $faker->text(180), 'price' => $faker->numberBetween(50, 100) ]; });
database/factories/ProductFactory.php
<?php use App\Product; use Illuminate\Database\Seeder; class ProductSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { factory(Product::class, 50)->create(); } }
database/seeds/ProductSeeder.php
<?php use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { /** * Seed the application's database. * * @return void */ public function run() { $this->call(ProductSeeder::class); } }
database/seeds/DatabaseSeeder.php

Thanks to these changes we are ready to execute the next command while having data to work with.

php artisan migrate:fresh --seed

If everything was correctly executed, this message should appear at the end of the console: “Database seeding completed successfully.”

In this case, we’ll manage the state of the shopping cart with a Laravel session. We’ll create a facade that manages it using basic operations to obtain, add and remove products. I won’t explain how to configure a facade in Laravel because it’s not part of the guide’s objective. Our facade should look like this:‌‌

<?php namespace App\Helpers; use App\Product; class Cart { public function __construct() { if($this->get() === null) $this->set($this->empty()); } public function add(Product $product): void { $cart = $this->get(); array_push($cart['products'], $product); $this->set($cart); } public function remove(int $productId): void { $cart = $this->get(); array_splice($cart['products'], array_search($productId, array_column($cart['products'], 'id')), 1); $this->set($cart); } public function clear(): void { $this->set($this->empty()); } public function empty(): array { return [ 'products' => [], ]; } public function get(): ?array { return request()->session()->get('cart'); } private function set($cart): void { request()->session()->put('cart', $cart); } } 
Cart.php‌‌

Now that our facade is configured properly we can start developing our catalog. We’ll be using TailwindCSS to structure and style the application. In order to add this dependency we will follow the following steps:

npm install tailwindcss npx tailwind init npm install npm run watch

Now we should modify the following file by adding the Tailwind’s directives:‌‌resources/sass/app.scss

@tailwind base; @tailwind components; @tailwind utilities; 

Finally, we import tailwind to the project in the next file:‌‌

const mix = require('laravel-mix'); mix.js('resources/js/app.js', 'public/js'); const tailwindcss = require('tailwindcss') mix.sass('resources/sass/app.scss', 'public/css') .options({ processCssUrls: false, postCss: [ tailwindcss('tailwind.config.js') ], }) 
webpack.mix.js

We now need to define our layout. Livewire will automatically search for a layout named app.blade.php and render the components by default where we define the content section:‌‌

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Laravel</title> <link href="" rel="stylesheet"> @livewireStyles </head> <body> <div class="mx-auto"> @yield('content') </div> @livewireScripts <script src=""></script> </body> </html>
resources/views/layouts/app.blade.php

To ease the access to the different views, we should add a navigation bar and create a new file called resources/views/layouts/header.blade.php  with the following content:

<nav class="flex items-center justify-between flex-wrap p-6 mb-6 shadow"> <div class="block lg:hidden"> <button class="flex items-center px-3 py-2 border rounded text-teal-200 border-teal-400 hover:text-white hover:border-white"> <svg class="fill-current h-3 w-3" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>Menu</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"/></svg> </button> </div> <div class="w-full block flex-grow lg:flex lg:items-center lg:w-auto"> <div class="text-sm lg:flex-grow"> <a href="/" class="block mt-4 lg:inline-block lg:mt-0 mr-4"> Home </a> <a href="/products" class="block mt-4 lg:inline-block lg:mt-0 mr-4"> Products </a> <a href="/cart" class="block mt-4 lg:inline-block lg:mt-0 mr-4"> Cart </a> </div> </div> </nav>
resources/views/layouts/header.blade.php

It’s necessary to update the layout and integrate the header. The layout file should be seen like this:‌‌

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Laravel</title> <link href="" rel="stylesheet"> @livewireStyles </head> <body> @include('layouts.header') <div class="mx-auto"> @yield('content') </div> @livewireScripts <script src=""></script> </body> </html>
resources/views/layouts/app.blade.php

Now we’re ready to start creating components with Livewire. We’ll start creating a component for the home interface, in which we want to show a welcome message.

To do this, we will execute the command php artisan make:livewire Home which will automatically add two new files in the project. The first one contains all the logic of the components and the other one contains all the HTML linked to it. It’s worth mentioning that this last one is a blade file, so we can use the same blade directives we’re used to using in Laravel.‌‌

The views files are in resources/views/livewire , we must add content to the file home.blade.php within the existing route tag <div>.         ‌


! Don’t forget that the blade file must have only one root element.


<div> <p class="text-center p-6"> <span class="text-4xl">Welcome!</span> </p> </div>
resources/views/livewire/home.blade.php

The next step is to modify the route’s answer so that Livewire can take control of the route and render the component within the section ‘content’ defined in the layout.‌‌

Let’s now modify the file web.php, it should be seen this way:‌‌

<?php use Illuminate\Support\Facades\Route; Route::livewire('/', 'home')->name('home');
routes/web.php

! Livewire’s library automatically searches for a component named home within the directory app/http/livewire‌‌


If we now run our project with php artisan serve and access to http://localhost:8000 this is what we’ll see:‌‌

Laravel-livewire-1.png

If you can see this view, you’re on the right track! You can now move forward to the construction of our product catalog. ‌‌‌‌We’ll again create a Livewire component with this command:‌‌

php artisan make:livewire Products

The ‘Products’ component will have the following content:‌‌

<?php namespace App\Http\Livewire; use App\Product; use App\Facades\Cart; use Illuminate\View\View; use Livewire\Component; use Livewire\WithPagination; class Products extends Component { use WithPagination; public $search; protected $updatesQueryString = ['search']; public function mount(): void { $this->search = request()->query('search', $this->search); } public function render(): View { return view('livewire.products', [ 'products' => $this->search === null ? Product::paginate(12) : Product::where('name', 'like', '%' . $this->search . '%')->paginate(12) ]); } public function addToCart(int $productId): void { Cart::add(Product::where('id', $productId)->first()); } }

Here we will see functions with different objectives. The ‘mount’ function will always be executed before the ‘render function’. It’s where we should define the logic that involves the data that will be rendered later.‌‌

The ‘render’ function is in charge of rendering the blade file. It’s executed the first time the page is loaded and every time there’s an interaction with the page (whenever a user interacts with the different visual components).‌‌

We also have the ‘addToCart’ function, which can be called from the view. For example: On a button click.‌‌

The content of the view is:

<div> <div class="w-full flex justify-center"> <input wire:model="search" type="text" class="shadow appearance-none border rounded w-1/2 py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" placeholder="Search products by name..."> </div> <div class="w-full flex justify-center"> <div class="flex flex-col md:flex-wrap md:flex-row p-5"> @foreach ($products as $product) <div class="w-full md:w-1/2 lg:w-1/3 md:px-2 py-2"> <div class="bg-white rounded shadow p-5 h-full relative"> <h5 class="font-black uppercase text-2xl mb-4">  </h5> <h6 class="font-bold text-gray-700 text-xl mb-3">U$S </h6> <p class="text-gray-900 font-normal mb-12">  </p> <div class="flex justify-end mt-5 absolute w-full bottom-0 left-0 pb-5"> <button wire:click="addToCart()" class="block uppercase font-bold text-green-600 hover:text-green-500 mr-4"> Add to cart </button> </div> </div> </div> @endforeach </div> </div> <div class="w-full flex justify-center pb-6">  </div> </div>
resources/views/livewire/products.blade.php‌‌

As we can see in line 3, the ‘search’ attribute is used as the model in the input. This means that every time a change is made in the input, the component will be updated with the new value of the input. This ends up in the execution of the ‘render’ function once again.‌‌

We’ll also use the following layout for the product pagination.‌‌

@if ($paginator->lastPage() > 1) <ul > <li class=" inline border-t border-b border-l border-brand-light px-3 py-2 no-underline"> <a href=""><<</a> </li> @for ($i = 1; $i <= $paginator->lastPage(); $i++) <li class=" inline border-t border-b border-l border-brand-light px-3 py-2 no-underline"> <a href=""></a> </li> @endfor <li class=" inline border-t border-b border-r border-l border-brand-light px-3 py-2 no-underline"> <a href="" >>></a> </li> </ul> @endif
resources/views/layouts/pagination.blade.php

It’s time to define the route ‘/products’ to make our component work. To do this, we modify the file web.php.

<?php use Illuminate\Support\Facades\Route; Route::livewire('/', 'home')->name('home'); Route::livewire('/products', 'products')->name('products');
routes/web.php

After defining this, if we access to the route /products we’ll obtain the following result:‌‌

Laravel-livewire-2.png

At this point we can filter the products by their name, as well as add them to the shopping cart. ‌‌

The next step is to create the shopping cart interface. By doing that we’ll be able to see the products, remove them and finish the checkout process. Now execute the following command:‌‌

php artisan make:livewire Cart

Add the following content to the files app/Http/Livewire/Cart.php and resources/views/livewire/cart.blade.php‌‌

<?php namespace App\Http\Livewire; use App\Facades\Cart as CartFacade; use Livewire\Component; class Cart extends Component { public $cart; public function mount(): void { $this->cart = CartFacade::get(); } public function render() { return view('livewire.cart'); } public function removeFromCart($productId): void { CartFacade::remove($productId); $this->cart = CartFacade::get(); } public function checkout(): void { CartFacade::clear(); $this->cart = CartFacade::get(); } }
app/Http/Livewire/Cart.php

We’ll obtain the status of the shopping cart in the mount() function. This way we’ll be able to show the existing products in the view.‌‌

We should define the function ‘RemoveFromCart’, which will be in charge of removing a product (using its ID) from the shopping cart. It’s important to notice that this job is delegated to the facade.‌‌

There’s also a function called ‘checkout’ that makes the checkout and removes all the products from the shopping cart.‌‌

The view should look like this:‌                          

<div> <div class="w-2/3 mx-auto"> <div class="bg-white shadow-md rounded my-6"> @if(count($cart['products']) > 0) <table class="text-left w-full border-collapse"> <thead> <tr> <th class="py-4 px-6 bg-grey-lightest font-bold uppercase text-sm text-grey-dark border-b border-grey-light">Name</th> <th class="py-4 px-6 bg-grey-lightest font-bold uppercase text-sm text-grey-dark border-b border-grey-light">Price</th> <th class="py-4 px-6 bg-grey-lightest font-bold uppercase text-sm text-grey-dark border-b border-grey-light">Actions</th> </tr> </thead> <tbody> @foreach($cart['products'] as $product) <tr class="hover:bg-grey-lighter"> <td class="py-4 px-6 border-b border-grey-light"></td> <td class="py-4 px-6 border-b border-grey-light"></td> <td class="py-4 px-6 border-b border-grey-light"> <a wire:click="removeFromCart()" class="text-green-600 font-bold py-1 px-3 rounded text-xs bg-green hover:bg-green-dark cursor-pointer">Remove</a> </td> </tr> @endforeach </tbody> </table> <div class="text-right w-full p-6"> <button wire:click="checkout()" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"> Checkout </button> </div> @else <div class="text-center w-full border-collapse p-6"> <span class="text-lg">¡Your cart is empty!</span> </div> @endif </div> </div> </div>
resources/views/livewire/cart.blade.php

This is probably the interface in which we use the blade directives the most. We can verify the existence of products in the shopping cart in the line 4. If they do exist, the elements will be listed. If there are no existing elements, a message will be displayed saying that there are no products in the shopping cart.‌‌

Notice that each product is associated with a button. Every time the click event is executed, the function RemoveFromCart – defined in the component – is activated and will remove the element from the shopping cart.‌‌‌‌

What’s left? Updating the file web.php, which should now be seen this way:‌‌

<?php use App\Http\Controllers\HomeController; use App\Http\Controllers\ProductsController; use Illuminate\Support\Facades\Route; Route::livewire('/', 'home')->name('home'); Route::livewire('/products', 'products')->name('products'); Route::livewire('/cart', 'cart')->name('cart');
routes/web.php

Finally, if we add a product to the shopping cart and access a /cart, we should get to the following view:‌‌

Laravel-livewire-3.png

Activating the SPA mode‌‌

To activate this navigation mode, the first thing we should do is install the turbolinks dependency. This dependency automatically injects the received HTML without reloading the page. To install it, we use:‌‌ npm install --save turbolinks

After this, we must update the app.js file, which should contain this content:‌‌

require('./bootstrap'); var Turbolinks = require("turbolinks") Turbolinks.start()
resources/js/app.js

Finally, we execute npm run watch

If we head back to our project in the navigator, we’ll be able to see how it’s exchanged throughout the different pages, without reloading the site.

Product counter (optional)‌‌

We can also add a product counter in the shopping cart. For this we will use events and listeners of the Livewire components. The logic behind this functionality is to issue notifications every time a new product is added to the shopping cart and every time it is removed. We need a component to listen to those events and updates when it does.‌‌

To do this, we’ll need to modify our project a little bit, the first thing we will do is create a new header component‌‌

php artisan make:livewire Header

We modify the generated files in the following way:

<?php namespace App\Http\Livewire; use Illuminate\View\View; use Livewire\Component; use App\Facades\Cart; class Header extends Component { public $cartTotal = 0; protected $listeners = [ 'productAdded' => 'updateCartTotal', 'productRemoved' => 'updateCartTotal', 'clearCart' => 'updateCartTotal' ]; public function mount(): void { $this->cartTotal = count(Cart::get()['products']); } public function render(): View { return view('livewire.header'); } public function updateCartTotal(): void { $this->cartTotal = count(Cart::get()['products']); } }
app/Http/Livewire/Header.php
<div> <nav class="flex items-center justify-between flex-wrap p-6 mb-6 shadow"> <div class="block lg:hidden"> <button class="flex items-center px-3 py-2 border rounded text-teal-200 border-teal-400 hover:text-white hover:border-white"> <svg class="fill-current h-3 w-3" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>Menu</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"/></svg> </button> </div> <div class="w-full block flex-grow lg:flex lg:items-center lg:w-auto"> <div class="text-sm lg:flex-grow"> <a href="/" data-turbolinks-action="replace" class="block mt-4 lg:inline-block lg:mt-0 mr-4"> Home </a> <a href="/products" data-turbolinks-action="replace" class="block mt-4 lg:inline-block lg:mt-0 mr-4"> Products </a> <a href="/cart" data-turbolinks-action="replace" class="block mt-4 lg:inline-block lg:mt-0 mr-4"> Cart() </a> </div> </div> </nav> </div>
resources/views/header.blade.php

Now what’s left is modifying our layout, substituting the header with the new component header we just created.‌‌

<!DOCTYPE html> <html> <head> .. </head> <body> @livewire('header') // Before @include('layouts.header') .. <script src=""></script> </body> </html>
resources/views/layouts/app.blade.js

We don’t need the file we were including in the header anymore because we’re now using our component, so we can delete the file.‌‌

Now we must modify the function that adds and removes products from the shopping cart. We’ll modify app/Http/Livewire/Products.php y app/Http/Livewire/Cart.php respectively.‌‌

public function addToCart(int $productId): void { Cart::add(Product::where('id', $productId)->first()); $this->emit('productAdded'); }
app/Http/Livewire/Product.php
public function removeFromCart($productId): void { CartFacade::remove($productId); $this->cart = CartFacade::get(); $this->emit('productRemoved'); } public function checkout(): void { CartFacade::clear(); $this->emit('clearCart'); $this->cart = CartFacade::get(); }
app/Http/Livewire/Cart.php

Conclusions‌‌

One of Livewire’s best advantages is the possibility to construct user interfaces with PHP’s simplicity. It would be much complex to build the same thing using javascript and would require more learning and development time.‌‌

I guess that another amazing advantage is the possibility of using all the resources Laravel Core provides us in Livewire’s class components. This unifies both environments.‌‌

Livewire came here to stay, willing to set an ecosystem in which backend developers feel free to implement solutions with PHP in both, frontend and backend.‌‌

Repository: https://github.com/Light-it-labs/laravel-livewire-cart

Live Demo: https://livewire-shopping-cart.demo.lightit.io/products

programming

via Laravel News Links https://ift.tt/2dvygAJ

April 9, 2020 at 09:12AM

5 B2B Best Practices to Get More Website Leads

5 B2B Best Practices to Get More Website Leads

https://ift.tt/2y5zwHW

When it comes to B2B lead generation, what impacts the most?

Many B2B business houses tend to spend a lot of time, pouring water into leaky buckets. Rather than fixing the bucket i.e. the marketing funnel, they pour more water i.e. traffic into the bucket to keep it to the brim. This tendency leads to inflated acquisition costs and below-average results in terms of generating leads.

Landing pages are most crucial, particularly- the forms. Forms separate your leads from non-leads. Structures on your website indicate on your conversion rates and overall lead generation results.

Let’s say around 1000 people visit your landing page, maybe about 1% tends to convert. The fundamental reason is that most people tend to procrastinate, some feel that they will convert late, and some tend to find a different alternative. So what is to be done now? An urgency arises, in such a situation to turn at the moment. Here comes the main gameplay of optimizing your website for conversion. It is an ongoing process. There is always a next step you can take to bring in more leads and make that 1% go up the chart.

You must be feeling a dire need to optimize your website for lead generation. Questions might arise in your mind like how do I improve my landing page? What strategies should I follow? How to get more website leads?

Exclusively for you, we are covering website lead generation best practices that will help experienced business owners to take the next step in optimizing their website for lead generation and conversion, Let’s begin!

A website is one of the most potent tools most business owners possess when it comes to generating leads.

  • All potential new leads should be brought back to your website aka remarketing
  • When a person visits your website, there are several ways in which you can get them to convert i.e. (content marketing, email marketing, lead nurturing, Social media marketing)

Map Your Customer Journey and Website Flow

It is evident that when a person visits your website’s homepage, it needs more than some kind of action in order to convert and become a lead. If each step is not transparent, it becomes hard for visitors to convert.

A clear path needs to be set for visitors to follow through the website to convert from just leads to buyers. The more, the merrier is the motto. It means that the website must draw more traffic and every page of your website has to correspond to the ideal customer journey that you want visitors and leads to follow.

Every website and growth agency lead generation efforts start and end with that path, implying that every web page you create has to serve as a specific stepping stone toward conversion.

Every step should build a bridge towards the next step.

One of the ways to approach this outlook can be using Google Analytics Behavior Flow and filter for website visitors who become your best leads and best customers. It indicates which pages target audience visited along the way and help you identify where in the journey potential leads who did not convert fall off.

Google Analytics Behavior Flow

Build One Landing Page for One Persona (Buyer)

One of the biggest mistakes that most B2B business houses make with their website is that they use the same content to attract different segments of their customer base. If you create a landing page designed to convert all of your buyer personas, chances are, it will not be so effective since every buyer has a different opinion, mindset and preference.

It is therefore needed to create multiple landing pages –

one for each of your buyer personas

Let’s say you create a landing page exclusive that addresses the pain points of your audience, and another landing page for your visitors who visits due to interest, create one landing page for end-users focusing on your product and a separate landing page for their managers, so on and so forth.

Breaking out separate landing pages in that way, can get even more target audience and is effective to bring in “quality leads

Send a Consistent Message All Over Your Digital Presence

The message you are sending to your target audience, if inconsistent, creates a mismatch in their expectations. If your website does not do what it promises its target audience, then it leads to

  • High exit rate of the visitors
  • Lower conversion rate
  • Poor user experience

Issues like High exit rate, high bounce rates, and low session durations can also negative effect.

According to Google, “the experience you offer affects your Ad Rank and therefore your CPC and position in the ad auction.”

The solution to these problems is to ensure everything that leads visitors to a given page of your website sends the same message and sets the same expectations.

Example –

  • Your advertisement copy – paid social media ads, display PPC ads, Google Ads, etc.
  • Your SEO, meta title and meta description
  • Social media posts

Get Your Capture Form Right

If you want to convert website visitors into leads, then you need to have some kind of capture form included in your website design. That is “Basic Marketing 101“.

To generate better-qualified leads, you will need to take it a step further and optimize your capture form for not only your unique audience but also for your business.

The best method is to offer something your audience deems valuable in exchange for their information. Basic -” helps them solve a problem” like a 30-minute consultant call or a quick chat.

Balance the type and amount of information you gather (like phone numbers and other contact information) with that perceived value. The more value your visitors expect to get from you, the more information they will be willing to hand over.

Conversion optimization activities, optimizing your form’s Call To Action and submit buttons also falls under this best practice along with a combination of placement, web design, and A/B testing.

Use Retargeting to Capture (Who Didn’t Convert)

No matter how consistent and compelling your website might be, there will always be visitors who fit the persona of your best leads but still do not convert – the worst part of digital marketing. It does not imply that those potential customers disappear and they cannot be converted. This is where Remarketing “ makes its marks.

The biggest disadvantage of retargeting advertisements only target one individual, and most of the times that do not work for B2B businesses. You need to find tools that will allow you to target retargeting advertisements to an entire business/organisation. For this, it is recommended to use website visitor identification software along with some retargeting tools that focus on B2B.

Conclusion

Majority of website visits end without conversion, but there is always something more you can do to get more qualified companies to convert into leads. With the best practices above, you can take it to the next step and start generating more and more qualified website leads and increase your business profitability. Experiment with them to see exactly what works for you. Test the methods that convert the most, test the lead magnets that will convert best. Keep testing until you find what works best for your target audience and this helps you acquire more leads, make more sales. Tell us what do you feel?

via Noupe https://www.noupe.com

April 9, 2020 at 10:00AM

Orange Whip

Orange Whip

https://ift.tt/2xXzn9C

Orange Whip

Link

In the right hands, a whip can be an incredibly painful and precise weapon. But most of the whips we’ve seen are made from leather or paracord. This TikTok clip posted by masterlolik_yt shows how a heavy length of chain can be even more dangerous as a whip as it literally makes oranges explode on contact. More here.

fun

via The Awesomer https://theawesomer.com

April 8, 2020 at 02:45PM

Making Skateboard Lumber

Making Skateboard Lumber

https://ift.tt/3e3DzVT

Making Skateboard Lumber

Link

We’ve seen lots of nifty objects made from old skateboard decks, but what Woby Designs is showing off here is something different. By laminating together 20 wood decks, he was able to create a usable lumber with a colorful pattern running through its center. The prep work looks like the most time-consuming part.

fun

via The Awesomer https://theawesomer.com

April 8, 2020 at 12:30PM

Netflix now lets you lock your personal profile with a PIN to keep kids (and roommates) out

Netflix now lets you lock your personal profile with a PIN to keep kids (and roommates) out

https://ift.tt/34kLfPh

Want to let your kids poke around Netflix without them wandering their way beyond the kids section? Got a roommate who keeps inexplicably forgetting to use their profile and is totally screwing up your “Continue Watching” list?

Good news! Netflix is now letting users set a PIN to keep individual profiles locked down.

The new feature comes as part of a wider update this morning focusing on improved parental controls.

Other new features include:

  • Filtering titles based on their maturity rating in your country. Useful if you want someone to have access to more than just the kids section while still blocking off anything beyond, say, PG-13.
  • Disabling auto-play on a kid profile to make Octonaut marathons a bit more… intentional.
  • Blocking specific titles by name. Need a break from Boss Baby? Maybe add it to the list for a while.

It’s all pretty basic stuff… but with more people working from home with kids in tow right now, it’s a good time for all of it to land.

Looking for the new controls? Visit Netflix.com in a browser, make sure you’re toggled into a non-kid profile, tap the dropdown arrow in the upper right, hit “Account”, then look for the “Profile & Parental Controls” section — everything should be nested in there, with individual settings for each profile on your account.

technology

via TechCrunch https://techcrunch.com

April 7, 2020 at 04:06PM

Oil Platform Fly-through

Oil Platform Fly-through

https://ift.tt/34hcZEa

Oil Platform Fly-through

Link

With the help of Tac Gas, drone pilot NURK FPV had the rare opportunity to visit an abandoned gas and oil platform. There, he zigged and zagged his aerial camera through the rusted out facility, capturing some fascinating and truly unique visuals. The over-water footage had us hanging on for dear life.

fun

via The Awesomer https://theawesomer.com

April 7, 2020 at 12:55PM

Firedisc Propane Cooker

Firedisc Propane Cooker

https://ift.tt/2UOvtZC

Firedisc Propane Cooker

 | Buy

This portable cooker works differently from other propane grills. Rather than open grates, the Firedisc has a smooth surface that can cook just about anything you’d make in a pan or a griddle, from eggs to pancakes to meats, and you can even use it as a fryer. Its curved edges keep food warm, while its center cooks nice and hot.

fun

via The Awesomer https://theawesomer.com

April 7, 2020 at 10:15AM

500-year-old manuscript contains earliest known use of the “F-word”

500-year-old manuscript contains earliest known use of the “F-word”

https://ift.tt/2yGnDc3

"Your mother was a hamster and your father smelt of elderberries!" <em>Monty Python' and the Holy Grail</em>'s family-friendly approach to swearing handily avoids the F-word.
Enlarge /

“Your mother was a hamster and your father smelt of elderberries!”

Monty Python’ and the Holy Grail

‘s family-friendly approach to swearing handily avoids the F-word.

Scotland has much to recommend it: impressive architecture, gorgeous Highlands, and a long, distinguished intellectual tradition that has spawned some of the Western world’s greatest thinkers over several centuries. It’s also, apparently, home to a medieval manuscript that contains the earliest known usage of the swear word “F#$%.”

The profanity appears in a poem recorded by a bored student in Edinburgh while under lockdown as the plague ravaged Europe—something we can all relate to these days. The poem is getting renewed attention thanks to its inclusion in a forthcoming BBC Scotland documentary exploring the country’s long, proud tradition of swearing, Scotland—Contains Strong Language.

The Bannatyne Manuscript gets its name from a young 16th-century Edinburgh merchant named George Bannatyne, who compiled the roughly 400 poems while stuck at home in late 1568, as the plague ravaged his city. It’s an anthology of Scottish literature, particularly the texts of poems by some of the country’s greatest bards (known as makars) in the 15th and 16th centuries. According to a spokeswoman for the National Library of Scotland (where the manuscript is housed), “It has long been known that the manuscript contains some strong swearwords that are now common in everyday language, although at the time, they were very much used in good-natured jest.”

The five sections to the compilation are devoted to religious themes, moral or philosophical themes, love ballads, fables and allegories, and comedy, especially satire. The latter section is where one is most likely to encounter the swears, particularly in the poetry of William Dunbar and Walter Kennedy. Both poets feature in the poem where the notorious F-word appears: “The Flyting of Dunbar and Kennedie.”

Flyting is a poetic genre in Scotland—essentially a poetry slam or rap battle, in which participants exchange creative insults with as much verbal pyrotechnics (doubling and tripling of rhymes, lots of alliteration) as they can muster. (It’s a safe bet Shakespeare excelled at this art form.)

Dunbar and Kennedy supposedly faced off for a flyting in the court of James IV of Scotland around 1500, and their exchange was set down for posterity in Bannatyne’s manuscript. In the poem, Dunbar makes fun of Kennedy’s Highland dialect, for instance, as well as his personal appearance, and he suggests his opponent enjoys sexual intercourse with horses. Kennedy retaliates with attacks on Dunbar’s diminutive stature and lack of bowel control, suggesting his rival gets his inspiration from drinking “frogspawn” from the waters of a rural pond. You get the idea.

And then comes the historic moment: an insult containing the phrase “wan fukkit funling,” marking the earliest known surviving record of the F-word.

Of course, as the BBC Scotland documentary notes, that first “F#%$” is nothing compared to author James Kelman’s 1994 stream-of-consciousness novel, How Late It Was, How Late. The work is noteworthy not just because it won the Booker Prize and is written in a lowbrow Scottish dialect, but also because it uses the F-word over 4,000 times. Bannatyne would be proud of the legacy his first “F$#%” wrought.

Game of Thrones‘ Tyrion Lannister tries to negotiate with the insulting Frenchman from Monty Python and the Holy Grail in this mashup from Funny or Die.

geeky

via Ars Technica https://arstechnica.com

April 6, 2020 at 07:34PM

Disney Works From Home With Its At Home With Olaf Digital Series

Disney Works From Home With Its At Home With Olaf Digital Series

https://ift.tt/39OMSWz

You don’t gotta go to work work work work work work work…
Image: Disney Animation (Twitter)

Like the great Fifth Harmony once said: “You can work from home, oh oh, oh oh.” Disney Animation is keeping kids entertained remotely with a new digital series all about Frozen’s Olaf, created at home by animator Hyrum Osmond and voiced by Josh Gad.

As stated on Twitter, Disney Animation is releasing a new recurring digital series called At Home With Olaf. The episodes will spend time with Olaf as he gets into mischief, presumably while his human friends are social distancing. The first clip, which you can watch below, is all about Olaf having some fun with his snow buddies during a solo snowball fight.

More Olaf is not necessarily good Olaf—lest we forget the tragedy that was Olaf’s Frozen Adventure—but it’s great to see Disney working on some fun projects to help keep kids happy during social distancing. And hey, if it keeps animators working from home, all the better. This isn’t the only project Gad’s been working on during the covid-19 pandemic; he’s also been hosting regular storytime sessions online.

The first episode of At Home With Olaf is set to debut online later this week.


For more, make sure you’re following us on our Instagram @io9dotcom.

geeky,Tech

via Gizmodo https://gizmodo.com

April 6, 2020 at 10:48PM