Пошаговое руководство по созданию нумерации страниц в CakePHP 4

Разбивка на страницы — важная часть веб-разработки, которая позволяет пользователям легко перемещаться по большому списку данных. Он делит данные на небольшие фрагменты и отображает их в структурированном и управляемом виде.

В CakePHP 4 вам не нужно писать много кода для создания списка страниц. Для этого доступен метод paginate(), который выполняется из контроллера.

В этом руководстве я покажу, как создать разбиение на страницы в CakePHP 4 с данными базы данных MySQL.

Содержание

  1. Создать таблицу
  2. Конфигурация базы данных
  3. Создать модель
  4. Создать контроллер
  5. Создать шаблон
  6. Выход
  7. Заключение

1. Создать таблицу

В примере я использую usersтаблицы. Он имеет следующую структуру и данные:

CREATE TABLE `users` (
    `id` int(11) NOT NULL,
    `username` varchar(50) NOT NULL,
    `name` varchar(60) NOT NULL,
    `gender` varchar(10) NOT NULL,
    `email` varchar(60) NOT NULL,
    `city` varchar(80) NOT NULL
);

ALTER TABLE `users`
    ADD PRIMARY KEY (`id`);

ALTER TABLE `users`
    MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;

INSERT INTO `users` (`id`, `username`, `name`, `gender`, `email`, `city`) VALUES
(1, 'yssyogesh', 'Yogesh singh', 'male', 'yogesh@makitweb.com', 'Bhopal'),
(2, 'bsonarika', 'Sonarika Bhadoria', 'female', 'bsonarika@makitweb.com', 'Indore'),
(3, 'sunil', 'Sunil singh', 'male', 'sunil@makitweb.com', 'Pune'),
(4, 'vishal', 'Vishal Sahu', 'male', 'vishal@makitweb.com', 'Bhopal'),
(5, 'jiten', 'jitendra singh', 'male', 'jitendra@makitweb.com', 'Delhi'),
(6, 'shreya', 'Shreya joshi', 'female', 'shreya@makitweb.com', 'Indore'),
(7, 'abhilash', 'Abhilash namdev', 'male', 'abhilash@makitweb.com', 'Pune'),
(8, 'ekta', 'Ekta patidar', 'female', 'ekta@makitweb.com', 'Bhopal'),
(9, 'deepak', 'Deepak singh', 'male', 'deepak@makitweb.com', 'Delhi'),
(10, 'rohit', 'Rohit Kumar', 'male', 'rohit@makitweb.com', 'Bhopal'),
(11, 'bhavna', 'Bhavna Mahajan', 'female', 'bhavna@makitweb.com', 'Indore'),
(12, 'ajay', 'Ajay singh', 'male', 'ajay@makitweb.com', 'Delhi'),
(13, 'mohit', 'Mohit', 'male', 'mohit@makitweb.com', 'Pune'),
(14, 'akhilesh', 'Akhilesh Sahu', 'male', 'akhilesh@makitweb.com', 'Indore'),
(15, 'ganesh', 'Ganesh', 'male', 'ganesh@makitweb.com', 'Pune'),
(16, 'vijay', 'Vijay', 'male', 'vijay@makitweb.com', 'Delhi');

2. Конфигурация базы данных

  • Откройте config/app_local.phpфайл.
  • Укажите детали конфигурации вашей базы данных в файле Datasources default.
'Datasources' => [
     'default' => [
          'host' => '127.0.0.1',
          /*
          * CakePHP will use the default DB port based on the driver selected
          * MySQL on MAMP uses port 8889, MAMP users will want to uncomment
          * the following line and set the port accordingly
          */
          //'port' => 'non_standard_port_number',

          'username' => 'root',
          'password' => 'root',

          'database' => 'cakephp4',
          /*
          * If not using the default 'public' schema with the PostgreSQL driver
          * set it here.
          */
          //'schema' => 'myapp',

          /*
          * You can use a DSN string to set the entire configuration
          */
          'url' => env('DATABASE_URL', null),
     ],

     /*
     * The test connection is used during the test suite.
     */
     'test' => [
          'host' => 'localhost',
          //'port' => 'non_standard_port_number',
          'username' => 'my_app',
          'password' => 'secret',
          'database' => 'test_myapp',
          //'schema' => 'myapp',
          'url' => env('DATABASE_TEST_URL', 'sqlite://127.0.0.1/tests.sqlite'),
     ],
],

3. Создать модель

  • Создать  Users модель.
bin/cake bake model Users
  • Это создаст 2 файла —
src/Model/Entity/User.php
src/Model/Table/UsersTable.php

Источник/Модель/Сущность/User.php

В этом классе Entity укажите имена полей, которые вы хотите разрешить вставлять и обновлять. Вы можете либо удалить имя поля, либо установить его,  false если не хотите разрешать.

Завершенный код

<?php
declare(strict_types=1);

namespace App\Model\Entity;

use Cake\ORM\Entity;

class Users extends Entity
{

      protected $_accessible = [
           'username' => true,
           'name' => true,
           'age' => true,
           'gender' => true,
           'email' => true,
           'city' => true,
      ];
}

источник/Модель/Таблица/UsersTable.php

Этот класс Table сообщает ORM, какую таблицу необходимо использовать, и определяет правила проверки для полей.

Завершенный код

<?php
declare(strict_types=1);

namespace App\Model\Table;

use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

class UsersTable extends Table
{

      public function initialize(array $config): void
      {
            parent::initialize($config);

            $this->setTable('users');
            $this->setDisplayField('name');
            $this->setPrimaryKey('id');
      }

      public function validationDefault(Validator $validator): Validator
      {
            $validator
            ->scalar('username')
            ->maxLength('username', 50)
            ->requirePresence('username', 'create')
            ->notEmptyString('username');

            $validator
            ->scalar('name')
            ->maxLength('name', 60)
            ->requirePresence('name', 'create')
            ->notEmptyString('name');

            $validator
            ->integer('age')
            ->requirePresence('age', 'create')
            ->notEmptyString('age');
 
            $validator
            ->scalar('gender')
            ->maxLength('gender', 10)
            ->requirePresence('gender', 'create')
            ->notEmptyString('gender');

            $validator
            ->email('email')
            ->requirePresence('email', 'create')
            ->notEmptyString('email');

            $validator
            ->scalar('city')
            ->maxLength('city', 80)
            ->requirePresence('city', 'create')
            ->notEmptyString('city');

            return $validator;
      }

      public function buildRules(RulesChecker $rules): RulesChecker
      {
            $rules->add($rules->isUnique(['username']), ['errorField' => 'username']);

            return $rules;
      }
}

4. Создайте контроллер

  • Создайте  UsersController.php файл в  src/Controller/ папке.
  • Создайте  UsersController класс, который расширяет  AppController.

Создать 1 метод –

  • index() — создать объект  Users таблицы с помощью —  $this->getTableLocator()->get('Users').

Я разбиваю все usersзаписи таблицы. Если вы хотите разбить определенные записи на страницы, вы можете установить предложение where, используя $query$query->where();.

В $settingsмассиве установите ограничение и порядок по умолчанию по имени столбца. Я устанавливаю ограничение в 5 и упорядочиваю по nameполям по ascпорядку.

Звонок $this->paginate()для создания пагинации. Вот $queryи проходите $settings.

Использование $this->set()перехода $usersк шаблону.

Завершенный код

<?php
declare(strict_types=1);

namespace App\Controller;

class UsersController extends AppController
{

     public function index(){

          $UsersTable = $this->getTableLocator()->get('Users');

          // Paginate the ORM table.
          $query = $UsersTable->find('all');

          // Settings
          $settings = [
               'limit'=> '5',
               'order' => ['Users.name' => 'asc']
          ];

          $users = $this->paginate($query,$settings );

          $this->set(compact('users'));

     }

}

5. Создать шаблон

Создать Usersпапку в templates/месте. В Users папке создайте index.phpфайл – templates/Users/index.php.

Создать макет страницы —

Создать <table >.

  • Сортировка –

Добавьте столбцы заголовков и с помощью Paginatorпомощника разрешите сортировку по имени столбца заголовка.

<?= $this->Paginator->sort('id', 'ID') ?>

Здесь первый параметр — это имя столбца сортировки, а второй параметр — заголовок столбца.

Например, я не установил сортировку в столбце электронной почты.

  • Список данных с разбивкой на страницы –

Цикл по, $usersесли он не пуст. Прочитайте значения и добавьте новые <tr> <td>.

  • Ссылки навигации по страницам –

Снова используйте Paginatorпомощник для добавления навигационных ссылок. Я добавляю списки «Предыдущий», «Следующий» и «Номер».

Завершенный код

<div class="row">
     <div class="col-6">

          <!-- User paginated list -->
          <table >
              <thead>
                  <tr>
                      <th><?= $this->Paginator->sort('id', 'ID') ?></th>
                      <th><?= $this->Paginator->sort('username', 'Username') ?></th>
                      <th><?= $this->Paginator->sort('id', 'Name') ?></th>
                      <th><?= $this->Paginator->sort('id', 'Gender') ?></th>
                      <th><?= $this->Paginator->sort('id', 'Age') ?></th>
                      <th>Email</th>
                      <th><?= $this->Paginator->sort('id', 'City') ?></th>
                  </tr>
              </thead>
              <tbody>
                   <?php if(count($users) > 0):?>
                        <?php foreach ($users as $user): ?>
                            <tr>
                                <td><?= $user['id'] ?></td>
                                <td><?= $user['username'] ?></td>
                                <td><?= $user['name'] ?></td>
                                <td><?= $user['gender'] ?></td>
                                <td><?= $user['age'] ?></td>
                                <td><?= $user['email'] ?></td>
                                <td><?= $user['city'] ?></td>
                            </tr>
                        <?php endforeach; ?>
                   <?php else: ?>
                        <tr>
                            <td colspan="7">No record found.</td>
                        </tr>
                   <?php endif; ?> 
              </tbody>
          </table>

          <!-- Pagination links -->
          <ul class="pagination">
              <?= $this->Paginator->prev("<<") ?>
              <?= $this->Paginator->numbers() ?>
              <?= $this->Paginator->next(">>") ?>
          </ul>
     </div>

</div>

6. Выход

Просмотр вывода


7. Заключение

Вызовите paginate()метод в контроллере, чтобы создать разбивку ваших данных на страницы. Отобразите его в шаблоне, зациклив сгенерированные данные и используя Paginatorпомощник.

Просмотрите этот официальный документ по созданию разбивки на страницы, если хотите узнать об этом больше.

Вы также можете ознакомиться с другими руководствами по CakePHP 4 на сайте.

Оригинальный источник статьи:   https://makitweb.com/

#cakephp #php #step

Пошаговое руководство по созданию нумерации страниц в CakePHP 4

如何在 CakePHP 4 中创建分页的分步教程

分页是 Web 开发的重要组成部分,它允许用户轻松浏览大量数据列表。它将数据分成小块,并以结构化和可管理的方式显示它们。

在 CakePHP 4 中,您不需要编写大量代码来创建分页列表。为此,可以使用从控制器执行的 paginate() 方法。

在本教程中,我展示了如何使用 MySQL 数据库数据在 CakePHP 4 中创建分页。

内容

  1. 创建表
  2. 数据库配置
  3. 创建模型
  4. 创建控制器
  5. 创建模板
  6. 输出
  7. 结论

1.创建表

在示例中,我使用的是users表格。它具有以下结构和数据 -

CREATE TABLE `users` (
    `id` int(11) NOT NULL,
    `username` varchar(50) NOT NULL,
    `name` varchar(60) NOT NULL,
    `gender` varchar(10) NOT NULL,
    `email` varchar(60) NOT NULL,
    `city` varchar(80) NOT NULL
);

ALTER TABLE `users`
    ADD PRIMARY KEY (`id`);

ALTER TABLE `users`
    MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;

INSERT INTO `users` (`id`, `username`, `name`, `gender`, `email`, `city`) VALUES
(1, 'yssyogesh', 'Yogesh singh', 'male', 'yogesh@makitweb.com', 'Bhopal'),
(2, 'bsonarika', 'Sonarika Bhadoria', 'female', 'bsonarika@makitweb.com', 'Indore'),
(3, 'sunil', 'Sunil singh', 'male', 'sunil@makitweb.com', 'Pune'),
(4, 'vishal', 'Vishal Sahu', 'male', 'vishal@makitweb.com', 'Bhopal'),
(5, 'jiten', 'jitendra singh', 'male', 'jitendra@makitweb.com', 'Delhi'),
(6, 'shreya', 'Shreya joshi', 'female', 'shreya@makitweb.com', 'Indore'),
(7, 'abhilash', 'Abhilash namdev', 'male', 'abhilash@makitweb.com', 'Pune'),
(8, 'ekta', 'Ekta patidar', 'female', 'ekta@makitweb.com', 'Bhopal'),
(9, 'deepak', 'Deepak singh', 'male', 'deepak@makitweb.com', 'Delhi'),
(10, 'rohit', 'Rohit Kumar', 'male', 'rohit@makitweb.com', 'Bhopal'),
(11, 'bhavna', 'Bhavna Mahajan', 'female', 'bhavna@makitweb.com', 'Indore'),
(12, 'ajay', 'Ajay singh', 'male', 'ajay@makitweb.com', 'Delhi'),
(13, 'mohit', 'Mohit', 'male', 'mohit@makitweb.com', 'Pune'),
(14, 'akhilesh', 'Akhilesh Sahu', 'male', 'akhilesh@makitweb.com', 'Indore'),
(15, 'ganesh', 'Ganesh', 'male', 'ganesh@makitweb.com', 'Pune'),
(16, 'vijay', 'Vijay', 'male', 'vijay@makitweb.com', 'Delhi');

2.数据库配置

  • 打开config/app_local.php文件。
  • 在 中指定您的数据库配置详细信息Datasources default
'Datasources' => [
     'default' => [
          'host' => '127.0.0.1',
          /*
          * CakePHP will use the default DB port based on the driver selected
          * MySQL on MAMP uses port 8889, MAMP users will want to uncomment
          * the following line and set the port accordingly
          */
          //'port' => 'non_standard_port_number',

          'username' => 'root',
          'password' => 'root',

          'database' => 'cakephp4',
          /*
          * If not using the default 'public' schema with the PostgreSQL driver
          * set it here.
          */
          //'schema' => 'myapp',

          /*
          * You can use a DSN string to set the entire configuration
          */
          'url' => env('DATABASE_URL', null),
     ],

     /*
     * The test connection is used during the test suite.
     */
     'test' => [
          'host' => 'localhost',
          //'port' => 'non_standard_port_number',
          'username' => 'my_app',
          'password' => 'secret',
          'database' => 'test_myapp',
          //'schema' => 'myapp',
          'url' => env('DATABASE_TEST_URL', 'sqlite://127.0.0.1/tests.sqlite'),
     ],
],

3.创建模型

  • 创建 Users 模型。
bin/cake bake model Users
  • 这将创建 2 个文件 -
src/Model/Entity/User.php
src/Model/Table/UsersTable.php

源代码/模型/实体/User.php

在此实体类中指定要允许插入和更新的字段名称。false 如果您不想允许,您可以删除字段名称或将其设置为 。

完成代码

<?php
declare(strict_types=1);

namespace App\Model\Entity;

use Cake\ORM\Entity;

class Users extends Entity
{

      protected $_accessible = [
           'username' => true,
           'name' => true,
           'age' => true,
           'gender' => true,
           'email' => true,
           'city' => true,
      ];
}

src/模型/表/UsersTable.php

这个 Table 类告诉 ORM 需要使用哪个表并定义字段的验证规则。

完成代码

<?php
declare(strict_types=1);

namespace App\Model\Table;

use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

class UsersTable extends Table
{

      public function initialize(array $config): void
      {
            parent::initialize($config);

            $this->setTable('users');
            $this->setDisplayField('name');
            $this->setPrimaryKey('id');
      }

      public function validationDefault(Validator $validator): Validator
      {
            $validator
            ->scalar('username')
            ->maxLength('username', 50)
            ->requirePresence('username', 'create')
            ->notEmptyString('username');

            $validator
            ->scalar('name')
            ->maxLength('name', 60)
            ->requirePresence('name', 'create')
            ->notEmptyString('name');

            $validator
            ->integer('age')
            ->requirePresence('age', 'create')
            ->notEmptyString('age');
 
            $validator
            ->scalar('gender')
            ->maxLength('gender', 10)
            ->requirePresence('gender', 'create')
            ->notEmptyString('gender');

            $validator
            ->email('email')
            ->requirePresence('email', 'create')
            ->notEmptyString('email');

            $validator
            ->scalar('city')
            ->maxLength('city', 80)
            ->requirePresence('city', 'create')
            ->notEmptyString('city');

            return $validator;
      }

      public function buildRules(RulesChecker $rules): RulesChecker
      {
            $rules->add($rules->isUnique(['username']), ['errorField' => 'username']);

            return $rules;
      }
}

4.创建控制器

  • 在文件夹中创建一个 UsersController.php 文件 src/Controller/ 。
  • 创建 UsersController 扩展 AppController.

创建 1 方法 –

  • index() –Users 使用 – 创建 Table 的对象 $this->getTableLocator()->get('Users')

我正在对所有users表记录进行分页。如果要对特定记录进行分页,则可以使用$query–设置 where 子句$query->where();

$settings数组中按列名设置限制和默认顺序。我将限制设置为 5 并按name字段asc排序。

调用$this->paginate()创建分页。在这里,通过$query$settings

使用$this->set()传递$users给模板。

完成代码

<?php
declare(strict_types=1);

namespace App\Controller;

class UsersController extends AppController
{

     public function index(){

          $UsersTable = $this->getTableLocator()->get('Users');

          // Paginate the ORM table.
          $query = $UsersTable->find('all');

          // Settings
          $settings = [
               'limit'=> '5',
               'order' => ['Users.name' => 'asc']
          ];

          $users = $this->paginate($query,$settings );

          $this->set(compact('users'));

     }

}

5. 创建模板

Users在位置创建文件夹templates/。在Users 文件夹中创建index.php文件 – templates/Users/index.php.

创建分页布局 –

创建<table >

  • 排序 –

添加标题列并使用Paginator帮助程序允许对标题列名称进行排序单击 –

<?= $this->Paginator->sort('id', 'ID') ?>

这里,第一个参数是排序列名,第二个参数是列标题。

例如,我没有在电子邮件列上设置排序。

  • 列出分页数据 –

$users如果它不为空,则循环。读取值并添加新的<tr> <td>.

  • 分页导航链接 –

再次使用Paginatorhelper 添加导航链接。我正在添加上一个、下一个和编号列表。

完成代码

<div class="row">
     <div class="col-6">

          <!-- User paginated list -->
          <table >
              <thead>
                  <tr>
                      <th><?= $this->Paginator->sort('id', 'ID') ?></th>
                      <th><?= $this->Paginator->sort('username', 'Username') ?></th>
                      <th><?= $this->Paginator->sort('id', 'Name') ?></th>
                      <th><?= $this->Paginator->sort('id', 'Gender') ?></th>
                      <th><?= $this->Paginator->sort('id', 'Age') ?></th>
                      <th>Email</th>
                      <th><?= $this->Paginator->sort('id', 'City') ?></th>
                  </tr>
              </thead>
              <tbody>
                   <?php if(count($users) > 0):?>
                        <?php foreach ($users as $user): ?>
                            <tr>
                                <td><?= $user['id'] ?></td>
                                <td><?= $user['username'] ?></td>
                                <td><?= $user['name'] ?></td>
                                <td><?= $user['gender'] ?></td>
                                <td><?= $user['age'] ?></td>
                                <td><?= $user['email'] ?></td>
                                <td><?= $user['city'] ?></td>
                            </tr>
                        <?php endforeach; ?>
                   <?php else: ?>
                        <tr>
                            <td colspan="7">No record found.</td>
                        </tr>
                   <?php endif; ?> 
              </tbody>
          </table>

          <!-- Pagination links -->
          <ul class="pagination">
              <?= $this->Paginator->prev("<<") ?>
              <?= $this->Paginator->numbers() ?>
              <?= $this->Paginator->next(">>") ?>
          </ul>
     </div>

</div>

6.输出

查看输出


七、结论

调用paginate()控制器中的方法来创建数据的分页。通过循环生成的数据并使用Paginator帮助程序将其显示在模板中。

如果您想了解更多信息,请查看此有关分页创建的官方文档。

您还可以在站点上查看更多CakePHP 4 教程。

文章原文出处:https:   //makitweb.com/

#cakephp #php #step

如何在 CakePHP 4 中创建分页的分步教程
Oral  Brekke

Oral Brekke

1678802400

How to Step-by-Step Tutorial on Creating Pagination in CakePHP 4

Pagination is an essential part of web development that allows users to navigate through a large list of data easily. It divides the data into small chunks and displays them in a structured and manageable way.

In CakePHP 4 you don’t need to write lots of code to create a pagination list. For this paginate() method is available that executes from the controller.

In this tutorial, I show how you can create a pagination in CakePHP 4 with MySQL database data.

Contents

  1. Create Table
  2. Database Configuration
  3. Create Model
  4. Create Controller
  5. Create Template
  6. Output
  7. Conclusion

1. Create Table

In the example, I am using users Tables. It has the following structure and data –

CREATE TABLE `users` (
    `id` int(11) NOT NULL,
    `username` varchar(50) NOT NULL,
    `name` varchar(60) NOT NULL,
    `gender` varchar(10) NOT NULL,
    `email` varchar(60) NOT NULL,
    `city` varchar(80) NOT NULL
);

ALTER TABLE `users`
    ADD PRIMARY KEY (`id`);

ALTER TABLE `users`
    MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;

INSERT INTO `users` (`id`, `username`, `name`, `gender`, `email`, `city`) VALUES
(1, 'yssyogesh', 'Yogesh singh', 'male', 'yogesh@makitweb.com', 'Bhopal'),
(2, 'bsonarika', 'Sonarika Bhadoria', 'female', 'bsonarika@makitweb.com', 'Indore'),
(3, 'sunil', 'Sunil singh', 'male', 'sunil@makitweb.com', 'Pune'),
(4, 'vishal', 'Vishal Sahu', 'male', 'vishal@makitweb.com', 'Bhopal'),
(5, 'jiten', 'jitendra singh', 'male', 'jitendra@makitweb.com', 'Delhi'),
(6, 'shreya', 'Shreya joshi', 'female', 'shreya@makitweb.com', 'Indore'),
(7, 'abhilash', 'Abhilash namdev', 'male', 'abhilash@makitweb.com', 'Pune'),
(8, 'ekta', 'Ekta patidar', 'female', 'ekta@makitweb.com', 'Bhopal'),
(9, 'deepak', 'Deepak singh', 'male', 'deepak@makitweb.com', 'Delhi'),
(10, 'rohit', 'Rohit Kumar', 'male', 'rohit@makitweb.com', 'Bhopal'),
(11, 'bhavna', 'Bhavna Mahajan', 'female', 'bhavna@makitweb.com', 'Indore'),
(12, 'ajay', 'Ajay singh', 'male', 'ajay@makitweb.com', 'Delhi'),
(13, 'mohit', 'Mohit', 'male', 'mohit@makitweb.com', 'Pune'),
(14, 'akhilesh', 'Akhilesh Sahu', 'male', 'akhilesh@makitweb.com', 'Indore'),
(15, 'ganesh', 'Ganesh', 'male', 'ganesh@makitweb.com', 'Pune'),
(16, 'vijay', 'Vijay', 'male', 'vijay@makitweb.com', 'Delhi');

2. Database Configuration

  • Open config/app_local.php file.
  • Specify your database configuration details in the Datasources default.
'Datasources' => [
     'default' => [
          'host' => '127.0.0.1',
          /*
          * CakePHP will use the default DB port based on the driver selected
          * MySQL on MAMP uses port 8889, MAMP users will want to uncomment
          * the following line and set the port accordingly
          */
          //'port' => 'non_standard_port_number',

          'username' => 'root',
          'password' => 'root',

          'database' => 'cakephp4',
          /*
          * If not using the default 'public' schema with the PostgreSQL driver
          * set it here.
          */
          //'schema' => 'myapp',

          /*
          * You can use a DSN string to set the entire configuration
          */
          'url' => env('DATABASE_URL', null),
     ],

     /*
     * The test connection is used during the test suite.
     */
     'test' => [
          'host' => 'localhost',
          //'port' => 'non_standard_port_number',
          'username' => 'my_app',
          'password' => 'secret',
          'database' => 'test_myapp',
          //'schema' => 'myapp',
          'url' => env('DATABASE_TEST_URL', 'sqlite://127.0.0.1/tests.sqlite'),
     ],
],

3. Create Model

  • Create Users Model.
bin/cake bake model Users
  • This will create 2 files –
src/Model/Entity/User.php
src/Model/Table/UsersTable.php

src/Model/Entity/User.php

In this Entity class specify field names that you want to allow insertion and updation. You can either remove the field name or set it to false if you don’t want to allow.

Completed Code

<?php
declare(strict_types=1);

namespace App\Model\Entity;

use Cake\ORM\Entity;

class Users extends Entity
{

      protected $_accessible = [
           'username' => true,
           'name' => true,
           'age' => true,
           'gender' => true,
           'email' => true,
           'city' => true,
      ];
}

src/Model/Table/UsersTable.php

This Table class tells ORM which table needs to use and defines validation rules for the fields.

Completed Code

<?php
declare(strict_types=1);

namespace App\Model\Table;

use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

class UsersTable extends Table
{

      public function initialize(array $config): void
      {
            parent::initialize($config);

            $this->setTable('users');
            $this->setDisplayField('name');
            $this->setPrimaryKey('id');
      }

      public function validationDefault(Validator $validator): Validator
      {
            $validator
            ->scalar('username')
            ->maxLength('username', 50)
            ->requirePresence('username', 'create')
            ->notEmptyString('username');

            $validator
            ->scalar('name')
            ->maxLength('name', 60)
            ->requirePresence('name', 'create')
            ->notEmptyString('name');

            $validator
            ->integer('age')
            ->requirePresence('age', 'create')
            ->notEmptyString('age');
 
            $validator
            ->scalar('gender')
            ->maxLength('gender', 10)
            ->requirePresence('gender', 'create')
            ->notEmptyString('gender');

            $validator
            ->email('email')
            ->requirePresence('email', 'create')
            ->notEmptyString('email');

            $validator
            ->scalar('city')
            ->maxLength('city', 80)
            ->requirePresence('city', 'create')
            ->notEmptyString('city');

            return $validator;
      }

      public function buildRules(RulesChecker $rules): RulesChecker
      {
            $rules->add($rules->isUnique(['username']), ['errorField' => 'username']);

            return $rules;
      }
}

4. Create Controller

  • Create a UsersController.php file in src/Controller/ folder.
  • Create UsersController Class that extends AppController.

Create 1 method –

  • index() – Create an object of Users Table using – $this->getTableLocator()->get('Users').

I am paginating all users table records. If you want to paginate specific records then you can set where clause using $query$query->where();.

In the $settings Array set limit and default order by column name. I set a limit of 5 and order by name field in asc order.

Call $this->paginate() to create pagination. Here, pass $query and $settings.

Using $this->set() pass $users to the template.

Completed Code

<?php
declare(strict_types=1);

namespace App\Controller;

class UsersController extends AppController
{

     public function index(){

          $UsersTable = $this->getTableLocator()->get('Users');

          // Paginate the ORM table.
          $query = $UsersTable->find('all');

          // Settings
          $settings = [
               'limit'=> '5',
               'order' => ['Users.name' => 'asc']
          ];

          $users = $this->paginate($query,$settings );

          $this->set(compact('users'));

     }

}

5. Create Template

Create Users folder in templates/ location. In the Users folder create index.php file – templates/Users/index.php.

Create Pagination layout –

Create <table >.

  • Sorting –

Add header columns and with the use of Paginator helper allow sorting on the header column name click –

<?= $this->Paginator->sort('id', 'ID') ?>

Here, the first parameter is the sorting column name and 2nd parameter is the Column title.

For example purpose, I have not set sorting on the Email column.

  • List paginated data –

Loop on the $users if it is not empty. Read values and add new <tr> <td>.

  • Pagination Navigation links –

Again use Paginator helper for adding navigation links. I am adding the Previous, next, and number lists.

Completed Code

<div class="row">
     <div class="col-6">

          <!-- User paginated list -->
          <table >
              <thead>
                  <tr>
                      <th><?= $this->Paginator->sort('id', 'ID') ?></th>
                      <th><?= $this->Paginator->sort('username', 'Username') ?></th>
                      <th><?= $this->Paginator->sort('id', 'Name') ?></th>
                      <th><?= $this->Paginator->sort('id', 'Gender') ?></th>
                      <th><?= $this->Paginator->sort('id', 'Age') ?></th>
                      <th>Email</th>
                      <th><?= $this->Paginator->sort('id', 'City') ?></th>
                  </tr>
              </thead>
              <tbody>
                   <?php if(count($users) > 0):?>
                        <?php foreach ($users as $user): ?>
                            <tr>
                                <td><?= $user['id'] ?></td>
                                <td><?= $user['username'] ?></td>
                                <td><?= $user['name'] ?></td>
                                <td><?= $user['gender'] ?></td>
                                <td><?= $user['age'] ?></td>
                                <td><?= $user['email'] ?></td>
                                <td><?= $user['city'] ?></td>
                            </tr>
                        <?php endforeach; ?>
                   <?php else: ?>
                        <tr>
                            <td colspan="7">No record found.</td>
                        </tr>
                   <?php endif; ?> 
              </tbody>
          </table>

          <!-- Pagination links -->
          <ul class="pagination">
              <?= $this->Paginator->prev("<<") ?>
              <?= $this->Paginator->numbers() ?>
              <?= $this->Paginator->next(">>") ?>
          </ul>
     </div>

</div>

6. Output

View Output


7. Conclusion

Call the paginate() method in the controller to create the pagination of your data. Display it in the template by looping on the generated data and using the Paginator helper.

View this official document for pagination creation if you want to learn more about it.

You can also check out more CakePHP 4 tutorials on the site.

Original article source at:  https://makitweb.com/

#cakephp #php #step

How to Step-by-Step Tutorial on Creating Pagination in CakePHP 4

How to Laravel 10 CRUD Operation Step By Step

How to Laravel 10 CRUD Operation Step By Step

In this article, we will see how to create a crud operation in laravel 10. Here, we will learn about laravel 10 crud operation step by step. Day to day laravel improves secure, modern, and scalable applications. Also, it creates powerful and reusable codes and functionalities in the laravel framework.

So, here we will see how to create a CRUD operation in laravel 10, In this example, we have performed create, read, update, and delete.

So, let's see laravel 10 crud operation step by step, how to create crud operation in laravel 10, crud operations in laravel 10, laravel 10 crud operation, and laravel 10 crud step by step.

Step 1: Install Laravel 10 Application

Here, we will create a new project using the below command for CRUD operation in Laravel 10.

composer create-project --prefer-dist laravel/laravel blog

Step 2: Database Configuration

Now, we will set up database configuration like database name, username, password, etc for our crud application of laravel 10. So, copy the below code in your .env file and paste the details below.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_10_crud
DB_USERNAME=root
DB_PASSWORD=root

Step 3: Create a Migration

Here, we will create migration for the "posts" table using Laravel 10 php artisan command.

php artisan make:migration create_posts_table --create=posts

Migration:

<?php 

use Illuminate\Support\Facades\Schema; 
use Illuminate\Database\Schema\Blueprint; 
use Illuminate\Database\Migrations\Migration; 

class CreatePostsTable extends Migration { 

/** 
* Run the migrations. 
* 
* @return void */ 

public function up() 
{ 
    Schema::create('posts', function (Blueprint $table) { 
            $table->bigIncrements('id');
            $table->string('name')->nullable();
            $table->longText('detail')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
}

Now run migration using the below command.

php artisan migrate

Step 4: Add Resource Route

Now, Add the Resource route in the web.php file.

routes/web.php

<?php
  
use Illuminate\Support\Facades\Route;
  
use App\Http\Controllers\PostController;
  
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group that
| contains the "web" middleware group. Now create something great!
|
*/
  
Route::resource('posts', PostController::class);

Step 5: Add Controller and Model

In this step, we will create the PostController using the following command. Using the below command, you can create an index(), create(), store(), edit(), update() and destroy().

php artisan make:controller PostController --resource --model=Post

Now make changes in the Post.php Model. 

App/Models/Post.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasFactory;

    protected $fillable = [
        'id', 'name', 'detail'
    ];
}

After changes in the model, we need to add code in PostController.php

app/Http/Controllers/PostController.php

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $posts = Post::latest()->paginate(10);
        return view('post.index',compact('posts'))->with('i', (request()->input('page', 1) - 1) * 10);
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        return view('post.create');
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $request->validate([
            'name' => 'required',
            'detail' => 'required',
        ]);
  
        Post::create($request->all());
   
        return redirect()->route('posts.index')->with('success','Post created successfully.');
    }

    /**
     * Display the specified resource.
     *
     * @param  \App\Models\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function show(Post $post)
    {
        return view('post.show',compact('post'));
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  \App\Models\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function edit(Post $post)
    {
        return view('post.edit',compact('post'));
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\Models\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, Post $post)
    {
        $request->validate([
            'name' => 'required',
            'detail' => 'required',
        ]);
  
        $post->update($request->all());
  
        return redirect()->route('posts.index')->with('success','Post updated successfully');
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  \App\Models\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function destroy(Post $post)
    {
        $post->delete();
  
        return redirect()->route('posts.index')->with('success','Post deleted successfully');
    }
}

Step 6: Add Blade Files

Now, create blade files for layout and post file. So, create new folder "posts" and then create blade files of the CRUD example in laravel 10.

1. layout.blade.php

2. index.blade.php

3. create.blade.php

4. edit.blade.php

5. show.blade.php

resources/views/posts/layout.blade.php

<!DOCTYPE html>
<html>
<head>
    <title>Laravel 10 CRUD Operation Step By Step - Websolutionstuff</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    
<div class="container">
    @yield('content')
</div>
    
</body>
</html>

resources/views/posts/index.blade.php

@extends('post.layout')
 
@section('content')
    <div class="row">
        <div class="col-lg-12 margin-tb">
            <div class="pull-left">
                <h2>Laravel 10 CRUD Operation Step By Step - Websolutionstuff</h2>
            </div>
            <div class="pull-right">
                <a class="btn btn-success" href="{{ route('posts.create') }}"> Create New Post</a>
            </div>
        </div>
    </div>
   
    @if ($message = Session::get('success'))
        <div class="alert alert-success">
            <p>{{ $message }}</p>
        </div>
    @endif
   
    <table class="table table-bordered">
        <tr>
            <th>No</th>
            <th>Name</th>
            <th>Details</th>
            <th width="280px">Action</th>
        </tr>
        @foreach ($posts as $post)
        <tr>
            <td>{{ ++$i }}</td>
            <td>{{ $post->name }}</td>
            <td>{{ $post->detail }}</td>
            <td>
                <form action="{{ route('posts.destroy',$post->id) }}" method="POST">
   
                    <a class="btn btn-info" href="{{ route('posts.show',$post->id) }}">Show</a>
    
                    <a class="btn btn-primary" href="{{ route('posts.edit',$post->id) }}">Edit</a>
   
                    @csrf
                    @method('DELETE')
      
                    <button type="submit" class="btn btn-danger">Delete</button>
                </form>
            </td>
        </tr>
        @endforeach
    </table>
  
    {!! $posts->links() !!}
      
@endsection

resources/views/posts/create.blade.php

@extends('post.layout')
  
@section('content')
<div class="row">
    <div class="col-lg-12 margin-tb">
        <div class="pull-left">
            <h2>Add New Post</h2>
        </div>
        <div class="pull-right">
            <a class="btn btn-primary" href="{{ route('posts.index') }}"> Back</a>
        </div>
    </div>
</div>
   
@if ($errors->any())
    <div class="alert alert-danger">
        <strong>Error!</strong> <br>
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif
   
<form action="{{ route('posts.store') }}" method="POST">
    @csrf
  
     <div class="row">
        <div class="col-xs-12 col-sm-12 col-md-12">
            <div class="form-group">
                <strong>Name:</strong>
                <input type="text" name="name" class="form-control" placeholder="Name">
            </div>
        </div>
        <div class="col-xs-12 col-sm-12 col-md-12">
            <div class="form-group">
                <strong>Detail:</strong>
                <textarea class="form-control" style="height:150px" name="detail" placeholder="Detail"></textarea>
            </div>
        </div>
        <div class="col-xs-12 col-sm-12 col-md-12 text-center">
                <button type="submit" class="btn btn-primary">Submit</button>
        </div>
    </div>
   
</form>
@endsection

resources/views/posts/edit.blade.php

@extends('post.layout')
   
@section('content')
    <div class="row">
        <div class="col-lg-12 margin-tb">
            <div class="pull-left">
                <h2>Edit Post</h2>
            </div>
            <div class="pull-right">
                <a class="btn btn-primary" href="{{ route('posts.index') }}"> Back</a>
            </div>
        </div>
    </div>
   
    @if ($errors->any())
        <div class="alert alert-danger">
            <strong>Error!</strong> <br>
            <ul>
                @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </div>
    @endif
  
    <form action="{{ route('posts.update',$post->id) }}" method="POST">
        @csrf
        @method('PUT')
   
         <div class="row">
            <div class="col-xs-12 col-sm-12 col-md-12">
                <div class="form-group">
                    <strong>Name:</strong>
                    <input type="text" name="name" value="{{ $post->name }}" class="form-control" placeholder="Name">
                </div>
            </div>
            <div class="col-xs-12 col-sm-12 col-md-12">
                <div class="form-group">
                    <strong>Detail:</strong>
                    <textarea class="form-control" style="height:150px" name="detail" placeholder="Detail">{{ $post->detail }}</textarea>
                </div>
            </div>
            <div class="col-xs-12 col-sm-12 col-md-12 text-center">
              <button type="submit" class="btn btn-primary">Submit</button>
            </div>
        </div>
   
    </form>
@endsection

resources/views/posts/show.blade.php

@extends('post.layout')
@section('content')
    <div class="row">
        <div class="col-lg-12 margin-tb">
            <div class="pull-left">
                <h2> Show Post</h2>
            </div>
            <div class="pull-right">
                <a class="btn btn-primary" href="{{ route('posts.index') }}"> Back</a>
            </div>
        </div>
    </div>
   
    <div class="row">
        <div class="col-xs-12 col-sm-12 col-md-12">
            <div class="form-group">
                <strong>Name:</strong>
                {{ $post->name }}
            </div>
        </div>
        <div class="col-xs-12 col-sm-12 col-md-12">
            <div class="form-group">
                <strong>Details:</strong>
                {{ $post->detail }}
            </div>
        </div>
    </div>
@endsection

Now, we will laravel 10 crud operation application using the following command.

php artisan serve

Original article source at: https://websolutionstuff.com/

#laravel #crud #step

How to Laravel 10 CRUD Operation Step By Step
Desmond  Gerber

Desmond Gerber

1676648700

Emulate Android on Ubuntu 22.04 Step by Step

Emulate Android on Ubuntu 22.04 Step by Step

When you think of running Android on your Ubuntu, you must utilize an emulator to make it possible. When using Windows, you can run Android using a tool like Bluestacks. Linux has different Android emulators you can use to install and run your Android apps.

Linux does not support Android applications unless you install an app like Anbox, which allows you to emulate Android on Ubuntu 22.04. This post details all the steps to follow.

How to Use Anbox to Emulate Android on Ubuntu 22.04

Android is the most popularly used operating system for mobile. When you want to experience the same feeling on your Linux system, your best chance is to use an emulator. With an Android emulator, you can install and run Android applications without affecting your host system. For this guide, we will focus on the Anbox Android emulator.

Anbox is an open-source compatibility Linux system emulation tool that allows users to install and run Android applications. Anbox utilizes Linux Containers (LXC) to run the Android runtime environment and recreates the Android directory structure. With Anbox, all the Android applications installed use the native Linux kernel to run.

For Ubuntu 22.04, Anbox is available as a snap package. However, we must use the —devmode option as Anbox is yet to be fully available as a snap package.

Run the below command to install the Anbox snap.

$ sudo snap install --devmode --beta anbox

Let snap download and start the installation.

Once the command executes and Anbox installs, you will get an output like the one below confirming the installation was successful.

Anbox is now available in your Ubuntu 22.04 and you can open it from your applications.

Once you click on Anbox, it will start loading to start the Android runtime environment on your Linux system.

Once Anbox starts, it will open its Anbox application manager dashboard containing the different applications available for any Android system.

You can click on any option from the dashboard to explore more. For instance, if you click the Email option, you will be prompted to set up your account before using Email.

Still, you can click on an application like Calendar, which will open the same calendar application you get when using your Android mobile.

If you click on the Settings application, you can get all the details about the emulated Android. You will get the Settings page open like in the output below.

You can click on any option to view more details. For instance, we have opened the Languages & Input option to change the system’s language or the keyboard used for the emulated Android.

Suppose you want to view the available security features or set up your login authentication, clicking on security opens like an actual Android phone.

You can also access the file manager to view the different available options, such as the videos, if you have any.

Lastly, you can click on the Apps option to view the available applications on the emulated Android. You will notice that it has the same applications as most Android phones.

When you want to remove Anbox from your Ubuntu 22.04, run the command below.

$ sudo snap remove anbox

Conclusion

Running Android on Ubuntu requires you to use an emulator to help run the Android runtime environment on your Linux system. This guide has focused on how to use the Anbox Android emulator tool to emulate Android on Ubuntu 22.04. We have seen how to install, use, and remove Anbox.

Original article source at: https://linuxhint.com/

#android #ubuntu #step 

Emulate Android on Ubuntu 22.04 Step by Step
Web Monster

Web Monster

1676485360

Building a Blog from Scratch with HTML, CSS, and JavaScript: A Step-by

In this tutorial, you'll learn how to create a blog from scratch using HTML, CSS, and JavaScript. 

We'll start by setting up the basic structure of the blog, then add styling with CSS, and finally, 

include some interactive features using JavaScript. 

By the end of this video, you'll have a fully functional blog that you can customize and use as a personal website, portfolio, 

or anything else you like. 

🔗 Essential links 

----------------------------- 

Source Code: https://upfiles.com/AF1j 

📺 My Channel : 

https://www.youtube.com/channel/UCHI9Mo7HCSlqum1UMP2APFQ 

🔔 Subscribe : 

https://www.youtube.com/channel/UCHI9Mo7HCSlqum1UMP2APFQ 

 

🕐 New Videos Every Week 

🔗 Essential links

 ----------------------------- 

Image : https://unsplash.com/ 

Font : https://fonts.google.com/

 FontAwesome : https://fontawesome.com/ 

📹 Others Videos 

----------------------------- 

Music Landing Page Website Design using Html CSS & Javascript: 

➤ Watch: https://youtu.be/HoTbTNju0eg 

Sidebar Menu in HTML CSS & JavaScript | Dark/Light Mode : 

➤ Watch: https://youtu.be/QQni50OBNpU

Responsive Bloggar - News Magazine : 

➤ Watch: https://youtu.be/Js-J_SVZEnY

 Responsive Minimal Portfolio Website : 

➤ Watch: https://youtu.be/e1vjlzg8i3Y

💜 Like - Follow & Subscribe Me 

----------------------------- 

Twitter : https://bit.ly/3IOBEqc 

Instagram : https://bit.ly/3GHoQyT 

Facebook : https://bit.ly/3IMfk04 

Linkedin : https://bit.ly/3INnwNY 

Tags: 

#HTML #CSS #JavaScript #webmonster #responsivewebsite #LandinPage #web_development #blogging #create a blog #how_to_build_a_blog #website_design #website_development #front-end_development #coding_tutorial #step-by-step_tutorial #beginner-friendly

 

Link The Video:

➤ Watch: https://youtu.be/a0YjoJhIjXQ

 

Building a Blog from Scratch with HTML, CSS, and JavaScript: A Step-by
Nat  Grady

Nat Grady

1676334060

Guide for A Successful Hackathon with 10-step

Guide for a successful hackathon with 10-step

Follow this blueprint for planning, hosting, and managing a successful hackathon.

Hackathons are easy. How much thought do you need to put into them anyway? Just set a date, and people will show up. Well, that is not quite true!

While you may get lucky with that approach, the reality is that hackathons are a keystone experience in the tech industry, and attendees have specific expectations. Not only that, but your organization also has certain needs and should set goals for a hackathon. So, how do you ensure that a hackathon works for your organization and attendees?

A successful hackathon depends on several decisions that tend to be recursive. Decisions about what you want to achieve will impact what resources you allot and how you want to communicate. Those decisions affect whether you go virtual or in person, and that decision will once again impact the resources you need and how you communicate. Alignment when planning hackathons is not just about getting people to agree. You will have a whole suite of decisions that must internally align. For example, a technically difficult hackathon might not be able to attract a large audience (ask me how I know!) and will require a specialized recruitment strategy that requires different resources.

I've done many hackathons over the years, including just a few months back, when my organization hosted a hackathon that led to new features that we will incorporate into the next version of our open source product, Traefik Proxy 3.0. So, trust me when I say planning a hackathon that will enrich attendees and create valuable outcomes for your project is about more than hope, pizza, and chaos.

This article uses the most recent Traefik Labs Hackathon as a blueprint. I share a checklist, tips, and tricks to help you identify your objectives, plan, manage the contest and compensation, share your results, and manage the long tail of the hackathon (the work isn't over when the hackathon ends!)

This guide serves as a model for you to outline best practices so that you, too, can hold a successful hackathon with a sizable target audience that delivers results!

  1. Three questions to determine your goals
  2. Why are you doing this?
  3. Who is your audience?
  4. How are you measuring goals?
  5. Decide on in-person vs. virtual
  6. Build your communication strategy
  7. Decide on the prizes
  8. Swag it up
  9. Get the word out
  10. Managing the long tail

1. Three questions to determine your goals

The first and most crucial step is to set your goals. But this is no simple affair. Before you set goals, you need to coordinate internally on multiple fronts and ask questions such as:

  • Why do you want to do a hackathon?
  • Who do you want to attend?
  • How are you going to measure your success?

Identify your internal stakeholders and set expectations

Hackathons are cross-functional. No hackathon is run by a community person alone. It is important to ensure everyone is aligned on the goals, what is required to achieve them, and that the necessary resources are committed. This probably sounds super corporate, but these functions exist even within the smallest projects. A project needs adoption and code. It also needs value decisions based on who will be using it. And, of course, projects need passionate contributors.

Hackathons require cross-functional resources. One team with a single set of resources cannot successfully run a hackathon. The organization must make various resources available, including:

  • Marketing for planning and outreach.
  • Product Management for product and industry-specific insight.
  • Engineering for deep technical knowledge and community engagement.

For these reasons, hackathons usually support cross-functional goals. Your Community Team, for example, might want to build ownership and convert users to active community members. The Marketing Team might want to enhance awareness and court new users. The Engineering Team might need new perspectives on specific needs or challenges. The Product Team might have goals or no-go areas the community should be aware of.

And last but not least, the hackathon budget is cross-functional. I am sorry to inform you, but hackathons ain't free! Your largest expense is always the dedicated time of your team.

2. Why are you doing this?

Setting your goals is the most important part of a successful hackathon. If you don't know what you want to do or why a hackathon is important, at best, it will have a ton of wasted potential and be a disconnected mess at worst.

Communities feed off of ownership. Decide what you need from your community and what ownership stake you want community members to have. Without a clear understanding of this, your hackathon might not reach its full potential in empowering your community.

Be very careful with your hackathon design and goals. Different types of hackathons appeal to different skill levels. If the code you're looking for is very advanced, take the extra time to court the right audience and accept that it will include less overall attendance. Cast a wide net if the contributions can vary in skill and experience.

Are you hosting a hackathon to get code and build your project?

  • Sometimes, projects hit a critical juncture or acquire a lot of excitement around them, and you want to harness the energy to build something together. A hackathon is a great way to achieve this!
  • If you have an active community of users, a hackathon can bring everyone together at the same time to harness that excitement to feed the creative energy of your group.

Note: This is more easily achievable with smaller groups who know each other and have a shared experience with the project. You also need to carefully evaluate the skills required to build your project.

Are you hosting a hackathon to build your community or re-engage them?

  • Maybe you are just building your community or noticed that your community needs a little juice. Hackathons are exciting, and they can help bring that back.
  • Above, I said, "Communities feed off of ownership." If your community members do not feel they have a stake or that their needs and voices matter, they will drift away. This is common when projects grow and become more formalized. As the barrier to entry rises, the ability for community members to feel ownership falls, and the project becomes like a product to the user. One way to enhance community membership is by creating events that engage users and lower the bar for entry: Bug round-ups, light requests, and longer timelines.
  • Perhaps your user community is growing, but the contributor community is becoming more specialized as your tech becomes more complex. In this case, you need to court sophisticated technologists who understand your tech and the use cases. Look for community members who use your tech in their jobs—especially at companies with large or complex deployments. These people are more likely to understand the needs of users and of the tech itself. They will also have suggestions for significant and valuable enhancements.
  • You are free to choose goals that build your community and match your team and community members' energy and time. For example, at Traefik Labs, a hackathon aimed at enthusiastic folks with a small time commitment might target our Plugin Catalog. However, when looking for larger contributions or contributions that take significant expertise, we might target advanced technologists–especially those we know.

Are you hosting a hackathon to celebrate something?

  • Hackathons are a great way to celebrate a new launch and hype your community. For example, that is exactly why we hosted the Traefik Proxy 3.0 Hackaethon.
  • Hackathons are also great for getting the word out about a new product capability. The Traefik Plugin Hackaethon is an excellent example here.
  • Maybe you want to organize an event to celebrate your top contributors. Do it with a hackathon! Take a look at this hackathon organized by HackerOne. And if you're thinking, "but this is not about open source software (OSS), how can it be a hackathon?" I've got news for you—hackathons are not just for OSS! Hackathons are for creating with a large community.

Are you hosting a hackathon to build awareness?

Hackathons are a great place to begin if you are just starting and want to build awareness around your product/brand. However, there are a few conditions.

  • Laser-focused goals and big contributions are unlikely to happen at this stage. Go for a softer, broader focus, and minimize the work required by attendees.
  • Reach out to new community members, less experienced users, and users with less exposure to your specific project.

Are you hosting a hackathon to connect to users?

I can think of no better way to connect new users to your project than a hackathon. Not only will your users become intimately familiar with your project, but hackathons also have a unique way of engendering a sense of ownership, rarely achievable through other types of events.

3. Who is your audience?

Assuming you have pinpointed why you want to host a hackathon and what you want to achieve, it's time to assess the characteristics that a participant needs to be successful. Use your decisions about your goals to identify your audience to ask what type of community member can help you achieve your objectives. Use the list of comparisons below:

  • Highly-skilled vs. mixed-skilled vs. low-skilled
  • Specialized vs. generalized skill
  • Intensive time vs. less intensive time
  • Individual contributions vs. group contributions

Your most active community members must look a bit like your target audience.

You might rethink your goals if your target audience doesn't align with at least 80% of the people you know you can attract. Accurately identifying your target audience will go a long way to making your communication strategy around the hackathon and the hackathon itself more successful.

4. How are you measuring goals?

Perfect, now that you answered the first two big questions and have your goals laid down, it's time for the third big question—how will you measure those goals? Inspiring your internal teams and your community to work together in building the future of your project, engendering ownership, and increasing engagement are awesome, but you can't determine success if you can't measure your goals.

What does success look like immediately after the event?

  • Clearly, the first sign of success is that your attendees had an overall good experience and are motivated to engage more with your project.
  • If you are looking for outreach, set a quantity of participants to shoot for and a number of participants who return to contribute more after the event or in three months.
    • A major sign of success is whether attendees connect and engage with each other, co-educate, and build teams during their hackathon.
    • Were mentorships built? Through partnership, did several newer users grow into skilled mid-level users, or did mid-level users evolve into expert-tier users? This is the gold ring of success indicators.
    • Did your partner organizations (maybe universities) request future hackathons or other events?
  • If building awareness, you might also look for successful follow-up chatter. Who wrote blog posts? Were attendees talking about it on social media?
  • If you are looking for contributions, did they work for you? Are these the contributions you want? Did they impact how your team thinks about the problems they face? Will you have ongoing collaborations with these contributors?

What will denote success three months after the event?

Defining benchmarks for long-term success is just as important. Here are a few examples of what could indicate long-term success:

  • Your hackathon should increase the number of returning contributors to your project. The goal is to get people hooked. If people new to your project came from the hackathon and stayed as users, or if your existing users became more active, you know you won.
  • Hackathons are great as self-contained events, but they are supremely valuable as marketing content. They build trust in the community, showing you are responsive and value community input. They are fun loci of activity that let community members bond and look forward to the future, and they are aspirational. People love to see others celebrated and plan to achieve that celebration in the future.
  • When you build marketing content around your hackathon (or better yet, others build content around your hackathon), you can expand your reach among second-degree connections.
  • Tall poppy syndrome is a shame. Hackathons are a great opportunity to gather those participants who stood out and galvanize them to do other cool things and spread the word about your project.

5. Decide on in-person vs. virtual

I know what you're thinking—is in-person even a consideration? We've all gotten so used to doing everything virtually in the post-covid world. So, are the days of in-person gone? I would argue no, they are not. With care and safety measures in place, in-person events are the heart and soul of hackathons.

  • In-person means no distractions, lots of pizza, and energy drink-fueled friendship.
  • In-person fuels group participation rather than individual contributor participation.
  • In-person works well at scale and in miniature: Organizing in-person hackathons for large groups brings high energy and rewards. But they can get quite costly. If you want to organize a large-scale hackathon, you'll be more successful if you target less experienced developers (students, clubs, new careerists) because these folks have the most time and the most to gain when demonstrating their skill and passion.
  • In-person also works well for small groups and is great for intense planning and iteration—long nights with new and old friends, usually over food and beer!

And while many pros come with in-person hackathons, it doesn't mean that the virtual experience only comes with cons. Granted, nothing replaces that feeling of late nights with pizza, off-the-cuff remarks that end up changing your entire project, and a friendly set of eyes over your shoulder as you test or debug. But...

  • Virtual means you can get a wider group of participants at a significantly lower cost.
  • Virtual respects disability.
  • Virtual is geolocation friendly.
  • Virtual allows for higher individual contributor participation.
  • Virtual offers more flexibility in the style and length of the event – you cannot have a month-long in-person event!

Timelines of virtual hackathons

Did you decide to do a virtual hackathon? Great! Now, you need to determine the type of virtual hackathon you want. Do you envision a prolonged or intensive timeline? Keep in mind that the type of virtual hackathon you choose will determine, to some extent, your target audience and communication strategy.

Extended timeline:

  • Allows after-hours tinkering and enables developers to attend without taking time off from work.
  • Provides more time to solicit contributions.
  • Requires fewer resources for both the organizer and the participants.
  • Extended timelines require fewer real-time resources.

Intense timeline:

  • Recreates that feeling of intensity usually experienced in in-person hackathons.
  • Requires a high amount of resources for a short period of time.
  • Requires tight management and a communication platform.
  • Requires clear one-on-one communication, but also fosters group-to-group or intra-community communication.

6. Build your communication strategy

Speaking of communication, once you have your goals, you must decide who communicates with participants and how. It's common to choose between the popular apps of the day. Your choice impacts the event's feel. Different chat applications and collaboration platforms have their own cultures and strengths. The decision you made early on about how to host your hackathon (in-person or virtual, prolonged or intense timeline) is likely to have the most significant impact on your communication strategy.

In-person communication plan

If you are running an in-person hackathon, consider it a genuine event—it feels almost like a conference. In-person hackathons often include the following:

  • Workshops/round tables: Meant to educate and develop new industry standards/best practices for the concerns of the day. These sessions can function as proctored time-bound conversations amongst 6-10 individuals, where they agree upon findings and take notes that are made public to all participants.
  • Planning sessions: Often used for projects with non-code outcomes, like developing updated standards.
  • Coding sessions: Used for code-based projects which require work to maintain and enhance.

Each of the above has different communication needs:

  • General communication for running the event (food, cleaning, management of resources).
  • Masters of ceremonies to move through the agendas.
  • For workshops:
    • People prepared to facilitate, but not lead, conversations in workshops.
    • Note takers and people to make sure that the notes are turned into a publishable product.
    • Project managers to ensure the above tasks are done.

Making this all happen requires the resources and specialized knowledge from your Community, Product Managers, and teach-savvy teams. From past experience, it took a team of community members and staff to manage an event of this scope. To be successful, your team will need specialized people as well.

You also need to decide what types of communication you want to foster and who is responsible for it:

  • Multiple teams will need to take shifts to perform general support.
  • A DevRel, engineering, or support team will need to manage technical communication between triage and participants.
  • Community Teams usually spend extensive time connecting participants to help build strong groups by reinforcing skills or points of view. This is one way to ensure that hackathon magic.
  • Community Teams also need to support marketing efforts to engage participants and manage follow-up.

Virtual communication plan

For virtual hackathons, the choice of a communication platform depends heavily on the outcome you want to achieve, the timeline you've chosen for your hackathon (prolonged or intensive), and the type of communication you wish to facilitate (synchronous or asynchronous).

Using Pull Requests and Issues on Git hosts (asynchronous):

  • Choosing to communicate through Git pull requests and Issues on your project directly frees up technical staff resources because it keeps the conversations about projects in your current process and allows your team to be responsive rather than instigating communication.
  • This approach is great if the team for your hackathon is small or if the expected contributions are relatively small and you do not plan to help participants form teams.
  • Using your existing processes is especially great for prolonged hackathons as they do not require additional moderation or require your team to monitor an additional app.
  • The downside is that you will only facilitate communication with the individual contributor or group of contributors already working together. It's difficult to connect participants who are working separately. Participants can't find each other as easily on their own, so you lose some of the magic that happens when hackathon participants organically talk to each other in open threads.

Using a chat application (synchronous):

  • Choosing dedicated chat servers is required for intense hackathons.
  • Chat facilitates the team formation and communication necessary for complex projects with fast timelines and sparks the brainstorming that preludes an awesome contribution.
  • Additionally, your goal is to build community. People want to join communities where they have ownership, have friends, and feel comfortable. You need a place for participants to feel connected to each other if you want them to return.
  • Chat servers can outlast an event, allowing for continued community engagement.

Regardless of which platform you choose, you need a communication plan that identifies when every person on your team is available. Managing a virtual hackathon can get quite tricky, primarily due to the different timezones—people can participate whenever they want, from wherever they want. You must plan to accommodate participants across all time zones and for every occasion. Draw up a plan with who is responsible (and when) for the following:

  • Determining response SLAs.
  • Animating your virtual space (a dead space guarantees poor communication).
  • Encouraging team building.
  • Responding to technical questions.
  • Checking in on participants.
  • Moderating the space to ensure the safety of your participants.

7. Decide on the prizes

Is your hackathon a contest? Hackathon participants are often content with grand prizes and "swagpaloozas" for top contributions. But before you decide on the fun stuff (the actual awards), you must determine what your contest values.

  • What differentiates a good contribution from a great contribution? If your attendees know how you feel about this, they are more likely to hit it out of the park.
  • What do you value? This is your chance to tell participants what you want to see submitted by attaching a prize to it. For example, during the last Traefik Hackaethon, we offered bounties for the most-wanted features. These were, indeed, the ones most people worked on.
  • Are there categories of contributions? You need to decide on prizes for each category.
  • Create a rubric (a chart or grid defining and ranking achievements, like this example). This way, participants know what you value and how they are judged. This was one way we improved submissions at HackerOne.

On the other hand, some may argue that competition is overrated. If your goal is participation, feel free to reward every single one of your participants for simply giving back! Hacktoberfest is a great example of this approach.

8. Swag it up

Everyone loves swag! And your participants would certainly appreciate a token to remember this event, whether virtual or in person. Swag has two purposes:

  • Swag shows your appreciation: The contributors took their time to engage with you in an intense way; thank them with a gift that shows you value their contributions.
  • Swag builds awareness: Gifting swag to your participants helps them spread the love and build awareness of your community by sharing their loot and experience.

The community loves swag, but they don't love boring swag! You probably distributed your existing t-shirts and stickers during another event. Make your hackathon memorable and go for new, exciting, and exclusive designs. Shirts are great, and hoodies reign supreme. But think about cool swag participants may not have already. Think of something that could become their new staple item, like backup batteries or hats (both popular at HackerOne). Personally, my own home features some towels and slippers from hackathons!

9. Get the word out

Setting your goals and deciding on amazing grand prizes and swag are all important steps. But how will anyone know your hackathon is happening if you don't get the word out? You need to investigate the available channels carefully, and you need to be bold with your promotion. I'm talking blogs, vlogs, emails, social media—anything you can get your hands on.

However, depending on your defined goals, you need to invest in the appropriate channel. Where you advertise depends on who you want to invite to your hackathon.

  • IIf you want to attract more experienced users, target big organizations where your project is used. LinkedIn and email promotion would be most effective here.
  • If you want to bring in new and less experienced users, you're better off targeting universities and boot camps. Promoting the event on community-based media, like Mastodon, Matrix, Mattermost, Reddit, Discourse, Discord, and any place your target audience hangs out is a better choice.

10. Managing the long tail

Yay, the hackathon is over! Now all hackathon-related activities can stop, and we no longer need to pull resources, right? Wrong! Think of hackathons as only one step of the road in a series of events in your software development and community building. To deem your hackathon a success, you must be prepared to engage in post-event activities.

  • Communicating your results: Don't forget to communicate hackathon outcomes internally and externally. Demonstrate the ownership the community members gained during the hackathon to grow trust in your community and project.
  • Building community: Lean on your hackathon participants for future community activity.
  • Putting together the retrospective: What went well, what went terrible, what was meh, what surprised you? This analysis is how you grow, change, and iterate. Don't forget to do a blameless retro as soon as possible so it is all fresh in your mind.

Wrap up

If you started reading this article thinking that hackathons aren't that hard to pull off, I'm sorry to have burst your bubble! And although I sincerely believe hackathons are a great way to engage and communicate with your community on so many levels, having just the intention does not guarantee the results.

For a hackathon to be successful, you need to be meticulous and prepared to invest significant resources and effort to plan and execute it properly.

Thank you for reading, and I hope this checklist helps you successfully organize your next hackathon!

Download this article as a PDF and EPUB

Original article source at: https://opensource.com/

#hackathon #step 

Guide for A Successful Hackathon with 10-step
Hunter  Krajcik

Hunter Krajcik

1674813120

Validate Multi Step Form Using jQuery

In this article, we will see how to validate multi step form wizard using jquery. Here, we will learn to validate the multi step form using jquery. First, we create the multi step form using bootstrap. Also, in this example, we are not using any jquery plugin for multi step form wizard.

So, let's see jquery multi step form with validation, how to create multi step form, multi step form wizard with jquery validation, bootstrap 4 multi step form wizard with validation, multi step form bootstrap 5, and jQuery multi step form with validation and next previous navigation.

Add HTML:

<html lang="en">
</head>
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700&display=swap" rel="stylesheet">
</head>
<body>  
    <div class="main">
      <h3>How To Validate Multi Step Form Using jQuery - Websolutionstuff</h3>
        <form id="multistep_form">
            <!-- progressbar -->
            <ul id="progress_header">
                <li class="active"></li>
                <li></li>
                <li></li>
            </ul>
            <!-- Step 01 -->
            <div class="multistep-box">
                <div class="title-box">
                    <h2>Create your account</h2>
                </div>
                <p>
                    <input type="text" name="email" placeholder="Email" id="email">
                    <span id="error-email"></span>
                </p>
                <p>
                    <input type="password" name="pass" placeholder="Password" id="pass">
                    <span id="error-pass"></span>
                </p>
                <p>
                    <input type="password" name="cpass" placeholder="Confirm Password" id="cpass">
                    <span id="error-cpass"></span>
                </p>
                <p class="nxt-prev-button"><input type="button" name="next" class="fs_next_btn action-button" value="Next" /></p>
            </div>
            <!-- Step 02 -->
            <div class="multistep-box">
                <div class="title-box">
                    <h2>Social Profiles</h2>
                </div>
                <p>
                    <input type="text" name="twitter" placeholder="Twitter" id="twitter">
                    <span id="error-twitter"></span>
                </p>
                <p>
                    <input type="text" name="facebook" placeholder="Facebook" id="facebook">
                    <span id="error-facebook"></span>
                </p>
                <p>
                    <input type="text" name="linkedin" placeholder="Linkedin" id="linkedin">
                    <span id="error-linkedin"></span>
                </p>
                <p class="nxt-prev-button">
                    <input type="button" name="previous" class="previous action-button" value="Previous" />
                    <input type="button" name="next" class="ss_next_btn action-button" value="Next" />
                </p>
            </div>
            <!-- Step 03 -->
            <div class="multistep-box">
                <div class="title-box">
                    <h2>Personal Details</h2>
                </div>
                <p>
                    <input type="text" name="fname" placeholder="First Name" id="fname">
                    <span id="error-fname"></span>
                </p>
                <p>
                    <input type="text" name="lname" placeholder="Last Name" id="lname">
                    <span id="error-lname"></span>
                </p>
                <p>
                    <input type="text" name="phone" placeholder="Phone" id="phone">
                    <span id="error-phone"></span>
                </p>
                <p>
                    <textarea name="address" placeholder="Address" id="address"></textarea>
                    <span id="error-address"></span>
                </p>
                <p class="nxt-prev-button"><input type="button" name="previous" class="previous action-button" value="Previous" />
                    <input type="submit" name="submit" class="submit_btn ts_next_btn action-button" value="Submit" />
                </p>
            </div>
        </form>
        <h1>You are successfully logged in</h1>
    </div>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.0/jquery.easing.js" type="text/javascript"></script>
</body>
</html>

Add CSS:

body {
    display: inline-block;
    width: 100%;
    height: 100vh;
    overflow: hidden;
    background-image: url("https://img1.akspic.com/image/80377-gadget-numeric_keypad-input_device-electronic_device-space_bar-3840x2160.jpg");
    background-repeat: no-repeat;
    background-size: cover;
    position: relative;
    margin: 0;
    font-weight: 400;
    font-family: 'Roboto', sans-serif;
}
body:before {
    content: "";
    display: block;
    width: 100%;
    height: 100%;
    background: rgba(0,0,0,0.5);
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
}
.main {
    position: absolute;
    left: 0;
    right: 0;
    top: 30px;
    margin: 0 auto;
    height: 515px;
}
input:-internal-autofill-selected {
    background-color: #fff !important;
}
#multistep_form {
    width: 550px;
    margin: 0 auto;
    text-align: center;
    position: relative;
    height: 100%;
    z-index: 999;
    opacity: 1; 
    visibility: visible; 
}
/*progress header*/
#progress_header {
    overflow: hidden;
    margin: 0 auto 30px;
    padding: 0;
}
#progress_header li {
    list-style-type: none;
    width: 33.33%;
    float: left;
    position: relative;
    font-size: 16px;
    font-weight: bold;
    font-family: monospace;
    color: #fff;
    text-transform: uppercase;
}
#progress_header li:after {
    width: 35px;
    line-height: 35px;
    display: block;
    font-size: 22px;
    color: #888;
    font-family: monospace;
    background-color: #fff;
    border-radius: 100px;
    margin: 0 auto;
    background-repeat: no-repeat;
    font-family: 'Roboto', sans-serif;
}
#progress_header li:nth-child(1):after {
    content: "1";
}
#progress_header li:nth-child(2):after {
    content: "2";
}
#progress_header li:nth-child(3):after {
    content: "3";
}
#progress_header li:before {
    content: '';
    width: 100%;
    height: 5px;
    background: #fff;
    position: absolute;
    left: -50%;
    top: 50%;
    z-index: -1;
}
#progress_header li:first-child:before {
    content: none;
}
#progress_header li.active:before, 
#progress_header li.active:after {
    background-image: linear-gradient(to right top, #35e8c3, #36edbb, #3df2b2, #4af7a7, #59fb9b) !important;
    color: #fff !important;
    transition: all 0.5s;
}
/*title*/
.title-box {
    width: 100%;
    margin: 0 0 30px 0;
}
.title-box h2 {
    font-size: 22px;
    text-transform: uppercase;
    color: #2C3E50;
    margin: 0;
    font-family: cursive;
    display: inline-block;
    position: relative;
    padding: 0 0 10px 0;
    font-family: 'Roboto', sans-serif;
}
.title-box h2:before {
    content: "";
    background: #6ddc8b;
    width: 70px;
    height: 2px;
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    margin: 0 auto;
    display: block;
}
.title-box h2:after {
    content: "";
    background: #6ddc8b;
    width: 50px;
    height: 2px;
    position: absolute;
    bottom: -5px;
    left: 0;
    right: 0;
    margin: 0 auto;
    display: block;
}
/*Input and Button*/
.multistep-box {
    background: white;
    border: 0 none;
    border-radius: 3px;
    box-shadow: 1px 1px 55px 3px rgba(255, 255, 255, 0.4);
    padding: 30px 30px;
    box-sizing: border-box;
    width: 80%;
    margin: 0 10%;
    position: absolute;
}
.multistep-box:not(:first-of-type) {
    display: none;
}
.multistep-box p {
    margin: 0 0 12px 0;
    text-align: left;
}
.multistep-box span {
    font-size: 12px;
    color: #FF0000;
}
input, textarea {
    padding: 15px;
    border: 1px solid #ccc;
    border-radius: 3px;
    margin: 0;
    width: 100%;
    box-sizing: border-box;
    font-family: 'Roboto', sans-serif;
    color: #2C3E50;
    font-size: 13px;
    transition: all 0.5s;
    outline: none;
}
input:focus, textarea:focus {
    box-shadow: inset 0px 0px 50px 2px rgb(0,0,0,0.1);
}
input.box_error, textarea.box_error {
    border-color: #FF0000;
    box-shadow: inset 0px 0px 50px 2px rgb(255,0,0,0.1);
}
input.box_error:focus, textarea.box_error:focus {
    box-shadow: inset 0px 0px 50px 2px rgb(255,0,0,0.1);
}
p.nxt-prev-button {
    margin: 25px 0 0 0;
    text-align: center;
}
.action-button {
    width: 100px;
    font-weight: bold;
    color: white;
    border: 0 none;
    border-radius: 1px;
    cursor: pointer;
    padding: 10px 5px;
    margin: 0 5px;
    background-image: linear-gradient(to right top, #35e8c3, #36edbb, #3df2b2, #4af7a7, #59fb9b);
    transition: all 0.5s;
}
.action-button:hover, 
.action-button:focus {
    box-shadow: 0 0 0 2px white, 0 0 0 3px #6ce199;
}
.form_submited #multistep_form {
    opacity: 0; 
    visibility: hidden; 
}
.form_submited h1 {
    -webkit-background-clip: text;
    transform: translate(0%, 0%);
    -webkit-transform: translate(0%, 0%);
    transition: all 0.3s ease;
    opacity: 1;
    visibility: visible;
}
h1 {
    margin: 0;
    text-align: center;
    font-size: 90px;
    background-image: linear-gradient(to right top, #35e8c3, #36edbb, #3df2b2, #4af7a7, #59fb9b) !important;
    background-image: linear-gradient(to right top, #35e8c3, #36edbb, #3df2b2, #4af7a7, #59fb9b) !important;
    color: transparent;
    -webkit-background-clip: text;
    -webkit-background-clip: text;
    transform: translate(0%, -80%);
    -webkit-transform: translate(0%, -80%);
    transition: all 0.3s ease;
    opacity: 0;
    visibility: hidden;
    position: absolute;
    left: 0;
    right: 0;
    margin: 0 auto;
    text-align: center;
    top: 50%;
}
h3{
  color:#fff;
  text-align:center;
  margin-bottom:20px;
}

Add jQuery:

var current_slide, next_slide, previous_slide;
var left, opacity, scale;
var animation;

var error = false;

// email validation
$("#email").keyup(function() {
    var emailReg = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/;
    if (!emailReg.test($("#email").val())) {
        $("#error-email").text('Please enter an Email addres.');
        $("#email").addClass("box_error");
        error = true;
    } else {
        $("#error-email").text('');
        error = false;
        $("#email").removeClass("box_error");
    }
});
// password validation
$("#pass").keyup(function() {
    var pass = $("#pass").val();
    var cpass = $("#cpass").val();

    if (pass != '') {
        $("#error-pass").text('');
        error = false;
        $("#pass").removeClass("box_error");
    }
    if (pass != cpass && cpass != '') {
        $("#error-cpass").text('Password and Confirm Password is not matched.');
        error = true;
    } else {
        $("#error-cpass").text('');
        error = false;
    }
});
// confirm password validation
$("#cpass").keyup(function() {
    var pass = $("#pass").val();
    var cpass = $("#cpass").val();

    if (pass != cpass) {
        $("#error-cpass").text('Please enter the same Password as above.');
        $("#cpass").addClass("box_error");
        error = true;
    } else {
        $("#error-cpass").text('');
        error = false;
        $("#cpass").removeClass("box_error");
    }
});
// twitter
$("#twitter").keyup(function() {
    var twitterReg = /https?:\/\/twitter\.com\/(#!\/)?[a-z0-9_]+$/;
    if (!twitterReg.test($("#twitter").val())) {
        $("#error-twitter").text('Twitter link is not valid.');
        $("#twitter").addClass("box_error");
        error = true;
    } else {
        $("#error-twitter").text('');
        error = false;
        $("#twitter").removeClass("box_error");
    }
});
// facebook
$("#facebook").keyup(function() {
    var facebookReg = /^(https?:\/\/)?(www\.)?facebook.com\/[a-zA-Z0-9(\.\?)?]/;
    if (!facebookReg.test($("#facebook").val())) {
        $("#error-facebook").text('Facebook link is not valid.');
        $("#facebook").addClass("box_error");
        error = true;
    } else {
        $("#error-facebook").text('');
        error = false;
        $("#facebook").removeClass("box_error");
    }
});
// linkedin
$("#linkedin").keyup(function() {
    var linkedinReg = /(ftp|http|https):\/\/?(?:www\.)?linkedin.com(\w+:{0,1}\w*@)?(\S+)(:([0-9])+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
    if (!linkedinReg.test($("#linkedin").val())) {
        $("#error-linkedin").text('Linkedin link is not valid.');
        $("#linkedin").addClass("box_error");
        error = true;
    } else {
        $("#error-linkedin").text('');
        error = false;
        $("#linkedin").removeClass("box_error");
    }
});
// first name
$("#fname").keyup(function() {
    var fname = $("#fname").val();
    if (fname == '') {
        $("#error-fname").text('Enter your First name.');
        $("#fname").addClass("box_error");
        error = true;
    } else {
        $("#error-fname").text('');
        error = false;
    }
    if ((fname.length <= 2) || (fname.length > 20)) {
        $("#error-fname").text("User length must be between 2 and 20 Characters.");
        $("#fname").addClass("box_error");
        error = true;
    }
    if (!isNaN(fname)) {
        $("#error-fname").text("Only Characters are allowed.");
        $("#fname").addClass("box_error");
        error = true;
    } else {
        $("#fname").removeClass("box_error");
    }
});
// last name
$("#lname").keyup(function() {
    var lname = $("#lname").val();
    if (lname != lname) {
        $("#error-lname").text('Enter your Last name.');
        $("#lname").addClass("box_error");
        error = true;
    } else {
        $("#error-lname").text('');
        error = false;
    }
    if ((lname.length <= 2) || (lname.length > 20)) {
        $("#error-lname").text("User length must be between 2 and 20 Characters.");
        $("#lname").addClass("box_error");
        error = true;
    }
    if (!isNaN(lname)) {
        $("#error-lname").text("Only Characters are allowed.");
        $("#lname").addClass("box_error");
        error = true;
    } else {
        $("#lname").removeClass("box_error");
    }
});
// phone
$("#phone").keyup(function() {
    var phone = $("#phone").val();
    if (phone != phone) {
        $("#error-phone").text('Enter your Phone number.');
        $("#phone").addClass("box_error");
        error = true;
    } else {
        $("#error-phone").text('');
        error = false;
    }
    if (phone.length != 10) {
        $("#error-phone").text("Mobile number must be of 10 Digits only.");
        $("#phone").addClass("box_error");
        error = true;
    } else {
        $("#phone").removeClass("box_error");
    }
});
// address
$("#address").keyup(function() {
    var address = $("#address").val();
    if (address != address) {
        $("#error-address").text('Enter your Address.');
        $("#address").addClass("box_error");
        error = true;
    } else {
        $("#error-address").text('');
        error = false;
        $("#address").removeClass("box_error");
    }
});

// first step validation
$(".fs_next_btn").click(function() {
    // email
    if ($("#email").val() == '') {
        $("#error-email").text('Please enter an email address.');
        $("#email").addClass("box_error");
        error = true;
    } else {
        var emailReg = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/;
        if (!emailReg.test($("#email").val())) {
            $("#error-email").text('Please insert a valid email address.');
            error = true;
        } else {
            $("#error-email").text('');
            $("#email").removeClass("box_error");
        }
    }
    // password
    if ($("#pass").val() == '') {
        $("#error-pass").text('Please enter a password.');
        $("#pass").addClass("box_error");
        error = true;
    }
    if ($("#cpass").val() == '') {
        $("#error-cpass").text('Please enter a Confirm password.');
        $("#cpass").addClass("box_error");
        error = true;
    } else {
        var pass = $("#pass").val();
        var cpass = $("#cpass").val();

        if (pass != cpass) {
            $("#error-cpass").text('Please enter the same password as above.');
            error = true;
        } else {
            $("#error-cpass").text('');
            $("#pass").removeClass("box_error");
            $("#cpass").removeClass("box_error");
        }
    }
    // animation
    if (!error) {
        if (animation) return false;
        animation = true;

        current_slide = $(this).parent().parent();
        next_slide = $(this).parent().parent().next();

        $("#progress_header li").eq($(".multistep-box").index(next_slide)).addClass("active");

        next_slide.show();
        current_slide.animate({
            opacity: 0
        }, {
            step: function(now, mx) {
                scale = 1 - (1 - now) * 0.2;
                left = (now * 50) + "%";
                opacity = 1 - now;
                current_slide.css({
                    'transform': 'scale(' + scale + ')'
                });
                next_slide.css({
                    'left': left,
                    'opacity': opacity
                });
            },
            duration: 800,
            complete: function() {
                current_slide.hide();
                animation = false;
            },
            easing: 'easeInOutBack'
        });
    }
});
// second step validation
$(".ss_next_btn").click(function() {
    // twitter
    if ($("#twitter").val() == '') {
        $("#error-twitter").text('twitter link is required.');
        $("#twitter").addClass("box_error");
        error = true;
    } else {
        var twitterReg = /https?:\/\/twitter\.com\/(#!\/)?[a-z0-9_]+$/;
        if (!twitterReg.test($("#twitter").val())) {
            $("#error-twitter").text('Twitter link is not valid.');
            error = true;
        } else {
            $("#error-twitter").text('');
            $("#twitter").removeClass("box_error");
        }
    }
    // facebook
    if ($("#facebook").val() == '') {
        $("#error-facebook").text('Facebook link is required.');
        $("#facebook").addClass("box_error");
        error = true;
    } else {
        var facebookReg = /^(https?:\/\/)?(www\.)?facebook.com\/[a-zA-Z0-9(\.\?)?]/;
        if (!facebookReg.test($("#facebook").val())) {
            $("#error-facebook").text('Facebook link is not valid.');
            error = true;
            $("#facebook").addClass("box_error");
        } else {
            $("#error-facebook").text('');
        }
    }
    // linkedin
    if ($("#linkedin").val() == '') {
        $("#error-linkedin").text('Linkedin link is required.');
        $("#linkedin").addClass("box_error");
        error = true;
    } else {
        var linkedinReg = /(ftp|http|https):\/\/?(?:www\.)?linkedin.com(\w+:{0,1}\w*@)?(\S+)(:([0-9])+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
        if (!linkedinReg.test($("#linkedin").val())) {
            $("#error-linkedin").text('Linkedin link is not valid.');
            error = true;
        } else {
            $("#error-linkedin").text('');
            $("#linkedin").removeClass("box_error");
        }
    }

    if (!error) {
        if (animation) return false;
        animation = true;

        current_slide = $(this).parent().parent();
        next_slide = $(this).parent().parent().next();

        $("#progress_header li").eq($(".multistep-box").index(next_slide)).addClass("active");

        next_slide.show();
        current_slide.animate({
            opacity: 0
        }, {
            step: function(now, mx) {
                scale = 1 - (1 - now) * 0.2;
                left = (now * 50) + "%";
                opacity = 1 - now;
                current_slide.css({
                    'transform': 'scale(' + scale + ')'
                });
                next_slide.css({
                    'left': left,
                    'opacity': opacity
                });
            },
            duration: 800,
            complete: function() {
                current_slide.hide();
                animation = false;
            },
            easing: 'easeInOutBack'
        });
    }

});

// third step validation
$(".ts_next_btn").click(function() {
    // first name
    if ($("#fname").val() == '') {
        $("#error-fname").text('Enter your First name.');
        $("#fname").addClass("box_error");
        error = true;
    } else {
        var fname = $("#fname").val();
        if (fname != fname) {
            $("#error-fname").text('First name is required.');
            error = true;
        } else {
            $("#error-fname").text('');
            error = false;
            $("#fname").removeClass("box_error");
        }
        if ((fname.length <= 2) || (fname.length > 20)) {
            $("#error-fname").text("User length must be between 2 and 20 Characters.");
            error = true;
        }
        if (!isNaN(fname)) {
            $("#error-fname").text("Only Characters are allowed.");
            error = true;
        } else {
            $("#fname").removeClass("box_error");
        }
    }
    // last name
    if ($("#lname").val() == '') {
        $("#error-lname").text('Enter your Last name.');
        $("#lname").addClass("box_error");
        error = true;
    } else {
        var lname = $("#lname").val();
        if (lname != lname) {
            $("#error-lname").text('Last name is required.');
            error = true;
        } else {
            $("#error-lname").text('');
            error = false;
        }
        if ((lname.length <= 2) || (lname.length > 20)) {
            $("#error-lname").text("User length must be between 2 and 20 Characters.");
            error = true;
        } 
        if (!isNaN(lname)) {
            $("#error-lname").text("Only Characters are allowed.");
            error = true;
        } else {
            $("#lname").removeClass("box_error");
        }
    }
    // phone
    if ($("#phone").val() == '') {
        $("#error-phone").text('Enter your Phone number.');
        $("#phone").addClass("box_error");
        error = true;
    } else {
        var phone = $("#phone").val();
        if (phone != phone) {
            $("#error-phone").text('Phone number is required.');
            error = true;
        } else {
            $("#error-phone").text('');
            error = false;
        }
        if (phone.length != 10) {
            $("#error-phone").text("Mobile number must be of 10 Digits only.");
            error = true;
        } else {
            $("#phone").removeClass("box_error");
        }
    }
    // address
    if ($("#address").val() == '') {
        $("#error-address").text('Enter your Address.');
        $("#address").addClass("box_error");
        error = true;
    } else {
        var address = $("#address").val();
        if (address != address) {
            $("#error-address").text('Address is required.');
            error = true;
        } else {
            $("#error-address").text('');
            $("#address").removeClass("box_error");
            error = false;
        }
    }

    if (!error) {
        if (animation) return false;
        animation = true;

        current_slide = $(this).parent().parent();
        next_slide = $(this).parent().parent().next();

        $("#progress_header li").eq($(".multistep-box").index(next_slide)).addClass("active");

        next_slide.show();
        current_slide.animate({
            opacity: 0
        }, {
            step: function(now, mx) {
                scale = 1 - (1 - now) * 0.2;
                left = (now * 50) + "%";
                opacity = 1 - now;
                current_slide.css({
                    'transform': 'scale(' + scale + ')'
                });
                next_slide.css({
                    'left': left,
                    'opacity': opacity
                });
            },
            duration: 800,
            complete: function() {
                current_slide.hide();
                animation = false;
            },
            easing: 'easeInOutBack'
        });
    }
});
// previous
$(".previous").click(function() {
    if (animation) return false;
    animation = true;

    current_slide = $(this).parent().parent();
    previous_slide = $(this).parent().parent().prev();

    $("#progress_header li").eq($(".multistep-box").index(current_slide)).removeClass("active");

    previous_slide.show();
    current_slide.animate({
        opacity: 0
    }, {
        step: function(now, mx) {
            scale = 0.8 + (1 - now) * 0.2;
            left = ((1 - now) * 50) + "%";
            opacity = 1 - now;
            current_slide.css({
                'left': left
            });
            previous_slide.css({
                'transform': 'scale(' + scale + ')',
                'opacity': opacity
            });
        },
        duration: 800,
        complete: function() {
            current_slide.hide();
            animation = false;
        },
        easing: 'easeInOutBack'
    });
});

$(".submit_btn").click(function() {
    if (!error){
        $(".main").addClass("form_submited");
    }
    return false;
})

 

Output:

how_to_validate_multi_step_form_using_jquery_output

Original article source at: https://websolutionstuff.com/

#jquery #validate #step 

Validate Multi Step Form Using jQuery
Nat  Grady

Nat Grady

1671787380

Best 9 Steps on How to Build an Online Community

9 steps on how to build an online community intro

Whether you are a programmer, a visual artist, a media pundit, or a niche aficionado, online communities are there for you. Since the offer online is huge, more and more people are joining as we speak.

By now everyone is part of one and finds at least one reason to be there. But good online communities that grow as time passes are special.

If you want to get all the benefits from them, you got to know about how to actually build them big and strong.

In today’s article, we will do just that by mentioning 9 clever methods/considerations to keep in mind.

Let’s start.

Get to Know Your Audience

First and foremost, you got to make sure you understand your audience well. This includes its preferred interests, language and tone, as well as what influences it, etc.

This point is so crucial because you’re not there to pretend you care about them but to honestly engage.

Once you get to know your audience, everything you do, from the silliest comment, will be better received. And by extension, you and they will have a vibrant community on your hands.

There are various ways to gain an understanding of it. For example, you could research by looking at blogs and groups, or by using keywords.

Another main method is to simply experiment a bit. There won’t be a way to exactly know them until you go through experiences together. Like in physical relationships, experience and history together play a big role in enduring the bond.

Find a Solution to Their Problem.

When you get to know your audience, you will start to see some of their problems. Then, we recommend digging deeper and identifying as many pressing issues as possible.

You aim for variety and large numbers because you want to pick a few and provide a solution to them.

For example, some problems may have to do with their relationship with their parents. Here, there isn’t a great deal you can do, besides entertain and extending a community to them.

But imagine if someone is having trouble ascending on their career. And you, because of your knowledge of X skill/field, give them a clear and substantive solution. That’s what we are aiming at here.

Give Real Value

When you provide substantive solutions, you are giving real value in the process. So, in a nutshell, real value is nothing more than contributing to others in an important fashion.

A clear example of non-real value is any type of generic content made with the sole purpose of profit. This type of content lacks originality, authenticity and treats its target audience as cash cows.

But of course, you won’t do that because even if you’re not changing someone’s life every time. Because you provide solutions and give what people want/need, doing so with passion, value emerges naturally.

Interact With Your Community

Even if you are your community’s only source of joy, you must engage regularly to keep it. Again, just like with any relationship, contact (of a positive kind) strengthens them.

One great fact is that interactions come in many shapes and forms online. You count with polls, posts, comments, videos, asking for submissions, reviews, Q&A, lives, etc.

And very importantly as well, not everything has to be connected with your content. For example, you could spend a few hours a month on a casual chit-chat and still be making your community stronger.

Setup Platforms For Your Community (Like Facebook Groups)

Of course, to interact you need a space. Thankfully, the online world again gives you many great options for all kinds of folks.

Let’s talk about FB groups for a moment since they are very popular and quite long living. On them, you will see people posting, commenting, and engaging often. Even those that only like or read get into the community and absorb some of its particularities.

For example, these platforms often allow for internal language, jokes, references, to emerge. And everyone, in one way or the other, gets influenced by it. Which is exactly what you are aiming at.

One piece of advice, make sure to pick one platform that fits well with your audience and content. Reddit is not the same as Tumblr, nor as FB.

Grow Your Community Frequently

Though we tend to like keeping our circles strong and small, it is better to keep expansion always in mind here. By that, we don’t mean a type of imperialist mission, but only a healthy dose of ambition.

Niche small communities are lovely, yes, but you could enlarge it a bit without sacrificing its likeability. Besides, consider that even if you open it for the broad public, not everyone will join. Only those that somehow feel attracted to it will do, which will maintain the essence of the community intact.

Now, there are 2 main ways to grow it. One comes almost naturally as you engage with it and continue to provide value. People will realize that and spread the word, forming a healthy feedback loop.

The other method is to pursue it. For example, you can ask your audience to spread the word. Or do it yourself, via posts in other sites to drag some audiences, collabs, giveaways, competitions, etc.

Don’t Ask For Money

We all understand that making a few bucks as a reward for the great work and value is fair. But, if you are thinking about building a community, it is not a good step to take.

This is because it simply gives the impression that you are in it for the cash. It could be even worse if people think you “were faking all along”. Don’t get offended if you encounter some of those presumptions. We have to admit the internet is full of people that only care about money, so suspicion is natural, we even suffer from it.

But please, don’t get discouraged and believe that a strong community and money cannot go together.

They surely can, and the way to go is to turn that step of “converting” into a natural transition. If you are giving great value and count with a nice community, sales will follow if you intend it.

Besides, if you get along well, you might get some contributions based on that alone. Just picture a friend of yours starting a business, you will at least contribute once to him/her, even if you don’t like the project. Now imagine, you are that friend of your audience, but they do like what you are giving them. Why wouldn’t they monetary contribute/repay?

Ask to Join Your Sales Funnel

With that realization in mind, what follows is only to direct them to your sales funnel. Which is the process of securing a sale, from the very start to the end. An example online starts with reading a post, then clicking on a site, following, and later converting.

Besides, when you incorporate them into this process, you are not asking for money alone. You are saying, “I got this valuable product, and I believe you know I deserve some credit for it”. A stronger basis to build a mutually beneficial relationship than this escapes me.

Organize Events

Lastly, another method that is connected to the one about interaction, is to do this. Events, of all kinds, work as opportunities to bring more people together than usual. And let the distinct features of the community thrive.

We are a social species; our communities are places that give us a sense of belonging and a bigger purpose. What better way to go than allowing spaces for this to flourish?

Besides, they can be great spaces to have fun, further get to know them, and even sell merch!

Regardless of where you see them, events are a must.

Conclusion of 9 Steps on How To Build an Online Community

A key takeaway is to remember 4 words: genuine, knowing, value, interaction. If they become your mantra, you will succeed with your mission of building a strong community. And we are certain that, all those 4 are more than familiar to you.

Thanks for reading this article, we hope it has been helpful and wish you all good luck as always.

Original article source at: https://www.blog.duomly.com/

#step #online #community 

Best 9 Steps on How to Build an Online Community
Gordon  Murray

Gordon Murray

1671730920

Create a Multi Step Forms in Angular

Forms with multiple steps are common in web applications, but they can be difficult to organize and manage the data. Using reactive forms in Angular, however, it can be less painful. In this article we'll take a look at one way to manage multi step forms in your Angular app.

For this article, we'll use an example form with multiple steps. This sample will have just two steps, but the principles here can be applied to forms with more steps as well. The first of our two steps gathers the name, address, and phone number of a user. The second step gets their email, username, and password. We'll talk about how to split the form up into two parts and how to submit the form when finished.

Multi Step Form Container

Before we make the component to manage the first step of the form, the step that gathers the personal information for the user, we need to create a container component which will hold the two form steps. This is where we will manage the data from the two subforms, as well as manage which step the user is on or wants to be on. Finally, we'll also submit the completed form from this component as well. So, for the first step, create a new component:

$ ng g c form-container

Inside the component, add the following content:

type Step = 'personalInfo' | 'loginInfo';

export class FormContainerComponent implements OnInit {
  private currentStepBs: BehaviorSubject<Step> = new BehaviorSubject<Step>('personalInfo');
  public currentStep$: Observable<Step> = this.currentStepBs.asObservable();

  public userForm: FormGroup;

  constructor(private _fb: FormBuilder) {}

  ngOnInit() {
    this.userForm = this._fb.group({
      personalInfo: null,
      loginInfo: null
    });
  }

  subformInitialized(name: string, group: FormGroup) {
    this.userForm.setControl(name, group);
  }

  changeStep(currentStep: string, direction: 'forward' | 'back') {
    switch(currentStep) {
      case 'personalInfoStep':
        if (direction === 'forward') {
          this.currentStepBs.next('loginInfo');
        }
        break;
      case 'loginInfoStep':
        if (direction === 'back') {
          this.currentStepBs.next('personalInfo');
        }
        break;
    }
  }

  submitForm() {
      const formValues = this.userForm.value;
      // submit the form with a service
  }
}

This is the majority of the component's class file. It's all pretty straightforward, but let's break it down real quick. First, a BehaviorSubject is created, currentStepBs to keep track of which form step to show. The type is declared right before the component class, and is called Step. The possible values are strings representing the two steps. If you have more than two steps, you can provide one string for each step. Next, the userForm variable is created, but not initialized. That is done in the ngOnInit method. In the method, the form is intialized as a FormGroup with two attributes, one for each step of the form. Their value is initially null, but that will be updated when the step components appear on the screen. Next up is a method, subformInitialized which will be called when a form step is created and appears on the page. The method sets the control on the main userForm. Next is a method that changes the current step. It is pretty simple; it calls the next method on the BehaviorSubject and passes in the correct step. The last method is where the actual form submission will be done. This is where the form value will be used and passed into a service method to submit the value of the form.

Before moving on to the next section, let's look at the component's HTML file briefly. There won't be a lot here, but it will be good to look at.

<ng-container [ngSwitch]="currentStep$ | async">
  <app-personal-info 
    *ngSwitchCase="'personalInfo'" 
    [startingForm]="userForm.get('personalInfo').value" 
    (subformInitialized)="subformInitialized('personalInfo', $event)" 
    (changeStep)="changeStep('personalInfoStep', $event)"
  ></app-personal-info>
  <app-login-info 
    *ngSwitchCase="'loginInfo'" 
    [startingForm]="userForm.get('loginInfo').value" 
    (subformInitialized)="subformInitialized('loginInfo', $event)" 
    (changeStep)="changeStep('loginInfoStep', 'back')"
    (submitForm)="submitForm()"
  ></app-login-info>
</ng-container>

The two components that manage each step of the form are on the page. Only one is visible at a time, and they each get a starting value (to show previously entered information if they go back a step), as well as have Outputs for the subform being initialized and changing steps. The second step also has a submitForm output.

Now that our container component is created and everything is set up, we can move on to creating the PersonalInfo component, which will contain the form to gather the personal info.

Individual Steps

Let's begin by creating the component for the personal info step:

$ ng g c personal-info

Next, let's look at the class file for the component.

export class PersonalInfoComponent implements OnInit {
  @Input() startingForm: FormGroup;
  @Output() subformInitialized: EventEmitter<FormGroup> = new EventEmitter<FormGroup>();
  @Output() changeStep: EventEmitter<boolean> = new EventEmitter<boolean>();
  public personalInfoForm: FormGroup;

  constructor(private _fb: FormBuilder) {}

  ngOnInit() {
    if (this.startingForm) {
      this.personalInfoForm = this.startingForm;
    } else {
      this.personalInfoForm = this._fb.group({
        firstName: '',
        lastName: '',
        // ... continue with the other fields
      })
    }

    this.subformInitialized.emit(this.personalInfoForm);
  }

  doChangeStep(direction: 'forward') {
    this.changeStep.emit(direction);
  }
}

This component is a lot more simple than the container component. A form is created in the ngOnInit method, either from the data that's passed in to the component or with the FormBuilder. After the form is created, its value is emitted to the parent so the form values can be tracked in the parent component. There's a doChangeStep method that will emit forward and move the user to the next step. The template of the component will just be the form inputs as well as a "Next" button.

We won't cover creating the login info component, but it will be the same as this component, but will only allow the user to go back, and will allow the user to click a submit button to take them on to the next portion of the app after they've filled out the forms.

Conclusion

This method of managing complex forms has worked out really well for us. By organizing the forms like this each step can use its own reactive form with form validation and all of the other benefits of reactive forms. The parent container controls all the data from each step automatically. When it's time to submit the form, each step's data is contained in a separate attribute of the form.value object on the parent component. We've been able to easily add custom validators to the forms (one of my favorite features of reactive forms) which would have been more difficult with template driven forms.

This is also just one way to manage multi step forms in Angular. I'm sure you can achieve the same result with template driven forms, or in other manners. This way has been pretty straightforward and not overly complex. Most importantly, it worked for our situation. Hopefully it helps you as well!

Original article source at: https://www.prestonlamb.com

#angular #step 

Create a Multi Step Forms in Angular
Gordon  Matlala

Gordon Matlala

1670585767

5 Killer steps to Change And Innovation

Introduction

ADAPTing Agile development, as the leading Agile speaker and author Mike Cohn describes “how one can iterate toward increased agility by combining a senior-level guiding coalition with multiple action teams.”

To successfully implement a change, senior leadership need to create a sense of urgency in their organization so that employees, lower-level managers and other impacted stakeholders are on board. It will help gain confidence and cooperation of the teams.

Following 5 steps are necessary for ADAPTing to Agile development:

  1. Awareness – That there is always a room for improvement in the current practices.
  2. Desire – To adapt to changes for improvement.
  3. Ability – To work with Agile mindset
  4. Promote – Early success so others can see and follow
  5. Transfer – The Agile implication and impact through put the organisation.

For successful Agile transition, it’s very important the each individual moves through Awareness, Desire and Ability stages. Using the Promote stage of Agile transition, early adopters of Agile can build awareness and desire amongst the late adopters of Agile. Lastly its utmost important to Transfer the implication and impact of Agile to other groups throughout the organisation like Project management office, sales, information technology, operations, hardware development; otherwise the Agile transition will fail inevitably.

Lets go through the wife stages for ADAPTing to Agile development

ADAPTing Agile – Awareness

When it comes to achieving desired results, it’s important to be aware of the current process / practise and that its efficiency can be improved. A change in approach begins with recognizing that the current approach has a room for improvement. However, it can be difficult to embrace that what worked in the past may not work in the present. One of the most common reasons people don’t become aware of the need to change is because they don’t have enough exposure to the big picture. In this Awareness stage, Rather than listing a variety of common project problems, focus on the two or three major problems that reflect the need for change.

Some Tools to Create awareness:

  • Communicate the issue
  • Use metrics to track progress
  • Provide new and different experiences.
  • Focus attention on the most important reasons to change.

ADAPTing Agile – Desire

There needs to be a strong desire to adopt Agile as a way to address the current problems. Not only must we recognize the need for change, but we must also have the desire to change. Many people find it difficult to move from the realization that the current development process isn’t working to the desire to use a different one.

Desire Tools:

  • Communicate There’s a Better Way
  • Create a sense of urgency
  • Build momentum
  • Get the team to use Agile for a test drive
  • Align incentives
  • Focus on addressing fears
  • Helping people let go
  • Don’t discredit the past

ADAPTing Agile – Ability

If you want to be successful, you need to be able to be agile. To be successful with Agile, team members must learn new skills and overcome old ones. Some of the larger challenges that Agile teams will face includes: Learning new technical skills, think and work as an Agile team and delivering working product frequently.

Ability Tools:

  • Coaching and training can help your employees improve their performance.
  • Hold individuals accountable for their actions.
  • Share your information with others.
  • Aim for goals that are reasonable and achievable.

ADAPTing Agile – Promote

Promote by sharing our experiences with Agile, we can help others remember and learn from our successes. By publicizing current successes with Agile, you’ll quickly start creating awareness for the next round of improvements. This stage also help teams maintain Agile behavior by sharing the successes that teams have achieved. Finally, publicizing Agile success help those not directly involved in adopting Agile to become more aware and interested in it.

Promote Tools:

  • Publicize the successes of agile teams and help to get the word out about how agile can help businesses achieve their goals.
  • Organize an Agile safari
  • Attract attention and interest from potential agile adopters.

Transfer

It is impossible for a development team to remain Agile without help from the rest of the company. If the impact of using Agile is not transferred to other departments, the organizational focus from those departments will eventually stall and stifle transition efforts. The rest of the organization must be able to work effectively with Agile.

There are other departments that must also be aware of the effects of Agile on their work. Many groups with an organizational gravity, such as the project management office, sales, information technology, operations, hardware development, and others, contribute to the success of a business. The impact of Agile for long-term success will be important to consider.

Visit Knolway to access more resources.

Original article source at: https://blog.knoldus.com/

#agile #step #change #innovation 

5 Killer steps to Change And Innovation
Mike  Kozey

Mike Kozey

1661392920

Step_indicator: Create Progressing Timeline Easier with Number Of Step

StepIndicator is a package that lets you create progressing timeline easier with number of steps and easy to customize.

Features

int count; double indicatorRadius; int currentStep; Color indicatorColor; Color activeIndicatorColor; double lineWidth; double lineHeight; double lineRadius; Color lineColor; Color activeLineColor; Color indicatorBorderColor; double indicatorBorderWidth; final TextStyle numberStyle; TextStyle activeNumberStyle; double lineSpacing; List stepTitles; bool enableStepTitle; TextStyle stepTitleStyle;

Usage

import 'package:step_indicator/step_indicator.dart';

Screenshot

Screenshot

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add step_indicator

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  step_indicator: ^0.0.1

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:step_indicator/step_indicator.dart';

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:step_indicator/step_indicator.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const StepIndicator(
      currentStep: 0,
      count: 4,
      activeIndicatorColor: Colors.blue,
      activeLineColor: Colors.blue,
      enableStepTitle: true,
      indicatorBorderWidth: 2,
      stepTitles: ["Step 1", "Step 2", "Step 3", "Step 4"],
      stepTitleStyle: TextStyle(
        color: Colors.black,
      ),
    );
  }
}

Download Details:

Author: Veasnawt
Source Code: https://github.com/veasnawt/step_indicator 
License: MIT license

#flutter #dart #step 

Step_indicator: Create Progressing Timeline Easier with Number Of Step
Rocio  O'Keefe

Rocio O'Keefe

1659578460

A Progress Widget for Showing A Proccess Completed Step By Step

Step_progress

A progress widget for showing a proccess completed step by step

StepProgress

Example One

import 'package:flutter/material.dart';
import 'package:step_progress/step_progress.dart';

class ExampleOne extends StatefulWidget {
  ExampleOne({Key? key}) : super(key: key);

  @override
  _ExampleOneState createState() => _ExampleOneState();
}

class _ExampleOneState extends State<ExampleOne> {
  final PageController _pageController = PageController();
  final StepProgressController _stepProgressController =
      StepProgressController(initialStep: 0, totalStep: 4);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Example One'),
      ),
      body: Column(children: [
        Progress(
          stepProgressController: _stepProgressController,
          strokeColor: Color(0xff04A7B8),
          valueColor: Colors.white,
          backgroundColor: Color(0xff04A7B5),
          tickColor: Color(0xff04A7B5),
          onStepChanged: (index) {
            print('on step changed: $index');
          },
        ),
        Expanded(
          child: PageView(
            controller: _pageController,
            children: [
              Container(
                color: Colors.white,
              ),
              Container(
                color: Colors.grey[100],
              ),
              Container(
                color: Colors.grey[300],
              ),
              Container(
                color: Colors.grey[500],
              ),
            ],
          ),
        ),
      ]),
      floatingActionButton: Align(
        alignment: Alignment.bottomCenter,
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            FloatingActionButton(
              onPressed: () {
                _pageController.previousPage(
                    duration: Duration(milliseconds: 300),
                    curve: Curves.easeInCubic);
                _stepProgressController.prevStep();
              },
              tooltip: 'Back',
              child: Text("Back"),
            ),
            SizedBox(
              width: 30,
            ),
            FloatingActionButton(
              onPressed: () {
                _pageController.nextPage(
                    duration: Duration(milliseconds: 300),
                    curve: Curves.easeInCubic);
                _stepProgressController.nextStep();
              },
              tooltip: 'Next',
              child: Text("Next"),
            ),
          ],
        ),
      ),
    );
  }
}

Example Two

import 'package:flutter/material.dart';
import 'package:step_progress/step_progress.dart';

class ExampleTwo extends StatefulWidget {
  ExampleTwo({Key? key}) : super(key: key);

  @override
  _ExampleTwoState createState() => _ExampleTwoState();
}

class _ExampleTwoState extends State<ExampleTwo> {
  final StepProgressController _stepProgressController =
  StepProgressController(totalStep: 10);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Example Two'),
        bottom: PreferredSize(
          preferredSize: Size(double.infinity, 50),
          child: Progress(
            stepProgressController: _stepProgressController,
          ),
        ),
      ),
      body: Container(
        child: Center(
          child: Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              ElevatedButton(
                  onPressed: () {
                    setState(() {
                      _stepProgressController.prevStep();
                    });
                  },
                  child: Text("Prev")),
              SizedBox(
                width: 30,
              ),
              ElevatedButton(
                  onPressed: () {
                    setState(() {
                      _stepProgressController.nextStep();
                    });
                  },
                  child: Text("Next")),
            ],
          ),
        ),
      ),
    );
  }
}

Installing

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add step_progress

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  step_progress: ^0.0.2

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:step_progress/step_progress.dart';

example/lib/main.dart

import 'package:flutter/material.dart';

import 'example_one/example_one.dart';
import 'example_two/example_two.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FlutterStepProgressDemo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            ElevatedButton(
                onPressed: () {
                  Navigator.push(
                      context, MaterialPageRoute(builder: (_) => ExampleOne()));
                },
                child: Text('Example One')),
            SizedBox(height: 20),
            ElevatedButton(
                onPressed: () {
                  Navigator.push(
                      context, MaterialPageRoute(builder: (_) => ExampleTwo()));
                },
                child: Text('Example Two')),
          ],
        ),
      ),
    );
  }
}

Author: TalebRafiepour
Source Code: https://github.com/TalebRafiepour/step-progress 
License: MIT license

#flutter #dart #step 

A Progress Widget for Showing A Proccess Completed Step By Step
Hermann  Frami

Hermann Frami

1656940560

Serverless Step Functions

Serverless Step Functions  

This is the Serverless Framework plugin for AWS Step Functions.

Requirement

Serverless Framework v2.32.0 or later is required.

Install

Run npm install in your Serverless project.

$ npm install --save-dev serverless-step-functions

Add the plugin to your serverless.yml file

plugins:
  - serverless-step-functions

Setup

Specify your state machine definition using Amazon States Language in a definition statement in serverless.yml. You can use CloudFormation intrinsic functions such as Ref and Fn::GetAtt to reference Lambda functions, SNS topics, SQS queues and DynamoDB tables declared in the same serverless.yml. Since Ref returns different things (ARN, ID, resource name, etc.) depending on the type of CloudFormation resource, please refer to this page to see whether you need to use Ref or Fn::GetAtt.

Alternatively, you can also provide the raw ARN, or SQS queue URL, or DynamoDB table name as a string. If you need to construct the ARN by hand, then we recommend to use the serverless-pseudo-parameters plugin together to make your life easier.

In addition, if you want to reference a DynamoDB table managed by an external CloudFormation Stack, as long as that table name is exported as an output from that stack, it can be referenced by importing it using Fn::ImportValue. See the ddbtablestepfunc Step Function definition below for an example.

functions:
  hello:
    handler: handler.hello

stepFunctions:
  stateMachines:
    hellostepfunc1:
      events:
        - http:
            path: gofunction
            method: GET
        - schedule:
            rate: rate(10 minutes)
            enabled: true
            input:
              key1: value1
              key2: value2
              stageParams:
                stage: dev
      name: myStateMachine
      definition:
        Comment: "A Hello World example of the Amazon States Language using an AWS Lambda Function"
        StartAt: HelloWorld1
        States:
          HelloWorld1:
            Type: Task
            Resource:
              Fn::GetAtt: [hello, Arn]
            End: true
      dependsOn: CustomIamRole
      tags:
        Team: Atlantis
      alarms:
        topics:
          ok: arn:aws:sns:us-east-1:1234567890:NotifyMe
          alarm: arn:aws:sns:us-east-1:1234567890:NotifyMe
          insufficientData: arn:aws:sns:us-east-1:1234567890:NotifyMe
        metrics:
          - executionsTimedOut
          - executionsFailed
          - executionsAborted
          - metric: executionThrottled
            treatMissingData: breaching # overrides below default
          - executionsSucceeded
        treatMissingData: ignore # optional
    hellostepfunc2:
      definition:
        StartAt: HelloWorld2
        States:
          HelloWorld2:
            Type: Task
            Resource:
              Fn::GetAtt: [hello, Arn]
            End: true
    ddbtablestepfunc:
      definition:
        Comment: Demonstrates how to reference a DynamoDB Table Name exported from an external CloudFormation Stack
        StartAt: ImportDDBTableName
        States:
          ImportDDBTableName:
            Type: Task
            Resource: "arn:aws:states:::dynamodb:updateItem"
            Parameters:
              TableName:
                Fn::ImportValue: MyExternalStack:ToDoTable:Name # imports a table name from an external stack
              Key:
                id:
                  S.$: "$.todoId"
              UpdateExpression: "SET #status = :updatedStatus"
              ExpressionAttributeNames:
                "#status": status
              ExpressionAttributeValues:
                ":updatedStatus":
                  S: DONE
            End: true
      dependsOn:
        - DynamoDBTable
        - KinesisStream
        - CustomIamRole
      tags:
        Team: Atlantis
  activities:
    - myTask
    - yourTask
  validate: true # enable pre-deployment definition validation (disabled by default)

plugins:
  - serverless-step-functions
  - serverless-pseudo-parameters

In the example above, notice that we used Fn::GetAtt: [hello, Arn] to get the ARN for the hello function defined earlier. This means you don't have to know how the Serverless framework converts these local names to CloudFormation logical IDs (e.g. hello-world becomes HelloDashworldLambdaFunction).

However, if you prefer to work with logical IDs, you can. You can also express the above Fn::GetAtt function as Fn::GetAtt: [HelloLambdaFunction, Arn]. If you're unfamiliar with the convention the Serverless framework uses, then the easiest thing to do is to first run sls package then look in the .serverless folder for the generated CloudFormation template. Here you can find the logical resource names for the functions you want to reference.

Adding a custom name for a stateMachine

In case you need to interpolate a specific stage or service layer variable as the stateMachines name you can add a name property to your yaml.

service: messager

functions:
  sendMessage:
    handler: handler.sendMessage

stepFunctions:
  stateMachines:
    sendMessageFunc:
      name: sendMessageFunc-${self:custom.service}-${opt:stage}
      definition:
        <your definition>

plugins:
  - serverless-step-functions

Adding a custom logical id for a stateMachine

You can use a custom logical id that is only unique within the stack as opposed to the name that needs to be unique globally. This can make referencing the state machine easier/simpler because you don't have to duplicate the interpolation logic everywhere you reference the state machine.

service: messager

functions:
  sendMessage:
    handler: handler.sendMessage

stepFunctions:
  stateMachines:
    sendMessageFunc:
      id: SendMessageStateMachine
      name: sendMessageFunc-${self:custom.service}-${opt:stage}
      definition:
        <your definition>

plugins:
  - serverless-step-functions

You can then Ref: SendMessageStateMachine in various parts of CloudFormation or serverless.yml

Depending on another logical id

If your state machine depends on another resource defined in your serverless.yml then you can add a dependsOn field to the state machine definition. This would add the DependsOnclause to the generated CloudFormation template.

This dependsOn field can be either a string, or an array of strings.

stepFunctions:
  stateMachines:
    myStateMachine:
      dependsOn: myDB

    myOtherStateMachine:
      dependsOn:
        - myOtherDB
        - myStream

Adding retain property for a stateMachine

There are some practical cases when you would like to prevent state machine from deletion on stack delete or update. This can be achieved by adding retain property to the state machine section.

stepFunctions:
  stateMachines:
    myStateMachine:
      retain: true

Configuring in such way adds "DeletionPolicy" : "Retain" to the state machine within CloudFormation template.

CloudWatch Alarms

It's common practice to want to monitor the health of your state machines and be alerted when something goes wrong. You can either:

  • do this using the serverless-plugin-aws-alerts, which lets you configure custom CloudWatch Alarms against the various metrics that Step Functions publishes.
  • or, you can use the built-in alarms configuration from this plugin, which gives you an opinionated set of default alarms (see below)
stepFunctions:
  stateMachines:
    myStateMachine:
      alarms:
        topics:
          ok: arn:aws:sns:us-east-1:1234567890:NotifyMe
          alarm: arn:aws:sns:us-east-1:1234567890:NotifyMe
          insufficientData: arn:aws:sns:us-east-1:1234567890:NotifyMe
        metrics:
          - executionsTimedOut
          - executionsFailed
          - executionsAborted
          - executionThrottled
          - executionsSucceeded
        treatMissingData: missing

Both topics and metrics are required properties. There are 4 supported metrics, each map to the CloudWatch Metrics that Step Functions publishes for your executions.

You can configure how the CloudWatch Alarms should treat missing data:

  • missing (AWS default): The alarm does not consider missing data points when evaluating whether to change state.
  • ignore: The current alarm state is maintained.
  • breaching: Missing data points are treated as breaching the threshold.
  • notBreaching: Missing data points are treated as being within the threshold.

For more information, please refer to the official documentation.

The generated CloudWatch alarms would have the following configurations:

namespace: 'AWS/States'
metric: <ExecutionsTimedOut | ExecutionsFailed | ExecutionsAborted | ExecutionThrottled>
threshold: 1
period: 60
evaluationPeriods: 1
ComparisonOperator: GreaterThanOrEqualToThreshold
Statistic: Sum
treatMissingData: <missing (default) | ignore | breaching | notBreaching>
Dimensions:
  - Name: StateMachineArn
    Value: <ArnOfTheStateMachine>

You can also override the default treatMissingData setting for a particular alarm by specifying an override:

alarms:
  topics:
    ok: arn:aws:sns:us-east-1:1234567890:NotifyMe
    alarm: arn:aws:sns:us-east-1:1234567890:NotifyMe
    insufficientData: arn:aws:sns:us-east-1:1234567890:NotifyMe
  metrics:
    - executionsTimedOut
    - executionsFailed
    - executionsAborted
    - metric: executionThrottled
      treatMissingData: breaching # override
    - executionsSucceeded
  treatMissingData: ignore # default

Custom CloudWatch Alarm names

By default, the CloudFormation assigns names to the alarms based on the CloudFormation stack and the resource logical Id, and in some cases and these names could be confusing.

To use custom names to the alarms add nameTemplate property in the alarms object.

example:

service: myservice

plugins:
  - serverless-step-functions

stepFunctions:
  stateMachines:
    main-workflow:
      name: main
      alarms:
        nameTemplate: $[stateMachineName]-$[cloudWatchMetricName]-alarm
        topics:
          alarm: !Ref AwsAlertsGenericAlarmTopicAlarm
        metrics:
          - executionsFailed
          - executionsAborted
          - executionsTimedOut
          - executionThrottled
        treatMissingData: ignore
      definition: ${file(./step-functions/main.asl.yaml)}

Supported variables to the nameTemplate property:

  • stateMachineName
  • metricName
  • cloudWatchMetricName

Per-Metric Alarm Name

To overwrite the alarm name for a specific metric, add the alarmName property in the metric object.

service: myservice

plugins:
  - serverless-step-functions

stepFunctions:
  stateMachines:
    main-workflow:
      name: main
      alarms:
        nameTemplate: $[stateMachineName]-$[cloudWatchMetricName]-alarm
        topics:
          alarm: !Ref AwsAlertsGenericAlarmTopicAlarm
        metrics:
          - metric: executionsFailed
            alarmName: mycustom-name-${self:stage.region}-Failed-alarm
          - executionsAborted
          - executionsTimedOut
          - executionThrottled
        treatMissingData: ignore
      definition: ${file(./step-functions/main.asl.yaml)}

CloudWatch Notifications

You can monitor the execution state of your state machines via CloudWatch Events. It allows you to be alerted when the status of your state machine changes to ABORTED, FAILED, RUNNING, SUCCEEDED or TIMED_OUT.

You can configure CloudWatch Events to send notification to a number of targets. Currently this plugin supports sns, sqs, kinesis, firehose, lambda and stepFunctions.

To configure status change notifications to your state machine, you can add a notifications like below:

stepFunctions:
  stateMachines:
    hellostepfunc1:
      name: test
      definition:
        ...
      notifications:
        ABORTED:
          - sns: SNS_TOPIC_ARN
          - sqs: SQS_TOPIC_ARN
          - sqs: # for FIFO queues, which requires you to configure the message group ID
              arn: SQS_TOPIC_ARN
              messageGroupId: 12345
          - lambda: LAMBDA_FUNCTION_ARN
          - kinesis: KINESIS_STREAM_ARN
          - kinesis:
               arn: KINESIS_STREAM_ARN
               partitionKeyPath: $.id # used to choose the parition key from payload
          - firehose: FIREHOSE_STREAM_ARN
          - stepFunctions: STATE_MACHINE_ARN
        FAILED:
          ... # same as above
        ... # other status

As you can see from the above example, you can configure different notification targets for each type of status change. If you want to configure the same targets for multiple status changes, then consider using YML anchors to keep your YML succinct.

CloudFormation intrinsic functions such as Ref and Fn::GetAtt are supported.

When setting up a notification target against a FIFO SQS queue, the queue must enable the content-based deduplication option and you must configure the messageGroupId.

Blue green deployment

To implement a blue-green deployment with Step Functions you need to reference the exact versions of the functions.

To do this, you can specify useExactVersion: true in the state machine.

stepFunctions:
  stateMachines:
    hellostepfunc1:
      useExactVersion: true
      definition:
        ...

Pre-deployment validation

By default, your state machine definition will be validated during deployment by StepFunctions. This can be cumbersome when developing because you have to upload your service for every typo in your definition. In order to go faster, you can enable pre-deployment validation using asl-validator which should detect most of the issues (like a missing state property).

stepFunctions:
  validate: true

Disable Output Cloudformation Outputs section

Disables the generation of outputs in the CloudFormation Outputs section. If you define many state machines in serverless.yml you may reach the CloudFormation limit of 60 outputs. If you define noOutput: true then this plugin will not generate outputs automatically.

stepFunctions:
  noOutput: true

Express Workflow

At re:invent 2019, AWS introduced Express Workflows as a cheaper, more scalable alternative (but with a cut-down set of features). See this page for differences between standard and express workflows.

To declare an express workflow, specify type as EXPRESS and you can specify the logging configuration:

stepFunctions:
  stateMachines:
    hellostepfunc1:
      type: EXPRESS
      loggingConfig:
        level: ERROR
        includeExecutionData: true
        destinations:
          - Fn::GetAtt: [MyLogGroup, Arn]

CloudWatch Logs

You can enable CloudWatch Logs for standard Step Functions, the syntax is exactly like with Express Workflows.

stepFunctions:
  stateMachines:
    hellostepfunc1:
      loggingConfig:
        level: ERROR
        includeExecutionData: true
        destinations:
          - Fn::GetAtt: [MyLogGroup, Arn]

X-Ray

You can enable X-Ray for your state machine, specify tracingConfig as shown below.

stepFunctions:
  stateMachines:
    hellostepfunc1:
      tracingConfig:
        enabled: true

Current Gotcha

Please keep this gotcha in mind if you want to reference the name from the resources section. To generate Logical ID for CloudFormation, the plugin transforms the specified name in serverless.yml based on the following scheme.

  • Transform a leading character into uppercase
  • Transform - into Dash
  • Transform _ into Underscore

If you want to use variables system in name statement, you can't put the variables as a prefix like this:${self:service}-${opt:stage}-myStateMachine since the variables are transformed within Output section, as a result, the reference will be broken.

The correct sample is here.

stepFunctions:
  stateMachines:
    myStateMachine:
      name: myStateMachine-${self:service}-${opt:stage}
...

resources:
  Outputs:
    myStateMachine:
      Value:
        Ref: MyStateMachineDash${self:service}Dash${opt:stage}

Events

API Gateway

To create HTTP endpoints as Event sources for your StepFunctions statemachine

Simple HTTP Endpoint

This setup specifies that the hello state machine should be run when someone accesses the API gateway at hello via a GET request.

Here's an example:

stepFunctions:
  stateMachines:
    hello:
      events:
        - http:
            path: hello
            method: GET
      definition:

Here You can define an POST endpoint for the path posts/create.

stepFunctions:
  stateMachines:
    hello:
      events:
        - http:
            path: posts/create
            method: POST
      definition:

Custom Step Functions Action

Step Functions have custom actions like DescribeExecution or StopExecution to fetch and control them. You can use custom actions like this:

stepFunctions:
  stateMachines:
    start:
      events:
        - http:
            path: action/start
            method: POST
      definition:
        ...
    status:
      events:
        - http:
            path: action/status
            method: POST
            action: DescribeExecution
      definition:
        ...
    stop:
      events:
        - http:
            path: action/stop
            method: POST
            action: StopExecution
      definition:
        ...

Request template is not used when action is set because there're a bunch of actions. However if you want to use request template you can use Customizing request body mapping templates.

HTTP Endpoint with custom IAM Role

The plugin would generate an IAM Role for you by default. However, if you wish to use an IAM role that you have provisioned separately, then you can override the IAM Role like this:

stepFunctions:
  stateMachines:
    hello:
      events:
        - http:
            path: posts/create
            method: POST
            iamRole: arn:aws:iam::<accountId>:role/<roleName>
      definition:

Share API Gateway and API Resources

You can share the same API Gateway between multiple projects by referencing its REST API ID and Root Resource ID in serverless.yml as follows:

service: service-name
provider:
  name: aws
  apiGateway:
    # REST API resource ID. Default is generated by the framework
    restApiId: xxxxxxxxxx
    # Root resource, represent as / path
    restApiRootResourceId: xxxxxxxxxx

functions:
  ...

If your application has many nested paths, you might also want to break them out into smaller services.

However, Cloudformation will throw an error if we try to generate an existing path resource. To avoid that, we reference the resource ID:

service: service-a
provider:
  apiGateway:
    restApiId: xxxxxxxxxx
    restApiRootResourceId: xxxxxxxxxx
    # List of existing resources that were created in the REST API. This is required or the stack will be conflicted
    restApiResources:
      /users: xxxxxxxxxx

functions:
  ...

Now we can define endpoints using existing API Gateway ressources

stepFunctions:
  stateMachines:
    hello:
      events:
        - http:
            path: users/create
            method: POST

Enabling CORS

To set CORS configurations for your HTTP endpoints, simply modify your event configurations as follows:

stepFunctions:
  stateMachines:
    hello:
      events:
        - http:
            path: posts/create
            method: POST
            cors: true
      definition:

Setting cors to true assumes a default configuration which is equivalent to:

stepFunctions:
  stateMachines:
    hello:
      events:
        - http:
            path: posts/create
            method: POST
            cors:
              origin: '*'
              headers:
                - Content-Type
                - X-Amz-Date
                - Authorization
                - X-Api-Key
                - X-Amz-Security-Token
                - X-Amz-User-Agent
              allowCredentials: false
      definition:

Configuring the cors property sets Access-Control-Allow-Origin, Access-Control-Allow-Headers, Access-Control-Allow-Methods,Access-Control-Allow-Credentials headers in the CORS preflight response. To enable the Access-Control-Max-Age preflight response header, set the maxAge property in the cors object:

stepFunctions:
  stateMachines:
    SfnApiGateway:
      events:
        - http:
            path: /playground/start
            method: post
            cors:
              origin: '*'
              maxAge: 86400

HTTP Endpoints with AWS_IAM Authorizers

If you want to require that the caller submit the IAM user's access keys in order to be authenticated to invoke your Lambda Function, set the authorizer to AWS_IAM as shown in the following example:

stepFunctions:
  stateMachines:
    hello:
      events:
        - http:
            path: posts/create
            method: POST
            authorizer: aws_iam
      definition:

HTTP Endpoints with Custom Authorizers

Custom Authorizers allow you to run an AWS Lambda Function before your targeted AWS Lambda Function. This is useful for Microservice Architectures or when you simply want to do some Authorization before running your business logic.

You can enable Custom Authorizers for your HTTP endpoint by setting the Authorizer in your http event to another function in the same service, as shown in the following example:

stepFunctions:
  stateMachines:
    hello:
      - http:
          path: posts/create
          method: post
          authorizer: authorizerFunc
      definition:

If the Authorizer function does not exist in your service but exists in AWS, you can provide the ARN of the Lambda function instead of the function name, as shown in the following example:

stepFunctions:
  stateMachines:
    hello:
      - http:
          path: posts/create
          method: post
          authorizer: xxx:xxx:Lambda-Name
      definition:

Shared Authorizer

Auto-created Authorizer is convenient for conventional setup. However, when you need to define your custom Authorizer, or use COGNITO_USER_POOLS authorizer with shared API Gateway, it is painful because of AWS limitation. Sharing Authorizer is a better way to do.

stepFunctions:
  stateMachines:
    createUser:
      ...
      events:
        - http:
            path: /users
            ...
            authorizer:
              # Provide both type and authorizerId
              type: COGNITO_USER_POOLS # TOKEN, CUSTOM or COGNITO_USER_POOLS, same as AWS Cloudformation documentation
              authorizerId:
                Ref: ApiGatewayAuthorizer  # or hard-code Authorizer ID
              # [Optional] you can also specify the OAuth scopes for Cognito
              scopes:
                - scope1
                ...

LAMBDA_PROXY request template

The plugin generates default body mapping templates for application/json and application/x-www-form-urlencoded content types. The default template would pass the request body as input to the state machine. If you need access to other contextual information about the HTTP request such as headers, path parameters, etc. then you can also use the lambda_proxy request template like this:

stepFunctions:
  stateMachines:
    hello:
      events:
        - http:
            path: posts/create
            method: POST
            request:
              template: lambda_proxy

This would generate the normal LAMBDA_PROXY template used for API Gateway integration with Lambda functions.

Customizing request body mapping templates

If you'd like to add content types or customize the default templates, you can do so by including your custom API Gateway request mapping template in serverless.yml like so:

stepFunctions:
  stateMachines:
    hello:
      events:
        - http:
            path: posts/create
            method: POST
            request:
              template:
                application/json: |
                  #set( $body = $util.escapeJavaScript($input.json('$')) )
                  #set( $name = $util.escapeJavaScript($input.json('$.data.attributes.order_id')) )
                  {
                    "input": "$body",
                    "name": "$name",
                    "stateMachineArn":"arn:aws:states:#{AWS::Region}:#{AWS::AccountId}:stateMachine:processOrderFlow-${opt:stage}"
                  }
      name: processOrderFlow-${opt:stage}
      definition:

Customizing response headers and templates

If you'd like to add custom headers in the HTTP response, or customize the default response template (which just returns the response from Step Function's StartExecution API), then you can do so by including your custom headers and API Gateway response mapping template in serverless.yml like so:

stepFunctions:
  stateMachines:
    hello:
      events:
        - http:
            path: posts/create
            method: POST
            response:
              headers:
                Content-Type: "'application/json'"
                X-Application-Id: "'my-app'"
              template:
                application/json: |
                  {
                    "status": 200,
                    "info": "OK"
                  }
      definition:

Send request to an API

You can input an value as json in request body, the value is passed as the input value of your statemachine

$ curl -XPOST https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/posts/create -d '{"foo":"bar"}'

Setting API keys for your Rest API

You can specify a list of API keys to be used by your service Rest API by adding an apiKeys array property to the provider object in serverless.yml. You'll also need to explicitly specify which endpoints are private and require one of the api keys to be included in the request by adding a private boolean property to the http event object you want to set as private. API Keys are created globally, so if you want to deploy your service to different stages make sure your API key contains a stage variable as defined below. When using API keys, you can optionally define usage plan quota and throttle, using usagePlan object.

Here's an example configuration for setting API keys for your service Rest API:

service: my-service
provider:
  name: aws
  apiKeys:
    - myFirstKey
    - ${opt:stage}-myFirstKey
    - ${env:MY_API_KEY} # you can hide it in a serverless variable
  usagePlan:
    quota:
      limit: 5000
      offset: 2
      period: MONTH
    throttle:
      burstLimit: 200
      rateLimit: 100
functions:
  hello:
    handler: handler.hello

    stepFunctions:
      stateMachines:
        statemachine1:
          name: ${self:service}-${opt:stage}-statemachine1
          events:
            - http:
                path: /hello
                method: post
                private: true
          definition:
            Comment: "A Hello World example of the Amazon States Language using an AWS Lambda Function"
            StartAt: HelloWorld1
            States:
              HelloWorld1:
                Type: Task
                Resource:
                  Fn::GetAtt: [hello, Arn]
                End: true


    plugins:
      - serverless-step-functions
      - serverless-pseudo-parameters

Please note that those are the API keys names, not the actual values. Once you deploy your service, the value of those API keys will be auto generated by AWS and printed on the screen for you to use. The values can be concealed from the output with the --conceal deploy option.

Clients connecting to this Rest API will then need to set any of these API keys values in the x-api-key header of their request. This is only necessary for functions where the private property is set to true.

Schedule

The following config will attach a schedule event and causes the stateMachine crawl to be called every 2 hours. The configuration allows you to attach multiple schedules to the same stateMachine. You can either use the rate or cron syntax. Take a look at the AWS schedule syntax documentation for more details.

stepFunctions:
  stateMachines:
    crawl:
      events:
        - schedule: rate(2 hours)
        - schedule: cron(0 12 * * ? *)
      definition:

Enabling / Disabling

Note: schedule events are enabled by default.

This will create and attach a schedule event for the aggregate stateMachine which is disabled. If enabled it will call the aggregate stateMachine every 10 minutes.

stepFunctions:
  stateMachines:
    aggregate:
      events:
        - schedule:
            rate: rate(10 minutes)
            enabled: false
            input:
              key1: value1
              key2: value2
              stageParams:
                stage: dev
        - schedule:
            rate: cron(0 12 * * ? *)
            enabled: false
            inputPath: '$.stageVariables'

Specify Name and Description

Name and Description can be specified for a schedule event. These are not required properties.

events:
  - schedule:
      name: your-scheduled-rate-event-name
      description: 'your scheduled rate event description'
      rate: rate(2 hours)

Scheduled Events IAM Role

By default, the plugin will create a new IAM role that allows AWS Events to start your state machine. Note that this role is different than the role assumed by the state machine. You can specify your own role instead (it must allow events.amazonaws.com to assume it, and it must be able to run states:StartExecution on your state machine):

events:
  - schedule:
      rate: rate(2 hours)
      role: arn:aws:iam::xxxxxxxx:role/yourRole

Specify InputTransformer

You can specify input values ​​to the Lambda function.

stepFunctions:
  stateMachines:
    stateMachineScheduled:
      events:
        - schedule: 
            rate: cron(30 12 ? * 1-5 *)
            inputTransformer:
              inputPathsMap: 
                time: '$.time'
                stage: '$.stageVariables'
              inputTemplate: '{"time": <time>, "stage" : <stage> }'
      definition:
        ...

CloudWatch Event / EventBridge

Simple event definition

This will enable your Statemachine to be called by an EC2 event rule. Please check the page of Event Types for CloudWatch Events.

stepFunctions:
  stateMachines:
    first:
      events:
        - cloudwatchEvent:
            event:
              source:
                - "aws.ec2"
              detail-type:
                - "EC2 Instance State-change Notification"
              detail:
                state:
                  - pending
      definition:
        ...

You can alternatively use EventBridge:

stepFunctions:
  stateMachines:
    first:
      events:
        - eventBridge:
            event:
              source:
                - "aws.ec2"
              detail-type:
                - "EC2 Instance State-change Notification"
              detail:
                state:
                  - pending
      definition:
        ...

All the configurations in this section applies to both cloudwatchEvent and eventBridge.

Enabling / Disabling

Note: cloudwatchEvent and eventBridge events are enabled by default.

This will create and attach a disabled cloudwatchEvent event for the myCloudWatch statemachine.

stepFunctions:
  stateMachines:
    cloudwatchEvent:
      events:
        - cloudwatchEvent:
            event:
              source:
                - "aws.ec2"
              detail-type:
                - "EC2 Instance State-change Notification"
              detail:
                state:
                  - pending
            enabled: false
      definition:
        ...

Specify Input or Inputpath or InputTransformer

You can specify input values ​​to the Lambda function.

stepFunctions:
  stateMachines:
    cloudwatchEvent:
      events:
        - cloudwatchEvent:
            event:
              source:
                - "aws.ec2"
              detail-type:
                - "EC2 Instance State-change Notification"
              detail:
                state:
                  - pending
            input:
              key1: value1
              key2: value2
              stageParams:
                stage: dev
        - cloudwatchEvent:
            event:
              source:
                - "aws.ec2"
              detail-type:
                - "EC2 Instance State-change Notification"
              detail:
                state:
                  - pending
            inputPath: '$.stageVariables'
        - cloudwatchEvent:
            event:
              source:
                - "aws.ec2"
              detail-type:
                - "EC2 Instance State-change Notification"
              detail:
                state:
                  - pending
            inputTransformer:
              inputPathsMap:
                stage: '$.stageVariables'
              inputTemplate: '{ "stage": <stage> }'
      definition:
        ...

Specifying a Description

You can also specify a CloudWatch Event description.

stepFunctions:
  stateMachines:
    cloudwatchEvent:
      events:
        - cloudwatchEvent:
            description: 'CloudWatch Event triggered on EC2 Instance pending state'
            event:
              source:
                - "aws.ec2"
              detail-type:
                - "EC2 Instance State-change Notification"
              detail:
                state:
                  - pending
      definition:
        ...

Specifying a Name

You can also specify a CloudWatch Event name. Keep in mind that the name must begin with a letter; contain only ASCII letters, digits, and hyphens; and not end with a hyphen or contain two consecutive hyphens. More infomation here.

stepFunctions:
  stateMachines:
    cloudwatchEvent:
      events:
        - cloudwatchEvent:
            name: 'my-cloudwatch-event-name'
            event:
              source:
                - "aws.ec2"
              detail-type:
                - "EC2 Instance State-change Notification"
              detail:
                state:
                  - pending
      definition:
        ...

Specifying a RoleArn

You can also specify a CloudWatch Event RoleArn. The Amazon Resource Name (ARN) of the role that is used for target invocation.

Required: No

stepFunctions:
  stateMachines:
    cloudwatchEvent:
      events:
        - cloudwatchEvent:
            name: 'my-cloudwatch-event-name'
            iamRole: 'arn:aws:iam::012345678910:role/Events-InvokeStepFunctions-Role'
            event:
              source:
                - "aws.ec2"
              detail-type:
                - "EC2 Instance State-change Notification"
              detail:
                state:
                  - pending
      definition:
        ...

Specifying a custom CloudWatch EventBus

You can choose which CloudWatch Event bus:

stepFunctions:
  stateMachines:
    exampleCloudwatchEventStartsMachine:
      events:
        - cloudwatchEvent:
            eventBusName: 'my-custom-event-bus'
            event:
              source:
                - "my.custom.source"
              detail-type:
                - "My Event Type"
              detail:
                state:
                  - pending
      definition:
        ...

Specifying a custom EventBridge EventBus

You can choose which EventBridge Event bus:

stepFunctions:
  stateMachines:
    exampleEventBridgeEventStartsMachine:
      events:
        - eventBridge:
            eventBusName: 'my-custom-event-bus'
            event:
              source:
                - "my.custom.source"
              detail-type:
                - "My Event Type"
              detail:
                state:
                  - pending
      definition:
        ...

Specifying a DeadLetterQueue

You can configure a target queue to send dead-letter queue events to:

stepFunctions:
  stateMachines:
    exampleEventBridgeEventStartsMachine:
      events:
        - eventBridge:
            eventBusName: 'my-custom-event-bus'
            event:
              source:
                - "my.custom.source"
              detail-type:
                - "My Event Type"
              detail:
                state:
                  - pending
            deadLetterConfig: 'arn:aws:sqs:us-east-1:012345678910:my-dlq' # SQS Arn
      definition:
        ...

Important point

Don't forget to Grant permissions to the dead-letter queue, to do that you may need to have the ARN of the generated EventBridge Rule.

In order to get the ARN you can use intrinsic functions against the logicalId, this plugin generates logicalIds following this format:

`${StateMachineName}EventsRuleCloudWatchEvent${index}`

Given this example 👇

stepFunctions:
  stateMachines:
    hellostepfunc1: # <---- StateMachineName
      events:
        - eventBridge:
            eventBusName: 'my-custom-event-bus'
            event:
              source:
                - "my.custom.source"
        - eventBridge:
            eventBusName: 'my-custom-event-bus'
            event:
              source:
                - "my.custom.source"
            deadLetterConfig: 'arn:aws:sqs:us-east-1:012345678910:my-dlq'
      name: myStateMachine
      definition:
        Comment: "A Hello World example of the Amazon States Language using an AWS Lambda Function"
        StartAt: HelloWorld1
        States:
          HelloWorld1:
            Type: Task
            Resource:
              Fn::GetAtt: [hello, Arn]
            End: true

Then

# to get the Arn of the 1st EventBridge rule
!GetAtt Hellostepfunc1EventsRuleCloudWatchEvent1.Arn

# to get the Arn of the 2nd EventBridge rule
!GetAtt Hellostepfunc1EventsRuleCloudWatchEvent2.Arn

Tags

You can specify tags on each state machine. Additionally any global tags (specified under provider section in your serverless.yml) would be merged in as well.

If you don't want for global tags to be merged into your state machine, you can include the inheritGlobalTags property for your state machine.

provider:
  tags:
    app: myApp
    department: engineering
stepFunctions:
  stateMachines:
    hellostepfunc1:
      name: myStateMachine
      inheritGlobalTags: false
      tags:
        score: 42
      definition: something

As a result, hellostepfunc1 will only have the tag of score: 42, and not the tags at the provider level

Commands

deploy

Run sls deploy, the defined Stepfunctions are deployed.

invoke

$ sls invoke stepf --name <stepfunctionname> --data '{"foo":"bar"}'

options

  • --name or -n The name of the step function in your service that you want to invoke. Required.
  • --stage or -s The stage in your service you want to invoke your step function.
  • --region or -r The region in your stage that you want to invoke your step function.
  • --data or -d String data to be passed as an event to your step function.
  • --path or -p The path to a json file with input data to be passed to the invoked step function.

IAM Role

The IAM roles required to run Statemachine are automatically generated for each state machine in the serverless.yml, with the IAM role name of StatesExecutionPolicy-<environment>. These roles are tailored to the services that the state machine integrates with, for example with Lambda the InvokeFunction is applied. You can also specify a custom ARN directly to the step functions lambda.

Here's an example:

stepFunctions:
  stateMachines:
    hello:
      role: arn:aws:iam::xxxxxxxx:role/yourRole
      definition:

It is also possible to use the CloudFormation intrinsic functions to reference resources from elsewhere. This allows for an IAM role to be created, and applied to the state machines all within the serverless file.

The below example shows the policy needed if your step function needs the ability to send a message to an sqs queue. To apply the role either the RoleName can be used as a reference in the state machine, or the role ARN can be used like in the example above. It is important to note that if you want to store your state machine role at a certain path, this must be specified on the Path property on the new role.

stepFunctions:
  stateMachines:
    hello:
      role:
        Fn::GetAtt: ["StateMachineRole", "Arn"]
      definition:
        ...

resources:
  Resources:
    StateMachineRole:
      Type: AWS::IAM::Role
      Properties:
        RoleName: RoleName
        Path: /path_of_state_machine_roles/
        AssumeRolePolicyDocument:
          Statement:
          - Effect: Allow
            Principal:
              Service:
                - states.amazonaws.com
            Action:
              - sts:AssumeRole
        Policies:
          - PolicyName: statePolicy
            PolicyDocument:
              Version: "2012-10-17"
              Statement:
                - Effect: Allow
                  Action:
                    - lambda:InvokeFunction
                  Resource:
                    - arn:aws:lambda:lambdaName
                - Effect: Allow
                  Action:
                    - sqs:SendMessage
                  Resource:
                    - arn:aws:sqs::xxxxxxxx:queueName

The short form of the intrinsic functions (i.e. !Sub, !Ref) is not supported at the moment.

Tips

How to specify the stateMachine ARN to environment variables

Here is serverless.yml sample to specify the stateMachine ARN to environment variables. This makes it possible to trigger your statemachine through Lambda events

functions:
  hello:
    handler: handler.hello
    environment:
      statemachine_arn: ${self:resources.Outputs.MyStateMachine.Value}

stepFunctions:
  stateMachines:
    hellostepfunc:
      name: myStateMachine
      definition:
        <your definition>

resources:
  Outputs:
    MyStateMachine:
      Description: The ARN of the example state machine
      Value:
        Ref: MyStateMachine

plugins:
  - serverless-step-functions

How to split up state machines into files

When you have a large serverless project with lots of state machines your serverless.yml file can grow to a point where it is unmaintainable.

You can split step functions into external files and import them into your serverless.yml file.

There are two ways you can do this:

Single external file

You can define the entire stateMachines block in a separate file and import it in its entirety.

includes/state-machines.yml:

stateMachines:
  hellostepfunc1:
    name: myStateMachine1
    definition:
      <your definition>
  hellostepfunc2:
    name: myStateMachine2
    definition:
      <your definition>

serverless.yml:

stepFunctions:
  ${file(includes/state-machines.yml)}

plugins:
  - serverless-step-functions

Separate Files

You can split up the stateMachines block into separate files.

includes/state-machine-1.yml:

name: myStateMachine1
definition:
  <your definition>

includes/state-machine-2.yml:

name: myStateMachine2
definition:
  <your definition>

serverless.yml:

stepFunctions:
  stateMachines:
    hellostepfunc1:
      ${file(includes/state-machine-1.yml)}
    hellostepfunc2:
      ${file(includes/state-machine-2.yml)}

plugins:
  - serverless-step-functions

Sample statemachines setting in serverless.yml

Wait State

functions:
  hello:
    handler: handler.hello

stepFunctions:
  stateMachines:
    yourWateMachine:
      definition:
        Comment: "An example of the Amazon States Language using wait states"
        StartAt: FirstState
        States:
          FirstState:
            Type: Task
            Resource:
              Fn::GetAtt: [hello, Arn]
            Next: wait_using_seconds
          wait_using_seconds:
            Type: Wait
            Seconds: 10
            Next: wait_using_timestamp
          wait_using_timestamp:
            Type: Wait
            Timestamp: '2015-09-04T01:59:00Z'
            Next: wait_using_timestamp_path
          wait_using_timestamp_path:
            Type: Wait
            TimestampPath: "$.expirydate"
            Next: wait_using_seconds_path
          wait_using_seconds_path:
            Type: Wait
            SecondsPath: "$.expiryseconds"
            Next: FinalState
          FinalState:
            Type: Task
            Resource:
              Fn::GetAtt: [hello, Arn]
            End: true
plugins:
  - serverless-step-functions
  - serverless-pseudo-parameters

Retry Failure

functions:
  hello:
    handler: handler.hello

stepFunctions:
  stateMachines:
    yourRetryMachine:
      definition:
        Comment: "A Retry example of the Amazon States Language using an AWS Lambda Function"
        StartAt: HelloWorld
        States:
          HelloWorld:
            Type: Task
            Resource:
              Fn::GetAtt: [hello, Arn]
            Retry:
            - ErrorEquals:
              - HandledError
              IntervalSeconds: 1
              MaxAttempts: 2
              BackoffRate: 2
            - ErrorEquals:
              - States.TaskFailed
              IntervalSeconds: 30
              MaxAttempts: 2
              BackoffRate: 2
            - ErrorEquals:
              - States.ALL
              IntervalSeconds: 5
              MaxAttempts: 5
              BackoffRate: 2
            End: true
plugins:
  - serverless-step-functions
  - serverless-pseudo-parameters

Parallel

functions:
  hello:
    handler: handler.hello

stepFunctions:
  stateMachines:
    yourParallelMachine:
      definition:
        Comment: "An example of the Amazon States Language using a parallel state to execute two branches at the same time."
        StartAt: Parallel
        States:
          Parallel:
            Type: Parallel
            Next: Final State
            Branches:
            - StartAt: Wait 20s
              States:
                Wait 20s:
                  Type: Wait
                  Seconds: 20
                  End: true
            - StartAt: Pass
              States:
                Pass:
                  Type: Pass
                  Next: Wait 10s
                Wait 10s:
                  Type: Wait
                  Seconds: 10
                  End: true
          Final State:
            Type: Pass
            End: true
plugins:
  - serverless-step-functions
  - serverless-pseudo-parameters

Catch Failure

functions:
  hello:
    handler: handler.hello

stepFunctions:
  stateMachines:
    yourCatchMachine:
      definition:
        Comment: "A Catch example of the Amazon States Language using an AWS Lambda Function"
        StartAt: HelloWorld
        States:
          HelloWorld:
            Type: Task
            Resource:
              Fn::GetAtt: [hello, Arn]
            Catch:
            - ErrorEquals: ["HandledError"]
              Next: CustomErrorFallback
            - ErrorEquals: ["States.TaskFailed"]
              Next: ReservedTypeFallback
            - ErrorEquals: ["States.ALL"]
              Next: CatchAllFallback
            End: true
          CustomErrorFallback:
            Type: Pass
            Result: "This is a fallback from a custom lambda function exception"
            End: true
          ReservedTypeFallback:
            Type: Pass
            Result: "This is a fallback from a reserved error code"
            End: true
          CatchAllFallback:
            Type: Pass
            Result: "This is a fallback from a reserved error code"
            End: true
plugins:
  - serverless-step-functions
  - serverless-pseudo-parameters

Choice

functions:
  hello1:
    handler: handler.hello1
  hello2:
    handler: handler.hello2
  hello3:
    handler: handler.hello3
  hello4:
    handler: handler.hello4

stepFunctions:
  stateMachines:
    yourChoiceMachine:
      definition:
        Comment: "An example of the Amazon States Language using a choice state."
        StartAt: FirstState
        States:
          FirstState:
            Type: Task
            Resource:
              Fn::GetAtt: [hello, Arn]
            Next: ChoiceState
          ChoiceState:
            Type: Choice
            Choices:
            - Variable: "$.foo"
              NumericEquals: 1
              Next: FirstMatchState
            - Variable: "$.foo"
              NumericEquals: 2
              Next: SecondMatchState
            Default: DefaultState
          FirstMatchState:
            Type: Task
            Resource:
              Fn::GetAtt: [hello2, Arn]
            Next: NextState
          SecondMatchState:
            Type: Task
            Resource:
              Fn::GetAtt: [hello3, Arn]
            Next: NextState
          DefaultState:
            Type: Fail
            Cause: "No Matches!"
          NextState:
            Type: Task
            Resource:
              Fn::GetAtt: [hello4, Arn]
            End: true
plugins:
  - serverless-step-functions
  - serverless-pseudo-parameters

Map


functions:
  entry:
    handler: handler.entry
  mapTask:
    handler: handler.mapTask

stepFunctions:
  stateMachines:
    yourMapMachine:
      definition:
        Comment: "A Map example of the Amazon States Language using an AWS Lambda Function"
        StartAt: FirstState
        States:
          FirstState:
            Type: Task
            Resource:
              Fn::GetAtt: [entry, Arn]
            Next: mapped_task
          mapped_task:
            Type: Map
            Iterator:
              StartAt: FirstMapTask
              States:
                FirstMapTask:
                  Type: Task
                  Resource:
                    Fn::GetAtt: [mapTask, Arn]
                  End: true
            End: true

plugins:
  - serverless-step-functions

Author: serverless-operations
Source Code: https://github.com/serverless-operations/serverless-step-functions 
License: View license

#serverless #functions #step #aws 

Serverless Step Functions
Crypto Like

Crypto Like

1619508060

What is Step Finance (STEP) | What is Step Finance token | What is STEP token

In this article, we’ll discuss information about the Step Finance project and STEP token

Step Finance is a portfolio visualisation platform which aggregates all LPs, tokens, farms and positions that a user may have associated with their wallet and displays it in an easy to use dashboard with various useful metrics and visualisations. Step aims to be the page which DeFi users have open all day with all the functions and information they need to make informed decisions.

Step will also enable users to interact directly with their favourite protocols from within the dashboard which could include farming, pools, swaps and automated strategies. Many of these value added services will be done for a fee which will be collected and used to buyback and distribute tokens to STEP tokenholders. Today we will discuss the tokenomics behind STEP token.

Base Functionality

Step must connect to the relevant Programs (smart contracts) built on Solana and query them to be able to get the information necessary to display to a user and execute transactions. Step’s core functions that are implemented or planned are:

Wallet balances:

Dashboard view of all tokens you have associated with an individual address with options to consolidate multiple wallet addresses into the one view and display value over time denominated in a selection of base currencies.

LP tokens view:

Get information related to current LP tokens you hold in Solana AMMs and visualise performance of the pools over time, vs base currency/hodling, fee revenue, impermanent loss, yield and APYs.

Yield Farming and Staking:

Step will provide a table of current yield farming opportunities on Solana with easy one-click entry into any of the farms. Enter, exit and claim yield farm rewards on this page. This page also handles single asset staking on Solana validator nodes.

DEX positions:

The base AMM from which all DEXes on Solana are using or forked from is Serum. This is a full orderbook DEX where users can buy and sell any supported token. Step will display current wallet balances, open orders, unsettled balances and any other related information to user positions in Serum.

Investment Strategies:

Step was founded in the February 2021 Solana Defi Hackathon as an automated strategy platform. This page will cover automated strategies like DCA, EMAs and RSIs and others which users can enter into with potential in future to enable users to make their own custom strategies.

Swap aggregation:

All Programs on Solana are composable and as such liquidity can be fragmented among various pools and AMM programs. It is therefore important to be able to ensure users get the best possible deal on their swaps and Step being a DeFi aggregator is well placed to offer swap aggregation from within Step.

Design

Design is crucial to STEP being the front page of the Solana ecosystem. The STEP team aims for Step.finance to be a page which is used everyday by everyone in the Solana ecosystem. Step will be something which will have coverage of all other projects on the Step dashboard, providing an easy to use ‘Wallet view’ for all the coins, LPs, farms and positions a user may have associated with their wallet.

Design Principles:

  • Dashboard should be high level view immediately for NET worth of users account
  • User should be able to interrogate the information to view the underlying values associated with their wallet.
  • People first, interactions should be natural and mirror how people already interact with DeFi.

Value Accrual

It is essential to the success of the project that STEP actually generates revenue and adds value to the token. Therefore there are several value accrual mechanisms for the token itself which will further generate positive interest and incentives in a positive feedback loop. The below is a list of value accrual mechanics that the team intend to implement on the Step platform:

Transaction fees: The most basic and simple value accrual is STEP taking a fee on certain interactions on the STEP platform (swaps, yield farms, automated strategies, bridges etc). The majority of this fee revenue goes to stakers of the STEP token (80%) with the remaining 20% sent to the treasury.

Buyback and distribute: The core value accrual mechanic of STEP is buyback of STEP tokens from revenue generated by transaction fees in the protocol which are then distributed to stakers. This gives a native APY for staking the STEP token and aligns incentives of tokenholders and the team with the success of the STEP platform. This is awarded proportionally to the liquidity an LP stakes in this pool.

Reducing supply emission rate: Supply emissions should reduce over time without any large cliffs (like evident with SWRV which did a 90% cliff in emissions that made their TVL fall from $700m to $3m and cause people to forget/leave the project). The optimal scenario is a gradual reduction in new emissions over time at a set rate. For STEP this emission reduction will be 4% weekly of the community and ecosystem reserve. With a steady or increasing demand and a falling emission rate there reaches an inflection point where buybacks volume is greater than new emissions creating NET positive buy pressure on the STEP token.

Aggregation: A core part of the Step Finance platform is being an aggregator for various user actions like AMM Swaps and Yield Farms. Step will implement a swap router on Solana for improved liquidity and price discovery in AMM swap pools and also offer a number of tools for yield farmers like single asset entry/exit into LPs, autocompounding yields and managed yield farm vaults. Fees collected for these services will flow back to STEP stakers.

Lockups: Incentivising users to take coins off the market is one way to reduce potential sell pressure and ensure given flat or growing demand that there is NET positive buy pressure. STEP intends to implement various lockup LP pools where tokens will be locked for a specified amount of time and be able to share in a higher rate of emissions or staking revenue compared with staking pools which allow immediate withdrawal.

Automated Trading Strategies: Step started as an automated strategy platform and our intent has always been to build out this functionality. There are various automated vaults and trading strategy contracts on Solana which STEP will be exposing to users on the Step dashboard. Step will charge a fee to enter and exit these vaults and potentially for some strategies a performance fee as well. These fees will accrue to STEP stakers.

Stake for Access: There are a number of investment indicators, analytics, portfolio insights which Step plans to make available to users who are stakers of STEP. Stake-for-access is a superior model to subscriptions or paid plans and adds additional value to STEP token holders.

Fiat-Defi integrations: Step intends to implement arms length widgets which can handle fiat on/off ramps into Solana via Step. This brings more money into the Step ecosystem, puts it to productive use in Step strategies and swaps and ensure Step remains a focal point for access into and out of the Solana ecosystem. Step has a fiat partner onboard for this once we are ready for development.

Referrals: Referrals are a growing value additive mechanism in the crypto world with DeFi projects such as 1INCH utilising it effectively. One user may refer others via a weblink and earn a share of the fees. Step intends to implement this system for Swaps and potentially other automated strategies.

Cross chain Integration: Being multi-chain capable is a core principle at STEP finance, there are several bridges under development on Solana, we will support all of them and likely take a small fee on each tx. Adding additional blockchains is a path towards more TVL, users, revenue streams for STEP…

Governance:

Step.finance has a core team of 3 who initially will guide the direction of the project. There will be no governance functions initially but the team is open to exploring further tokenholder involvement in the decision making process once STEP token is established.

  • It is important in the beginning of a project’s life that the founding team has control over the direction and development of the project. Only after reaching a phase in the project’s life where the direct day to day input of the founders are not necessary to the continued development of STEP would a governance DAO be considered.

Tokenomics

The STEP token ensures there is a clear alignment of incentives between users, tokenholders and the team so that Step can continue to grow in a sustainable and productive manner for the benefit of all DeFi users. It is therefore crucial that the $STEP token have strong fundamental value accrual structures in place which build value over time.

Overview:

Max Supply 1 000 000 000 STEP
Emission Period: 2 years
Weekly Emission Reduction: -4%
Airdrops % of Supply: 1%
Founders % of Supply: 20%
Founder Lockup: Lockup over 2 years vested in 25% increments every 6 months.
Treasury % of Supply: 12.219%
Presale Investors: 11.780%
Presale Lockup: 2 years vesting. 50% available after 1 year, 50% available year 2.
Community & Ecosystem % of Supply: 55% (LPs and Trade executors)
Token Type: SPL (Solana Native)

Principles:

  • Value accrual to the STEP token is key focus
  • 2 year emission schedule with 4% weekly reduction. Early risk takers rewarded and new supply entering circulation reduces over time and helps build long term value given static or growing buyback demand. Mirrors similar tokenomics of other yield aggregators (Harvest, Pickle, Yearn, etc).
  • No new emissions go to STEP native staking pool. This dilutes supply away from value productive TVL accruing activities.
  • ‘Trade Executors’ are defined as users who execute transactions on the Step platform which incur a fee. We may choose to incentivise this in future.
  • Community reward emissions % is split between LPs vs Exchange Executors weekly. The % split may change over time. Current split at launch is 60% to Trade Executors 40% to LP Stakers
  • The Treasury should be sustainable and have enough supply to pay for top talent.
  • 80% of fees on Step go to STEP stakers and 20% to the Treasury.

Staking and Buybacks:

  1. Step.finance will have a single asset pool for the STEP token.
  2. This pool receives NO emissions. It only receives income from revenue generated by step.finance
  3. Step will charge fees for services on step.finance with 80% of fees going to stakers and 20% to treasury.
  4. STEP also has incentivised liquidity pools which will receive new emissions. At launch this will be STEP/USDC and potentially RAY/STEP.

How and Where to Buy Step Finance (STEP)?

STEP has been listed on a number of crypto exchanges, unlike other main cryptocurrencies, it cannot be directly purchased with fiats money. However, You can still easily buy this coin by first buying Bitcoin, ETH, USDT, BNB from any large exchanges and then transfer to the exchange that offers to trade this coin, in this guide article we will walk you through in detail the steps to buy STEP token.

You will have to first buy one of the major cryptocurrencies, usually either Bitcoin (BTC), Ethereum (ETH), Tether (USDT), Binance (BNB)…

We will use Binance Exchange here as it is one of the largest crypto exchanges that accept fiat deposits.

Binance is a popular cryptocurrency exchange which was started in China but then moved their headquarters to the crypto-friendly Island of Malta in the EU. Binance is popular for its crypto to crypto exchange services. Binance exploded onto the scene in the mania of 2017 and has since gone on to become the top crypto exchange in the world.

Once you finished the KYC process. You will be asked to add a payment method. Here you can either choose to provide a credit/debit card or use a bank transfer, and buy one of the major cryptocurrencies, usually either Bitcoin (BTC), Ethereum (ETH), Tether (USDT), Binance (BNB)…

SIGN UP ON BINANCE

Step by Step Guide : What is Binance | How to Create an account on Binance (Updated 2021)

Next step - Transfer your cryptos to an Altcoin Exchange

Since STEP is an altcoin we need to transfer our coins to an exchange that STEP can be traded. Below is a list of exchanges that offers to trade STEP in various market pairs, head to their websites and register for an account.

Once finished you will then need to make a BTC/ETH/USDT/BNB deposit to the exchange from Binance depending on the available market pairs. After the deposit is confirmed you may then purchase STEP from the exchange: https://dex.projectserum.com

The top exchange for trading in STEP token is currently 

There are a few popular crypto exchanges where they have decent daily trading volumes and a huge user base. This will ensure you will be able to sell your coins at any time and the fees will usually be lower. It is suggested that you also register on these exchanges since once STEP gets listed there it will attract a large amount of trading volumes from the users there, that means you will be having some great trading opportunities!

Top exchanges for token-coin trading. Follow instructions and make unlimited money

https://www.binance.com
 ☞ https://www.bittrex.com
 ☞ https://www.poloniex.com
 ☞ https://www.bitfinex.com
 ☞ https://www.huobi.com

Find more information STEP

WebsiteExplorerSource CodeSocial ChannelSocial Channel 2Coinmarketcap

🔺DISCLAIMER: The Information in the post isn’t financial advice, is intended FOR GENERAL INFORMATION PURPOSES ONLY. Trading Cryptocurrency is VERY risky. Make sure you understand these risks and that you are responsible for what you do with your money.

🔥 If you’re a beginner. I believe the article below will be useful to you

⭐ ⭐ ⭐ What You Should Know Before Investing in Cryptocurrency - For Beginner ⭐ ⭐ ⭐

I hope this post will help you. Don’t forget to leave a like, comment and sharing it with others. Thank you!

#bitcoin #cryptocurrency #step #step finance

What is Step Finance (STEP) | What is Step Finance token | What is STEP token