#usermodel
Explore tagged Tumblr posts
Text
🗣️php spark create:db users php spark make:migration users php spark make:model UserModel php spark db:table
7 notes
·
View notes
Text
Codeigniter 4 - MVC Design Pattern with examples
Codeigniter 4 – MVC Design Pattern with examples Model - Purpose: Represents the data and business logic of the application - Responsibilities: - Interact with the database - Perform calculations and data processing - Validate data - Example: app/Models/UserModel.php <?php namespace App\Models; use CodeIgniter\Model; class UserModel extends Model { protected $table = 'users'; protected…
0 notes
Text
MIDTERM PRESENTATION
Project Name: Con-gra-du-ation! Summary: This project was executed to observe and analyze the existing affair support system which made the students get annoyed and from that, we drew some flow model and social model to redesign this into envisioned flow model which contains our new system “Con-gra-du-ation!”. The name “Con-gra-du-ation!” came from the word ‘congratulation’ and ‘graduation’, which implicates that we want to help and celebrate the user’s graduation in a simple word by adopting a new system.
Presentation File: https://drive.google.com/open?id=1jhiQLpurHvdbvzUmVbXIs1PvOYVhbD-s
화질 보존과 편이성의 이유로 link에 첨부해드린 점 양해부탁드립니다.
#midtermpresentation#flowmodel#socialmodel#congraduation#usermodel#hci2018#congradulation#graduation#yonseiuniversity
0 notes
Text
Managing Offline Data Storage With Flutter Hive
Have you ever heard the name flutter hive? Nearly every app needs local storage for its data. Apps, including those built with Flutter, rely heavily on the ability to store and manipulate data. The replies from a REST API may be cached, an offline app could be developed, or data about customers could be saved for a food delivery service. Flutter provides developers with a number of mechanisms for saving state locally.
Of course, Flutter hive is one of the finest options for offline data storage if you need a fast and secure local database compatible with Flutter Web().
What is Flutter Hive?
Hive is a very fast and small key-value database developed entirely in Dart that can be used to store and synchronize data for applications even when not online.
Hive is a high-performance, Dart-based key-value data store that works with simple and complicated data structures. It is also encrypted with AES-256 for further security.
Steps to Take When Beginning to Manage Offline Data Storage Using Flutter Hive
This blog post will use the TypeAdapter in Flutter app development with the Hive database. Furthermore, we will develop a minimal app consisting of a single page that can be used to see a directory of users, create new users, edit current ones, or remove old ones.
Step 1- First, install any necessary dependencies.
Two prerequisites must be met before Hive can be used.
hive and hive_flutter
The packages Hive and hive flutter must be included to pubspec.yaml.
dependencies: Flutter: sdk: flutter hive: ^2.2.3 hive_flutter: ^1.1.0Add the dev dependenciesdev_dependencies: flutter_test: sdk: flutterhive_generator: ^1.1.3build_runner: ^2.2.0Step 2: Initialization of Hive Database
Before we can proceed with executing runApp in the Flutter app, we must first initialize Hive.
void main() async{ WidgetsFlutterBinding.ensureInitialized(); // Initializes Hive with a valid directory in your app files await Hive.initFlutter(); runApp(const MyApp());}
Hive is responsible for providing the initFlutter() method. Hive is essentially initialized by using the path given by the getApplicationDocumentsDirectory command.
What is a Box in Flutter Hive?
Using boxes, Flutter Hive categorizes the information it stores. The container looks like a SQL table but lacks any particular formatting and may store anything at all. Hive encrypts data, as indicated at the outset. Sensitive information may also be stored in encrypted boxes.
Hive saves its information in the form of key-value sets. You should start by opening the package.
void main() async{ WidgetsFlutterBinding.ensureInitialized();// Initializes Hive with a valid directory in your app files await Hive.initFlutter();// open boxawait Hive.openBox("userBox");runApp(const MyApp());}
Model Class With TypeAdapter
Our illustration includes a number of users, each with information such as their name, hobbies, and descriptions.
lib/models/person.dartimport 'package:hive/hive.dart';part 'person.g.dart';@HiveType(typeId: 1)class Person { @HiveField(0) final String name; @HiveField(1) final String country; Person({ required this.name, required this.country, });}
It is necessary to import the hive first. The type adapter may be generated by including the user model.g.dart file. Since we’re using the hive generator package, building a TypeAdapter from scratch is unnecessary.
TypeAdapters are generated automatically for almost all classes using the hive generator package. Several fields are marked in the userModel class.
Use @HiveType() to provide the model class and let the generator know it should produce a TypeAdapter.
The class’s fields must be annotated with @HiveField(index) and the matching index.
Execute the following command to build a TypeAdapter class:
flutter packages pub run build_runner build
Here an additional file, data model.g.dart (where g denotes “generated”), will be appended to the existing user model.dart. The resulting filename would be user model.g.dart.
CRUD Operations
Creating Data in Hive
/// Add new userFuture addUser({required UserModel userModel}) async { await box.add(userModel);}
When we touch the floating button, a dialogue window appears where you may get details such as our name, interests, and bio. Then we’ll click the “add” button, and information will populate.
The Flutter Hive ValuelistenableBuilder() stream may eavesdrop on activities inside the container.
Retrieving Data in Flutter Hive
lib/screens/add_screen.dartimport 'package:flutter/material.dart';import 'package:hive_demo/utils/add_person_form.dart';class AddScreen extends StatefulWidget { @override _AddScreenState createState() => _AddScreenState();}class _AddScreenState extends State<addscreen> { @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, appBar: AppBar( title: Text('Add Info'), ), body: Padding( padding: const EdgeInsets.all(16.0), child: AddPersonForm(), ), ); }}</addscreen>lib/screens/info_screen.dartimport 'package:flutter/material.dart';import 'package:hive_demo/screens/update_screen.dart';import 'package:hive_flutter/hive_flutter.dart';import 'package:hive_demo/screens/add_screen.dart';class InfoScreen extends StatefulWidget { @override _InfoScreenState createState() => _InfoScreenState();}class _InfoScreenState extends State<infoscreen> { late final Box contactBox; // Delete info from people box _deleteInfo(int index) { contactBox.deleteAt(index); print('Item deleted from box at index: $index'); } @override void initState() { super.initState(); // Get reference to an already opened box contactBox = Hive.box('peopleBox'); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('People Info'), ), floatingActionButton: FloatingActionButton( onPressed: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => AddScreen(), ), ), child: Icon(Icons.add), ), body: ValueListenableBuilder( valueListenable: contactBox.listenable(), builder: (context, Box box, widget) { if (box.isEmpty) { return Center( child: Text('Empty'), ); } else { return ListView.builder( itemCount: box.length, itemBuilder: (context, index) { var currentBox = box; var personData = currentBox.getAt(index)!; return InkWell( onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => UpdateScreen( index: index, person: personData, ), ), ), child: ListTile( title: Text(personData.name), subtitle: Text(personData.country), trailing: IconButton( onPressed: () => _deleteInfo(index), icon: Icon( Icons.delete, color: Colors.red, ), ), ), ); }, ); } }, ), ); }}</infoscreen>lib/screens/update_screen.dartimport 'package:flutter/material.dart';import 'package:hive_demo/models/person.dart';import 'package:hive_demo/utils/update_person_form.dart';class UpdateScreen extends StatefulWidget { final int index; final Person person; const UpdateScreen({ required this.index, required this.person, }); @override _UpdateScreenState createState() => _UpdateScreenState();}class _UpdateScreenState extends State<updatescreen> { @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, appBar: AppBar( title: Text('Update Info'), ), body: Padding( padding: const EdgeInsets.all(16.0), child: UpdatePersonForm( index: widget.index, person: widget.person, ), ), ); }}</updatescreen>lib/utils/add_person_form.dartimport 'package:flutter/material.dart';import 'package:hive/hive.dart';import 'package:hive_demo/models/person.dart';class AddPersonForm extends StatefulWidget { const AddPersonForm({Key? key}) : super(key: key); @override _AddPersonFormState createState() => _AddPersonFormState();}class _AddPersonFormState extends State<addpersonform> { final _nameController = TextEditingController(); final _countryController = TextEditingController(); final _personFormKey = GlobalKey<formstate>(); late final Box box; String? _fieldValidator(String? value) { if (value == null || value.isEmpty) { return 'Field can\'t be empty'; } return null; } // Add info to people box _addInfo() async { Person newPerson = Person( name: _nameController.text, country: _countryController.text, ); box.add(newPerson); print('Info added to box!'); } @override void initState() { super.initState(); // Get reference to an already opened box box = Hive.box('peopleBox'); } @override Widget build(BuildContext context) { return Form( key: _personFormKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Name'), TextFormField( controller: _nameController, validator: _fieldValidator, ), SizedBox(height: 24.0), Text('Home Country'), TextFormField( controller: _countryController, validator: _fieldValidator, ), Spacer(), Padding( padding: const EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 24.0), child: Container( width: double.maxFinite, height: 50, child: ElevatedButton( onPressed: () { if (_personFormKey.currentState!.validate()) { _addInfo(); Navigator.of(context).pop(); } }, child: Text('Add'), ), ), ), ], ), ); }}</formstate></addpersonform>lib/utils/update_person_form.dartimport 'package:flutter/material.dart';import 'package:hive/hive.dart';import 'package:hive_demo/models/person.dart';class UpdatePersonForm extends StatefulWidget { final int index; final Person person; const UpdatePersonForm({ required this.index, required this.person, }); @override _UpdatePersonFormState createState() => _UpdatePersonFormState();}class _UpdatePersonFormState extends State<updatepersonform> { final _personFormKey = GlobalKey<formstate>(); late final _nameController; late final _countryController; late final Box box; String? _fieldValidator(String? value) { if (value == null || value.isEmpty) { return 'Field can\'t be empty'; } return null; } // Update info of people box _updateInfo() { Person newPerson = Person( name: _nameController.text, country: _countryController.text, ); box.putAt(widget.index, newPerson); print('Info updated in box!'); } @override void initState() { super.initState(); // Get reference to an already opened box box = Hive.box('peopleBox'); _nameController = TextEditingController(text: widget.person.name); _countryController = TextEditingController(text: widget.person.country); } @override Widget build(BuildContext context) { return Form( key: _personFormKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Name'), TextFormField( controller: _nameController, validator: _fieldValidator, ), SizedBox(height: 24.0), Text('Home Country'), TextFormField( controller: _countryController, validator: _fieldValidator, ), Spacer(), Padding( padding: const EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 24.0), child: Container( width: double.maxFinite, height: 50, child: ElevatedButton( onPressed: () { if (_personFormKey.currentState!.validate()) { _updateInfo(); Navigator.of(context).pop(); } }, child: Text('Update'), ), ), ), ], ), ); }}</formstate></updatepersonform>lib/main.dartimport 'package:flutter/material.dart';import 'package:hive_demo/models/person.dart';import 'package:hive_flutter/hive_flutter.dart';import 'screens/info_screen.dart';main() async { // Initialize hive await Hive.initFlutter(); // Registering the adapter Hive.registerAdapter(PersonAdapter()); // Opening the box await Hive.openBox('peopleBox'); runApp(MyApp());}class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState();}class _MyAppState extends State<myapp> { @override void dispose() { // Closes all Hive boxes Hive.close(); super.dispose(); } @override Widget build(BuildContext context) { return MaterialApp( title: 'Hive Demo', theme: ThemeData( primarySwatch: Colors.purple, ), debugShowCheckedModeBanner: false, home: InfoScreen(), ); }}</myapp>
Updating Data in Flutter Hive
Information associated with a key may be modified using the put() function.
With this, the previously-provided value will be replaced with the new one at that key.
/// update user dataFuture updateUser({required int index,required UserModel userModel}) async { await box.putAt(index,userModel);
Output
Here, we’re taking use of auto-incrementing values; to make a change based on the index, just call the putAt(index) function on the box object.
Conclusion
Hive is a simple, quick, and secure NoSQL database. An API or developer language is often used to store, retrieve, search, update, query, and otherwise change data in a database. These tasks often occur behind the scenes of a program, undetected by users. Hence, the hive has an efficient database, and particularly, it is blazing rapidly, and it supports almost all platforms.
This article will explain the handling the offline storage with a Flutter hive. However, this functionality will aid you in creating high-quality and feature-rich apps with a few lines of coding! I hope you guys have a clear idea about handling the offline data in Flutter development. To integrate this functionality into your application, you can hire Flutter app experts to make an app innovative and with the latest features. Let’s get started and share your project requirements with us.
Frequently Asked Questions (FAQs)
1. What is Hive? and why is it used?
Hive is lightweight and has a fast key-value database written in Dart language. It grants you to store and sync application info offline. Thus, it is used to aid primitive and complicated data structures while delivering a high level of performance in the application.
2. How will you get the data from a hive database in Flutter?
Step 1: Install and Initialize the hive database
Step 2: Import hive and generate the type adapter
Step 3: Insert user_model.g.dart to automatically create a userModel class using the hive generator package.
Step 4: Write that type of adapter before calling the run app function to register UserModelAdapter
3. How to store data locally in Flutter with Hive?
To store data locally in Flutter with Hive, you can follow these general steps:
Define your data model
Initialize Hive
Open a Hive box
Write data to the box
Read data from the box
Close the box
4. How do I integrate my flutter app with hive?
Install the Hive package: Add the hive and hive_flutter packages to your pubspec.yaml file and run flutter pub get to install them.
Define your Hive data model: Create a class that extends from HiveObject and define the fields that you want to store in Hive.
Initialize Hive: In your app’s main method, initialize Hive by calling Hive.initFlutter().
Open a Hive box: To open a Hive box, call Hive.openBox() and pass in a unique name for your box.
Read and write data: To read data from your Hive box, call Box.get() the key for the data you want to read. To write data to your Hive box, call Box.put() with the key and the data you want to write.
Originally Published At: https://flutteragency.com/manage-offline-data-storage-flutter-hive/
0 notes
Text
Laravel One to One Eloquent Relationship Tutorial
Laravel's famous Eloquent ORM is an Object-Relational Mapper and this tutorial will demonstrate how One To One relationships work in Laravel with an example. Join me while I show you what they are
Laravel One to One Relationship
One-to-one relationships play a fundamental role in the Laravel framework. One instance of the UserModel has only one BankAccount, so it has a single account number. Thus, we can connect both models - User and Bank - as a one-to-one relationship with each other. Let's put the account method into the User model, and that account only belongs to one User. So in the User model, we can call the account method and that one call hasOneMethod.
Step 1: Create account details Schema.
php artisan make:migration create_account_table Once you enter above command in terminal you will find the one schema in which we need to define columns as follow. Read the full article
#eloquenthasonerelationship#laravelbelongstoonetoonerelationship#laraveleloquentonetoonerelationship#LaravelEloquentORMisthebestpossibledatabasesolution#laravelhasonerelationshipexample#LaravelOneToOneEloquentRelationshipsTutorialExample#ORMMappingisveryeasy
0 notes
Text
Coding convention là gì? Những điều bạn cần biết
Coding convention là một thuật ngữ mà có lẽ bất kể các lập trình viên nào cũng từng nghe nói đến. Mặc dù trên thực tế thì việc tuân thủ theo coding convention hay việc không ảnh hưởng đến các kết quả của công việc là một điều khó.
Quy tắc đặt tên ( Naming Convention)
Cú pháp CamelCase(Cú pháp lạc đà): Với ký tự đầu tiên viết thường những ký tự đầu tiên của ký tự tiếp theo được viết hoa, ví dụ như: thisIsThe Name, product Price,….
Cú pháp Pascal Case(Pascal): Viết hoa chữ cái đầu tiên ví dụ như: ProductPrice, ProductName,…
Cú pháp Snake_case(con rắn): Tất cả chữ cái đều viết thường và cách nhau bởi dấu gạch dưới như: product_name, product_price,…
Một số nguyên tắc đặt tên
Thường các Tên biến, tên lớp: Thường là cụm danh từ hoặc danh từ, tính từ như UserModel, userName, isDownloaded.
Tên biến, tên hàm được đặt theo CamelCase hoặc snake_case.
Tên lớp: Đặt theo PascalCase.
Hằng số: được đặt theo UPPER_CASE.
Đặt tên phải có nghĩa và không được đặt tên kiểu viết tắt như uName, pName, a1,…
Tránh đặt những cái tên quá chung chung hay tối nghĩa: như top, getAll,…
Quy tắc về số lượng
Hàm không nên sử dụng quá 30 dòng.
lớp không nên để vượt qua 500 dòng.
Một hàm không được vượt quá 5 tham số nên giữ dưới hoặc bằng 3.
Hàm chỉ làm một việc duy nhất, trong trường hợp chính đáng, bạn có thể làm 2 việc được phép tuy nhiên tên hàm phải nói rõ ràng về điều này.
Khi bạn khai báo biến thì một dòng chỉ chứa một biến .
Một dòng không nên để quá dài 80 ký tự.
Các câu lệnh được lồng nhau tối đa 4 cấp.
Quy tắc xuống hàng
Theo Oracle: Dấu phẩy thì xuống hàng sau dấu phẩy.
Xuống hàng trước toán tử +,-,x,….
Có nhiều cặp lồng vào nhau, thì xuống hàng phải theo từng cấp.
Dòng xuống hàng mới thì được bắt đầu ở cùng cột với các đoạn lệnh cùng cấp ở trên.
Hotline: 0973703357
Địa chỉ: Ngõ 76 Duy Tân - Cầu Giấy
Xem thêm bài viết qua:
https://testerpro.vn/coding-convention-la-gi/
Xem thêm các bài viết qua:
https://testerpro.vn/dao-tao-tester/
https://myspace.com/thaodinhtesterpro https://www.pinterest.com/thaodinhtesterpro/_saved/ https://www.instapaper.com/p/thaodinh https://www.folkd.com/user/thaodinhtesterpro https://www.diigo.com/profile/thaodinhtester https://ok.ru/thaodinhtesterpro https://testerpro.vn/cach-viet-dac-ta-yeu-cau-phan-mem/ https://stocktwits.com/thaodinhtesterpro/posts https://ello.co/thaodinhtesterpro
0 notes
Text
Exploring the .NET Core library Coravel for Task Scheduling, Caching, Mailing and more
Coravel claims it is a "Near-zero config .NET Core library that makes Task Scheduling, Caching, Queuing, Mailing, Event Broadcasting (and more) a breeze!" A lovely claim, that is, in fact, true! It's open source and on Github at https://github.com/jamesmh/coravel so give Coravel a star!
Coravel is available on NuGet as a package - as are all things - or you can also install it's helper CLI with a simple dotnet tool install --global coravel-cli. After this, using coravel easy, early, and often is as simple as:
coravel install
A nice teach that makes it easy, the coravel CLI adds the package reference, restores your project, and reminds you to set it up in ConfigureServices() in Startup.cs. A nice example of a thoughtful library that is trying to make onboarding simpler.
The Coravel CLI is also a nice scaffolder to get you started with item templates:
> coravel Usage: coravel [options] [command] Options: -?|-h|--help Show help information Commands: event install invocable mail
But what is it?
With a somewhat vague name and a list of cool features that may not seem related, you may find yourself wondering WHAT is this and WHY do I need it?
When you start thinking about layering and responsibilities of real production software, you'll note that there are arguably some gaps in the BCL (Base Class Libraries) that .NET makes available, particularly as you move up into the Application Development space.
Scheduled jobs and tasks, simple emailing with Razor Templates, a lightweight event dispatcher, easily queueable background tasks are just some of the higher level primitives you'll find yourself wanting when creating business apps. Coravel collects those buildable elements and allows you to string them together very quickly.
For example, I'll create an "Invocable." Basically just a method that is more 'job-like.' It has business scope and I need to have it invoked later by some schedule or process within my app.
Here I'll register one in my Startup.cs.
services.AddScoped<SendNightlyReportsEmailJob>();
So I need to send a nightly report. That's an invocable thing, and it's also an IMailer because it mails things. Note the injected IMailer in the constructor. All very natural in ASP.NET Core, using Dependency Injection.
public class SendNightlyReportsEmailJob : IInvocable { private IMailer _mailer; public SendNightlyReportsEmailJob(IMailer mailer) { this._mailer = mailer; } public async Task Invoke() { Console.WriteLine("NightlyReportMailable Started...."); await Task.Delay(10000); // You could grab multiple users from a DB query ;) var mailable = new NightlyReportMailable(new UserModel { Name = "Coravel is lovely!", Email = "[email protected]" }); await this._mailer.SendAsync(mailable); Console.WriteLine($"NightlyReportMailable was sent at {DateTime.UtcNow}."); } }
Then I can have this mailed every evening with the Coravel Scheduler:
scheduler.Schedule<SendNightlyReportsEmailJob>().Daily();
But when, right? Easy:
scheduler .Schedule<SendNightlyReportsEmailJob>() .DailyAt(1, 30) .Zoned(TimeZoneInfo.Local);
What if you have a task that needs to happen, but maybe it's either long-running or happening often. You don't want two tasks going at the same time, so PreventOverlapping! Clever.
scheduler .Schedule<DoAThingOften>() .EveryMinute() .PreventOverlapping("DoAThingOften");
And that's just the scheduler. That mail you have to send? You can use Razor Pages to create reach HTML emails! That makes New User Sign Up, or Completed Order very easy to create. All self-contained in your app. I dig it.
Finally note that there's Pro paid version of Coravel that gives you a very professional UI for Jobs and Invocables, and allows you to visually configure your back-end job schedules. A nice way to support open source - especially when you start using it and really depending on it - is to explore very reasonable Pro licenses like those that Pro Coravel has. Impressive stuff, truly.
*I have no relationship with Coravel the project or the Pro licenses, I'm just a fan.
Sponsor: Suffering from a lack of clarity around software bugs? Give your customers the experience they deserve and expect with error monitoring from Raygun.com. Installs in minutes, try it today!
© 2020 Scott Hanselman. All rights reserved.
Exploring the .NET Core library Coravel for Task Scheduling, Caching, Mailing and more published first on https://deskbysnafu.tumblr.com/
0 notes
Text
Exploring the .NET Core library Coravel for Task Scheduling, Caching, Mailing and more
Coravel claims it is a "Near-zero config .NET Core library that makes Task Scheduling, Caching, Queuing, Mailing, Event Broadcasting (and more) a breeze!" A lovely claim, that is, in fact, true! It's open source and on Github at https://github.com/jamesmh/coravel so give Coravel a star!
Coravel is available on NuGet as a package - as are all things - or you can also install it's helper CLI with a simple dotnet tool install --global coravel-cli. After this, using coravel easy, early, and often is as simple as:
coravel install
A nice teach that makes it easy, the coravel CLI adds the package reference, restores your project, and reminds you to set it up in ConfigureServices() in Startup.cs. A nice example of a thoughtful library that is trying to make onboarding simpler.
The Coravel CLI is also a nice scaffolder to get you started with item templates:
> coravel Usage: coravel [options] [command] Options: -?|-h|--help Show help information Commands: event install invocable mail
But what is it?
With a somewhat vague name and a list of cool features that may not seem related, you may find yourself wondering WHAT is this and WHY do I need it?
When you start thinking about layering and responsibilities of real production software, you'll note that there are arguably some gaps in the BCL (Base Class Libraries) that .NET makes available, particularly as you move up into the Application Development space.
Scheduled jobs and tasks, simple emailing with Razor Templates, a lightweight event dispatcher, easily queueable background tasks are just some of the higher level primitives you'll find yourself wanting when creating business apps. Coravel collects those buildable elements and allows you to string them together very quickly.
For example, I'll create an "Invocable." Basically just a method that is more 'job-like.' It has business scope and I need to have it invoked later by some schedule or process within my app.
Here I'll register one in my Startup.cs.
services.AddScoped<SendNightlyReportsEmailJob>();
So I need to send a nightly report. That's an invocable thing, and it's also an IMailer because it mails things. Note the injected IMailer in the constructor. All very natural in ASP.NET Core, using Dependency Injection.
public class SendNightlyReportsEmailJob : IInvocable { private IMailer _mailer; public SendNightlyReportsEmailJob(IMailer mailer) { this._mailer = mailer; } public async Task Invoke() { Console.WriteLine("NightlyReportMailable Started...."); await Task.Delay(10000); // You could grab multiple users from a DB query ;) var mailable = new NightlyReportMailable(new UserModel { Name = "Coravel is lovely!", Email = "[email protected]" }); await this._mailer.SendAsync(mailable); Console.WriteLine($"NightlyReportMailable was sent at {DateTime.UtcNow}."); } }
Then I can have this mailed every evening with the Coravel Scheduler:
scheduler.Schedule<SendNightlyReportsEmailJob>().Daily();
But when, right? Easy:
scheduler .Schedule<SendNightlyReportsEmailJob>() .DailyAt(1, 30) .Zoned(TimeZoneInfo.Local);
What if you have a task that needs to happen, but maybe it's either long-running or happening often. You don't want two tasks going at the same time, so PreventOverlapping! Clever.
scheduler .Schedule<DoAThingOften>() .EveryMinute() .PreventOverlapping("DoAThingOften");
And that's just the scheduler. That mail you have to send? You can use Razor Pages to create reach HTML emails! That makes New User Sign Up, or Completed Order very easy to create. All self-contained in your app. I dig it.
Finally note that there's Pro paid version of Coravel that gives you a very professional UI for Jobs and Invocables, and allows you to visually configure your back-end job schedules. A nice way to support open source - especially when you start using it and really depending on it - is to explore very reasonable Pro licenses like those that Pro Coravel has. Impressive stuff, truly.
*I have no relationship with Coravel the project or the Pro licenses, I'm just a fan.
Sponsor: Suffering from a lack of clarity around software bugs? Give your customers the experience they deserve and expect with error monitoring from Raygun.com. Installs in minutes, try it today!
© 2020 Scott Hanselman. All rights reserved.
Exploring the .NET Core library Coravel for Task Scheduling, Caching, Mailing and more published first on http://7elementswd.tumblr.com/
0 notes
Text
abstraction
const UserModel = require('models/user'); const EmailService = require('services/email'); const NotificationService = require('services/notification'); const FileModel = require('models/file'); const Logger = require('services/logger'); const removeUserHandler = await (userId) => { const message = 'Your account has been deleted'; try { const user = await UserModel.getUser(userId); await UserModel.removeUser(userId); await EmailService.send(userId, message); await NotificationService.push(userId, message); return true; } catch (e) { console.error(e); Logger.send('removeUserHandler', e); }; return true; }
0 notes
Text
Social Network Integration on Android
Many mobile apps require a user to create an account or to sign up for a service in order to use them. From a user’s point of view, this can be somewhat troublesome or annoying, and it’s not always the best user experience.
So how can you overcome this when building your app? To give users a seamless experience, you can give them the ability to sign in to your app with just a single tap of a button, using one of their social networking accounts, e.g., Facebook or Twitter.
In this tutorial, you’ll learn how to integrate a user’s Facebook and Twitter accounts into your Android app to allow them to log in and also share posts from your app into their social networking account.
Getting Started
Use the Download Materials link at the top or bottom of this tutorial to download and extract the Starter Project for this tutorial.
Next, open Android Studio 3.1.3 or later, and choose Open an existing Android Studio project from the welcome screen or File > Open form the menu. Open the folder root folder of the Sharetastic starter project.
You’ll be working on an app called Sharetastic, which allows a user to share a status update to Facebook or a tweet to Twitter.
Build and run the project and you’ll see the login screen for the app:
As of now, the app does nothing. In this tutorial, you’ll go through it step-by-step and complete the social network integration.
Connecting With Facebook
To connect your app to Facebook, you’ll need an active Facebook account with which you’ll create an app to get a Facebook App ID.
Creating a Facebook App ID on Developers Portal & Setting Up
Go to the Facebook Developers Portal (log in with your Facebook account if needed).
On this page, you’ll see an option to Add a New App. Click the button and you’ll then need to create a Facebook App ID if you haven’t already:
Enter Sharetastic in the Display Name field and enter your email address in the Contact Email field, then click Create App ID. Facebook will prompt you with a captcha dialog; complete the request and click Submit.
Facebook will then direct you to another page:
Click on Set Up on the Facebook Login component. Then, from the new page containing the platform options, select Android.
You’ll then see the following page with the steps to build your Android project:
At this point, you will skip steps 1 and 2 because they have already been completed for you in the starter project. Even so, it’s good to know what they are:
Step 1 includes downloading the Facebook SDK, and Step 2 tells you how to import it into the project. Here, Gradle will be used to sync the Facebook SDK rather than manually downloading the SDK, which you can see in the app module build.gradle file:
implementation 'com.facebook.android:facebook-login:[4,5)'
In Step 3, you’ll add your Package name com.raywenderlich.sharetastic and default Activity name com.raywenderlich.sharetastic.MainActivity.
Click on Save and then Continue (you may need to also confirm that your app is not yet in the Play Store).
For Step 4, you need to create a Development Key Hash and also a Release Key Hash if your app is live. A key hash is a 28-character-long string, which Facebook uses to verify the communication between your app and Facebook.
A key hash can be generated by typing the following command in the terminal:
For Mac and Linux:
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64
For Windows:
Things are not that simple here. First, you need to have keytool from the JDK, Secondly, get the openssl library here.
keytool -exportcert -alias androiddebugkey -keystore "C:\Users\USERNAME\.android\debug.keystore" | "PATH_TO_OPENSSL_LIBRARY\bin\openssl" sha1 -binary | "PATH_TO_OPENSSL_LIBRARY\bin\openssl" base64
Finally, after generating your Key Hash, paste it in the section provided in the fourth step.
Click Save then Continue.
For Step 5 on Single Sign On, if you’re working on a different app that is using notifications, you want want to set it to Yes, but, for now, leave it set to No and click on Save, then Next.
Now, for Step 6, open up strings.xml in the app/res/values folder, and paste the following after updating the placeholders with the values provided by Facebook:
<string name="facebook_app_id">Your-App-ID</string> <string name="fb_login_protocol_scheme">fbYour-App-ID</string>
Then, open AndroidManifest.xml and add the permission for accessing the Internet:
<uses-permission android:name="android.permission.INTERNET"/>
Additionally, under the application tag, paste the needed Facebook meta-data and activities:
<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/facebook_app_id"/> <activity android:name="com.facebook.FacebookActivity" android:configChanges= "keyboard|keyboardHidden|screenLayout|screenSize|orientation" android:label="@string/app_name" /> <activity android:name="com.facebook.CustomTabActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="@string/fb_login_protocol_scheme" /> </intent-filter> </activity>
Finally, you’re done setting things up from the Facebook developer console! The remaining steps you’ll need to login are handled in the next section.
Now it’s time move on to writing some code.
Log in With Facebook
Open up the main layout file activity_main.xml and add a Facebook login button below the TextView:
<com.facebook.login.widget.LoginButton android:id="@+id/facebookLoginButton" android:layout_width="wrap_content" android:layout_height="47dp" android:paddingBottom="15dp" android:paddingStart="10dp" android:paddingEnd="5dp" android:paddingTop="15dp" android:textSize="16sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.58" />
In MainActivity, create the following constants at the top of the class:
val EMAIL = "email" val PUBLIC_PROFILE = "public_profile" val USER_PERMISSION = "user_friends"
Inside the empty method facebookSetup(), add the following code:
callbackManager = CallbackManager.Factory.create() facebookLoginButton.setOnClickListener { facebookLoginButton.setReadPermissions(Arrays.asList(EMAIL, PUBLIC_PROFILE, USER_PERMISSION)) facebookLoginButton.registerCallback(callbackManager, object : FacebookCallback<LoginResult> { override fun onSuccess(loginResult: LoginResult) { } override fun onCancel() { } override fun onError(exception: FacebookException) { Toast.makeText(context,exception.localizedMessage, Toast.LENGTH_SHORT).show() } }) }
This code first initializes the CallbackManager Facebook property that was declared but uninitialized in the starter project. It then adds a click listener for the Facebook login button. Inside the click listener, it provides the permissions needed to read the email, public profile and friends of the user. It also logs in the user by returning the AccessToken.
Then in onActivityResult(), pass the result onto the CallbackManager:
callbackManager.onActivityResult(requestCode, resultCode, data)
In the onSuccess of the callback, you’ll get the user’s profile by using Facebook’s Graph API. You’ll then send the user to the Share screen. First, we need to talk to the Graph API.
User Profile from the Graph API
You’ll now create a Kotlin object, whose sole purpose will be to contain the helper methods to connect to the Graph API.
Create an object called Helper in a new package com.raywenderlich.sharetastic.util.
Once created, write the method getFacebookUserProfileWithGraphApi() inside of it:
object Helper { fun getFacebookUserProfileWithGraphApi(context: Context) { if (AccessToken.getCurrentAccessToken() != null){ val activity = context as Activity val request = GraphRequest.newMeRequest( AccessToken.getCurrentAccessToken() ) { jsonObject, _ -> val email = jsonObject?.get("email")?.toString() ?: "" val name = jsonObject.get("name").toString() val profileObjectImage = jsonObject?.getJSONObject("picture")?.getJSONObject("data")?.get("url").toString() } val parameters = Bundle() parameters.putString("fields", "id,name,link,picture.type(large), email") request.parameters = parameters request.executeAsync() } } }
This method uses a call to GraphRequest.newMeRequest() to fetch the userid, name, picture and email of the user who is currently logged in.
To keep things clean, create a package com.raywenderlich.sharetastic.model and create a class in the package called UserModel to contain the user’s data after the Graph API returns the results.
Your UserModel class would look something like this:
class UserModel(val name: String, val userName: String, val profilePictureUrl: String, val socialNetwork: SocialNetwork) : Serializable enum class SocialNetwork { Facebook, Twitter }
I have created the enum class SocialNetwork in the same class; you could create a separate file for that if you wish. The enum class is only for identifying which social network account the user is currently logged in with.
Head back to Helper where you’ll now write the method that will help in sending the user to the Share screen.
fun startShareActivity(context: Context, user: UserModel) { val activity = context as Activity val intent = Intent(context, ShareActivity::class.java) intent.putExtra("user", user) activity.startActivity(intent) activity.finish() }
This code takes the passed-in UserModel and sends it to the ShareActivity.
Go back to the method getFacebookUserProfileWithGraphApi() and after the line:
val profileObjectImage = jsonObject?.getJSONObject("picture")?.getJSONObject("data")?.get("url").toString() ?: ""
add the following:
val user = UserModel(name, email, profileObjectImage, SocialNetwork.Facebook) startShareActivity(context, user)
These lines convert the user’s info to a UserModel and pass it into the method startShareActivity().
After completing that, go back to MainActivity. In the onSuccess inside of facebookSetup(), write:
Helper.getFacebookUserProfileWithGraphApi(context)
The user should only be sent to the Share screen when the user has a valid AccessToken, and this can happen only in the onSuccess block of code.
Additionally, you need to set up a few things in the ShareActivity.
Create a UserModel property in the class:
lateinit var user: UserModel
And inside onCreate(), add:
user = intent.extras.get("user") as UserModel setData(user)
This piece of code is getting the passed in UserModel from the Intent method and passing the data to a new method setData().
The setData() method simply sets up the data in the UI, and includes conditionals that take slightly different actions depending on whether the logged in network is Facebook or Twitter.
fun setData(user: UserModel) { nameTextView.text = user.name userNameTextView.text = if (user.socialNetwork == SocialNetwork.Twitter) "@${user.userName}" else user.userName connectedWithTextView.text = if (user.socialNetwork == SocialNetwork.Twitter) "${connectedWithTextView.text} Twitter" else "${connectedWithTextView.text} Facebook" characterLimitTextView.visibility = if (user.socialNetwork == SocialNetwork.Twitter) View.VISIBLE else View.GONE postButton.text = if (user.socialNetwork == SocialNetwork.Twitter) "POST" else "CREATE POST" Picasso.with(this).load(user.profilePictureUrl).placeholder(R.drawable.ic_user).into(profileImageView) postEditText.visibility = View.GONE }
Now, run your app then tap on Continue with Facebook. You’ll be asked to give your app the permission to access the information. After this step, you’ll be redirected to the following screen:
You’ve successfully logged in!
Sharing on Facebook
It’s time to move on to posting a status to Facebook. For this, you need to change a few things.
Facebook recently changed its documentation and has removed the permission that was once required for the using the Graph API to share something on a user’s timeline. The alternative for that is now using the Facebook Share SDK.
Open the app build.gradle file, and add the following dependency in it:
implementation 'com.facebook.android:facebook-share:[4,5)'
Additionally, in your AndroiManifest.xml add the following line within the application tag:
<provider android:authorities="com.facebook.app.FacebookContentProvider{@string/facebook_app_id}" android:name="com.facebook.FacebookContentProvider" android:exported="true"/>
Now, open the ShareActivity class and write the method for posting status to Facebook:
fun postStatusToFacebook() { val builder = AlertDialog.Builder(this) builder.setTitle("Share Link") val input = EditText(this@ShareActivity) val lp = LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT) input.layoutParams = lp builder.setView(input) builder.setPositiveButton(android.R.string.ok) { dialog, p1 -> val link = input.text var isValid = true if (link.isBlank()) { isValid = false } if (isValid) { val content = ShareLinkContent.Builder() .setContentUrl(Uri.parse(link.toString())) .build() ShareDialog.show(this, content) } dialog.dismiss() } builder.setNegativeButton(android.R.string.cancel) { dialog, p1 -> dialog.cancel() } builder.show() }
This code will present an alert dialog to allow the user to enter a link to share, and then show the user the Facebook share dialog. We’re not doing any validation on the link other than to check that it’s not blank; you’d want to do some validation to make sure it’s a valid URL.
In later versions of the Facebook Share SDK, including the one you’re using in Sharetastic, you must provide some type of content to share. Your options are links, photos, videos, and other multimedia. See the Facebook Share SDK documentation for more details.
Next, in the postButtonAction() method, inside the setOnClickListener, add a call to the new function:
postStatusToFacebook()
Build and run the app again. You’ll need to tap logout on the Facebook button and re-connect. In a production app, you’ll want to saved the logged in state of the user so that they don’t have to log in again.
Click on CREATE POST. Now, try posting something to Facebook:
After pressing POST, go and check the Facebook app.
Hurray! Your status is posted to Facebook.
Logging Out of Facebook
Logging out is simply a one-line code, but, for logging out, you need to perform two additional tasks. You’ll now write a method in your ShareActivity that’ll do these tasks:
fun sendToMainActivity() { LoginManager.getInstance().logOut() finish() val intent = Intent(this, MainActivity::class.java) startActivity(intent) }
Going over the above: the first line of code allows a user to log out of Facebook. The rest of the lines finish the current activity and take a user to MainActivity. Finally, call this method inside the onOptionsItemSelected like this:
R.id.action_logout -> { sendToMainActivity() return true }
Once you tap the Logout button on top-right of the Share screen, you’ll be logged out from the app and taken to the Home screen.
Now, let’s connect the app with Twitter.
Connecting With Twitter
Like Facebook, you need a working Twitter account in order to integrate Twitter into your app, Twitter provides a Consumer Key and Consumer Secret for communication.
Creating a Twitter App on Developer Portal
Login to Twitter in a browser and head to Twitter’s Application Management Portal and click on Create New App.
Complete the necessary fields with the appropriate information (you’ll need to use a unique name like Sharetastic + your initials, and you also must provide a Privacy Policy URL and Terms of Service URL in order to follow along, but these can be placeholders like example.com) then click Create your Twitter application.
You’ll be taken to the following page:
I had to name the app Share-tastic because Sharetastic wasn’t available. :]
Copy the Consumer Key and Consumer Secret from the Keys and Access Tokens tab and paste them into the strings.xml with the names Twitter_CONSUMER_KEY and Twitter_CONSUMER_SECRET, respectively.
Then click on the Permissions tab.
If you want to get user’s email at the time of login, you have to check the option that says Request email addresses from users then click on Update Settings.
Setting Up
After finishing the creation of the app on the Twitter developer portal, you’ll now move on and add the Twitter Kit dependency.
Adding Twitter Kit Dependency
Note: This step can be skipped because it’s already done in the Starter Project
There are many dependencies provided by Twitter like Twitter Core, Tweet UI, Tweet Composer, and Twitter Mopub. For now, stick with Twitter Core because that’s the only dependency you need for this tutorial.
implementation 'com.twitter.sdk.android:twitter-core:3.1.1'
Paste the above dependency in the app build.gradle file and let the project sync.
Initializing Twitter Kit
Create a CustomApplication class extending from Application under a new package root. Override the onCreate() of the Application class as follows:
class CustomApplication : Application() { override fun onCreate() { super.onCreate() val config = TwitterConfig.Builder(this) .logger(DefaultLogger(Log.DEBUG)) .twitterAuthConfig(TwitterAuthConfig( resources.getString(R.string.Twitter_CONSUMER_KEY), resources.getString(R.string.Twitter_CONSUMER_SECRET))) .debug(true) .build() Twitter.initialize(config) } }
Then open AndroidManifest.xml and inside the tag application, paste the following snippet.
android:name=”com.raywenderlich.sharetastic.root.CustomApplication”
You are done setting up now and are ready to dive into writing some Twitter code!
Log in With Twitter
Add a Twitter login button to activity_main.xml:
<com.twitter.sdk.android.core.identity.TwitterLoginButton android:id="@+id/twitterLoginButton" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/facebookLoginButton" app:layout_constraintVertical_bias="0.1" />
Open MainActivity and inside the twitterSetup() method, add the following:
twitterLoginButton.callback = object : Callback<TwitterSession>() { override fun success(result: Result<TwitterSession>) { } override fun failure(exception: TwitterException) { Toast.makeText(context,exception.localizedMessage, Toast.LENGTH_SHORT).show() } }
And in the onActivityResult() method, add the following line:
twitterLoginButton.onActivityResult(requestCode, resultCode, data)
Like the method you wrote that fetches the user info after Facebook’s login is complete, you need to write a similar method for Twitter that gets the user’s info at login.
Open the Helper file and write the following method:
fun getTwitterUserProfileWthTwitterCoreApi( context: Context, session: TwitterSession) { TwitterCore.getInstance().getApiClient(session).accountService .verifyCredentials(true, true, false) .enqueue(object : Callback<User>() { override fun success(result: Result<User>) { val name = result.data.name val userName = result.data.screenName val profileImageUrl = result.data.profileImageUrl.replace("_normal", "") val user = UserModel(name, userName, profileImageUrl, SocialNetwork.Twitter) startShareActivity(context, user) } override fun failure(exception: TwitterException) { Toast.makeText(context, exception.localizedMessage, Toast.LENGTH_SHORT).show() } }) }
You’re using TwitterCore to authenticate the user and then going to the share screen on a successful authentication.
Next, open MainActivity and in the success part of the twitterLoginButton callback, add:
Helper.getTwitterUserProfileWthTwitterCoreApi(context, result.data)
Now, build and run your project and tap on Log in with Twitter. You’ll need to be running Sharetastic on a device or emulator that has the Twitter app installed and in which you are logged in.
You’ll be shown a screen to accept connecting your Twitter account to Sharetastic, and after you allow it, you’ll successfully log in and be taken to the Share screen.
A Tweet for the Tweeps
Before posting a tweet, make the app a little more interactive by placing the Twitter’s character limit — i.e., 240 — and change the TextView count placed on the top right with respect to the number of characters written in the posting TextView.
Write a method onTextChangeListener inside ShareActivity:
fun onTextChangeListener() { postEditText.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable) { characterLimitTextView.text = "${s.length}/240" } override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { } override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { } }) }
This code is handling the character count change logic at runtime.
Furthermore, change the setData() method of ShareActivity by replacing the line that sets the postEditText to be GONE with the following code:
if (user.socialNetwork == SocialNetwork.Twitter) { postEditText.filters = arrayOf<InputFilter>(InputFilter.LengthFilter(240)) onTextChangeListener() } else { postEditText.visibility = View.GONE }
Here, a character limit is applied on the TextView to stop a user from writing more than 240 characters.
Now, move on to posting a tweet. For that, you’ll write another method:
fun postATweet(message: String) { val statusesService = TwitterCore.getInstance().apiClient.statusesService val context = this statusesService.update(message, null, null, null, null, null, null, null, null) .enqueue(object : Callback<Tweet>() { override fun success(result: Result<Tweet>) { Toast.makeText(context,R.string.tweet_posted,Toast.LENGTH_SHORT).show() } override fun failure(exception: TwitterException) { Toast.makeText(context,exception.localizedMessage,Toast.LENGTH_SHORT).show() } }) postEditText.setText("") }
Finally, you need to tweak the postButtonAction() method a little bit:
fun postButtonAction() { postButton.setOnClickListener { view -> if (postEditText.text.toString().isBlank() && user.socialNetwork == SocialNetwork.Twitter) { Toast.makeText(this, R.string.cannot_be_empty, Toast.LENGTH_SHORT).show() } else if (user.socialNetwork == SocialNetwork.Facebook) { postStatusToFacebook() } else { postATweet(postEditText.text.toString()) } } }
Now the time has come in which you post your first tweet!
Build and run the app again. Like before, since you’re not saving the authenticated state of the user, you’ll need to login to Twitter again.
After logging in write, say, Hello Twitter from Sharetastic!
Then tap on POST and open the Twitter app.
You can finally see your tweet.
Feels good, doesn’t it?
Logging Out of Twitter
Like Facebook, logging out is pretty simple. All you have to do is change the method sendToMainActivity() in ShareActivity to the following:
if (user.socialNetwork == SocialNetwork.Facebook) { LoginManager.getInstance().logOut() } else { TwitterCore.getInstance().sessionManager.clearActiveSession() } finish() val intent = Intent(this, MainActivity::class.java) startActivity(intent)
The only change here is that the Twitter session is being cleared.
Once you run the app again, you’ll be able to log out from Twitter as well. :]
Where to Go From Here?
The Final Project for this tutorial can be found in the Download Materials link at the top or bottom of this tutorial. If you try to build the final project, please be sure to add in your own Facebook and Twitter app ID’s and keys.
You now know how to authenticate a user into Facebook and Twitter, post content to each, and log a user out of each.
As was mentioned in the tutorial, in your app you’re going to want to persist the state of the logged in user, so that the user does not have to log in to Facebook or Twitter every time the app runs. Try saving the user state as a challenge, once you’ve worked your way through the tutorial. If the user is logged in, take them right to the share screen.
If you want to explore more about the Facebook SDK and TwitterKit, visit the resources below:
Getting Started with Android Facebook SDK
Twitter Kit for Android
If you have any questions or comments, please let us know in the discussion below!
The post Social Network Integration on Android appeared first on Ray Wenderlich.
Social Network Integration on Android published first on https://medium.com/@koresol
0 notes
Text
Implementing VDOM in Elm
What if Elm didn't ship with a VDOM library? No HTML or SVG? You might be tempted that it wouldn't be very useful, but we can implement it ourselves using pure elm and ports. Ports? You heard right, good old trusty ports.
Simplest thing possible
So how can we render stuff? Well let's make a simple program to test:
module Main exposing (main) import MyHtml exposing (Html, program, div, text, href, a) import Time type Msg = Tick Float update : Msg -> Int -> (Int, Cmd Msg) update msg model = case msg of Tick t -> ( t, Cmd.none ) view : Int -> Html Msg view model = div [] [ text (model |> toString) , a [href "https://google.com"] [ text "GOOGLE IT"] ] main = program { init = ( 0, Cmd.none ) , update = update , subscriptions = \model -> Time.every Time.second Tick , view = view }
Looks pretty standard except for the funny import. Well, what does that look like?
port module MyHtml exposing (program, Html, Attribute, div, text, a, href) type Html msg = Node String (List (Attribute msg)) (List (Html msg)) | Text String type Attribute msg = Attr String String | Property String Json.Value
First, we defined some types to represent our HTML tree. Note that this is by no means complete, but can be easily extended to support things like SVG.
init : ( model, Cmd msg ) -> (model -> Html msg) -> ( model, Cmd msg ) init userInit userView = let ( initModel, initCmd ) = userInit in ( initModel, Cmd.batch [ render (userView initModel), initCmd ] ) update : (msg -> model -> ( model, Cmd msg )) -> (model -> Html msg) -> msg -> model -> ( model, Cmd msg ) update userUpdate userView msg model = let ( newModel, newCmd ) = userUpdate msg model in ( newModel, Cmd.batch [ render (userView newModel), newCmd ] ) program userProgram = Platform.program { init = init userProgram.init userProgram.view , update = update userProgram.update userProgram.view , subscriptions = userProgram.subscriptions }
Next let's define our program function. We're essentially wrapping the users program, but on both init and update we're adding an extra Cmd in to the mix:
render : Html msg -> Cmd msg render = encodeHtml >> renderPort port renderPort : Json.Value -> Cmd msg encodeHtml html = case html of Node name attrs children -> Json.object [ ( "type", Json.string "node" ) , ( "name", Json.string name ) , ( "attributes", Json.list (List.map encodeAttr attrs) ) , ( "children", Json.list (List.map encodeHtml children) ) ] Text str -> Json.object [ ( "type", Json.string "text" ), ( "value", Json.string str ) ] encodeAttr attr = case attr of Attr key val -> Json.object [ ( "type", Json.string "attribute" ), ( "key", Json.string key ), ( "value", Json.string val ) ] Property key val -> Json.object [ ( "type", Json.string "property" ), ( "key", Json.string key ), ( "value", val ) ]
So here we have encoded our HTML structure into JSON and sent it over a port. I'll spare you from future encoders, but they all look follow this same pattern. Let's see what the JavaScript side looks like:
<div id="output"></div> <script> var app = Elm.Main.worker() app.ports.renderPort.subscribe(function(html) { const output = document.getElementById("output"); while (output.firstChild) { output.removeChild(output.firstChild); } render(html, output); }); function render(struct, out) { switch(struct.type) { case "node": var el = document.createElement(struct.name); struct.attributes.forEach(attr => { switch(attr.type) { case "attribute": return el.setAttribute(attr.key, attr.value); case "property": return el[attr.key] = attr.value; } }); out.appendChild(el); struct.children.forEach(child => render(child, el)); break; case "text": var el = document.createTextNode(struct.value); out.appendChild(el); break; } } </script>
So whenever the renderPort triggers, we remove all contents from our destination node and use DOM apis to build the whole structure. Note that in DOM apis there are two flavors of attribute - actual atttributes that need to be set with setAttribute and properties that have specialised setters.
Does it work? Check it out:
Supporting events
Alright, we managed to get something on the screen, but we want to build a real app and so we need to support events coming in. We'll need to modify our approach a bit.
Let's make a little counter app to demo this:
module Main exposing (main) import MyHtml exposing (Html, program, div, text, href, a, onClick) type Msg = Inc | Dec main = program { init = ( 0, Cmd.none ) , update = \msg model -> case msg of Inc -> ( model + 1, Cmd.none ) Dec -> ( model - 1, Cmd.none ) , subscriptions = \model -> Sub.none , view = \model -> div [] [ text (model |> toString) , a [ onClick Inc ] [ text "+" ] , a [ onClick Dec ] [ text "-" ] ]
Event handlers in Elm are Json.Decode.Decoders that transform JavaScript event objects into the users custom msg type. But Json.Decode.Decoders are really functions under the hood.
Now a bit of trouble presents itself. We can't encode JSON Decoders into Json.Value objects and send them over ports. So how can we set up event listeners?
To solve this, we need to build up a dispatcher, which we can store in our model. So let's make our own model, that will wrap the users model:
import Dict exposing (Dict) type alias PrivateModel model msg = { userModel : model , handlers : Dict String (Decoder msg) }
The handlers key is a dispatcher datastructure: it holds the decoders the user specified stored under string keys. But where do those come from? We'll use a simple trick and compute a path through the DOM tree as their key. So for example the Inc onClick handler above, would have the key path div.0.a:click.
Next, we'll introduce a new representation, which we'll call SafeHtml. This is exactly the same as Html msg, except it drops the type variable and inside the coresponding SafeAttribute stores these key strings instead of the decoders. Note that we will only expose Html msg to the user of our library, SafeHtml is an implementation detail. We also add a new constructor for creating event listeners:
type Node handler = Node String (List (NodeAttribute handler)) (List (Node handler)) | Text String type NodeAttribute handler = Attr String String | Property String Json.Value | Event String Options handler type alias Options = { preventDefault : Bool , stopPropagation : Bool } type alias Html msg = Node (Json.Decode.Decoder msg) type alias Attribute msg = NodeAttribute (Json.Decode.Decoder msg) type alias SafeHtml = Node String type alias SafeAttribute = NodeAttribute String
We also update our encoders to only support SafeHtml.
Now we have the key data modeling pieces in hand. We now need a function that will take a Html msg and give us back both a handlers dispatcher and a SafeHtml tree that we can use for rendering:
extractListeners : String -> Html msg -> ( Dict String (Decoder msg), SafeHtml ) extractListeners prefix html = case html of Node name attrs children -> let key = prefix ++ "." ++ name safeAttrs = List.map (makeAttrSafe key) attrs listeners = List.filterMap getListener attrs kids = List.indexedMap (\index -> extractListeners (key ++ "." ++ toString index)) children childListeners = List.foldr (\( a, _ ) b -> Dict.union a b) Dict.empty kids in ( List.foldr (\( k, fn ) d -> Dict.insert (key ++ ":" ++ k) fn d) childListeners listeners , Node name safeAttrs (List.map Tuple.second kids) ) Text s -> ( Dict.empty, Text s ) makeAttrSafe : String -> Attribute msg -> SafeAttribute makeAttrSafe prefix attr = case attr of Event key options tagger -> Event key options (prefix ++ ":" ++ key) Attr k v -> Attr k v Property k v -> Property k v getListener : Attribute msg -> Maybe ( String, Decoder msg ) getListener attr = case attr of Event key _ tagger -> Just ( key, tagger ) _ -> Nothing
Now let's build it and use it:
subscriptions : (model -> Sub msg) -> PrivateModel model msg -> Sub (Maybe msg) subscriptions userSubscribtions model = let eventDispatcher ( key, event ) = Dict.get key model.handlers |> Maybe.andThen (\decoder -> Json.Decode.decodeValue decoder event |> Result.toMaybe ) in Sub.batch [ eventPort eventDispatcher, Sub.map Just (userSubscribtions model.userModel) ] port eventPort : (( String, Json.Value ) -> msg) -> Sub msg init : ( model, Cmd msg ) -> (model -> Html msg) -> ( PrivateModel model msg, Cmd (Maybe msg) ) init userInit userView = let ( initModel, initCmd ) = userInit ( handlers, safeView ) = extractListeners "" (userView initModel) in ( { userModel = initModel , handlers = handlers } , Cmd.batch [ render safeView, Cmd.map Just initCmd ] ) update : (msg -> model -> ( model, Cmd msg )) -> (model -> Html msg) -> Maybe msg -> PrivateModel model msg -> ( PrivateModel model msg, Cmd (Maybe msg) ) update userUpdate view maybeMsg model = case maybeMsg of Just msg -> let ( newModel, newCmd ) = userUpdate msg model.userModel ( handlers, safeView ) = extractListeners "" (view newModel) in ( { userModel = newModel, handlers = handlers } , Cmd.batch [ render safeView, Cmd.map Just newCmd ] ) Nothing -> ( model, Cmd.none ) program userProgram = Platform.program { init = init userProgram.init userProgram.view , update = update userProgram.update userProgram.view , subscriptions = subscriptions userProgram.subscriptions }
To handle the posibility that our dispatcher somehow gets out of sync with the DOM, we have to wrap the users message in a maybe. In the subscriptions call, we use the dispatcher to transform an incoming message (which is in the form of a (keyPath, eventObject) tuple) into the message the user expects. We handle the maybe in the new update function by simply ignoring the Nothing case. In both update and init we now call our extractListeners function to build up the dispatcher data structure.
And here's the updated JavaScript:
function render(struct, out, port) { switch(struct.type) { case "node": var el = document.createElement(struct.name); struct.attributes.forEach(attr => { switch(attr.type) { case "attribute": return el.setAttribute(attr.key, attr.value); case "property": return el[attr.key] = attr.value; case "event": return el.addEventListener(attr.key, e => { port.send([attr.value, e]); if (attr.stopPropagation) { e.stopPropagation(); } if (attr.preventDefault) { e.preventDefault(); } }); } }); out.appendChild(el); struct.children.forEach(child => render(child, el, port)); return; case "text": var el = document.createTextNode(struct.value); out.appendChild(el); return; } } var app = Elm.Main.worker() app.ports.renderPort.subscribe(function(html) { const output = document.getElementById("output"); while (output.firstChild) { output.removeChild(output.firstChild); } render(html, output, app.ports.eventPort); });
And that's basically all it takes to get events coming back in:
Making it Virtual
You may have noticed that we are re-rendering the entire DOM tree on every update. This is both not efficient and also potentially wrong, as it will lose focus on input elements and the like. The solution is to diff a new rendered view with the current one. This would create a bunch of patches that can then be applied to the real DOM for efficient updates.
The nice thing is that all but the application of patches can be done in Elm. So let's get started.
First let's look at describing changes between SafeHtml structures.
type Change = Change Patch | At Int Change | Batch (List Change) type Patch = Redraw SafeHtml | Facts (List ( Bool, SafeAttribute )) | TextChange String | Remove | Insert SafeHtml encodeChange : Change -> Json.Value encodeChange change = ... encodePatch : Patch -> Json.Value encodePatch patch = ...
I've spared you the implementation of the decoders in the interest of brevity. They look just the same as the other decoders.
Now let's take a look at how we might apply these changes to the existing DOM:
function render(struct, port) { switch(struct.type) { case "node": var el = document.createElement(struct.name); applyFacts(struct.attributes, el, port) struct.children.forEach(child => el.appendChild(render(child, port))); return el; case "text": return document.createTextNode(struct.value); } } function applyChange(change, element, port) { switch(change.type) { case "change": return applyPatch(change.patch, element, port); case "at": return applyChange(change.change, element.childNodes[change.index], port); case "batch": return change.changes.forEach(c => applyChange(c, element, port)); } } function applyPatch(patch, out, port) { switch(patch.type) { case "facts": return applyFacts(patch.facts, out, port); case "text": out.nodeValue = patch.value; return; case "redraw": return out.parentNode.replaceChild(render(patch.value, port), out); case "insert": return out.appendChild(render(patch.value, port)); case "remove": return out.parentNode.removeChild(out); } } function applyFacts(facts, el, port) { facts.forEach(attr => { switch(attr.type) { case "attribute": return attr.value == null ? el.removeAttribute(attr.key) : el.setAttribute(attr.key, attr.value); case "property": if (attr.value == null) { delete el[attr.key]; return; } else { el[attr.key] = attr.value; return; } case "event": if (attr.value == null) { el.removeEventListener(attr.key, el[attr.value]); delete el[attr.value]; } else { const handler = e => { port.send([attr.value, e]); if (attr.stopPropagation) { e.stopPropagation(); } if (attr.preventDefault) { e.preventDefault(); } }; el.addEventListener(attr.key, handler); // store a reference to the function so we can remove the handler el['handler-' + attr.value] = handler; } } }); } var app = Elm.Main.worker(); app.ports.renderPort.subscribe(function(change) { const output = document.getElementById("output"); applyChange(change, output, app.ports.eventPort); });
This might seem like a lot of code, but it's fairly simple. The Change datastructure allows to find the node that should change (that's what the At constructor is for). Then we apply one of the 5 possible changes. Note that there are more possible mutations that could increase efficiency, like a reorder change, but we've done only these for simplicity. Facts is a term used in Elm's virtual dom to refer to attributes, properties and event listeners.
Ok, let's try to get something on the screen:
initialRender : SafeHtml -> Cmd (Maybe msg) initialRender = Insert >> Change >> encodeChange >> renderPort type alias PrivateModel model msg = { userModel : model , handlers : Dict String (Decoder msg) , view : SafeHtml } init : ( model, Cmd msg ) -> (model -> Html msg) -> ( PrivateModel model msg, Cmd (Maybe msg) ) init userInit userView = let ( initModel, initCmd ) = userInit ( handlers, safeView ) = extractListeners "" (userView initModel) in ( { userModel = initModel , handlers = handlers , view = safeView } , Cmd.batch [ initialRender safeView, Cmd.map Just initCmd ] )
Note that we store a reference to the current view. Let's do some diffing against it:
wrapAt : Int -> List Change -> List Change wrapAt i changes = case changes of [] -> [] list -> [ At i (batchIfNecessary changes) ] batchIfNecessary : List Change -> Change batchIfNecessary changes = case changes of [] -> -- This should never happen Batch [] x :: [] -> x list -> Batch list diff : SafeHtml -> SafeHtml -> List Change diff before after = if before == after then [] else case ( before, after ) of ( Text bstr, Text astr ) -> [ Change (TextChange astr) ] ( Node bName bAttrs bChildren, Node aName aAttrs aChildren ) -> if aName == bName then let attrsDiff = if aAttrs == bAttrs then [] else List.map2 diffAttrs bAttrs aAttrs |> List.concat |> Facts |> Change |> List.singleton childrenDiff = if bChildren == aChildren then [] else diffChildren 0 bChildren aChildren in [ batchIfNecessary (attrsDiff ++ childrenDiff) ] else [ Change (Redraw after) ] _ -> [ Change (Redraw after) ] diffAttrs : SafeAttribute -> SafeAttribute -> List ( Bool, SafeAttribute ) diffAttrs before after = if before == after then [] else [ ( False, before ), ( True, after ) ] diffChildren : List SafeHtml -> List SafeHtml -> List Change diffChildren index before after = case ( before, after ) of ( [], [] ) -> [] ( b :: efore, [] ) -> At index (Change Remove) :: diffChildren (index + 1) efore after ( [], a :: fter ) -> Change (Insert a) :: diffChildren (index + 1) before fter ( b :: efore, a :: fter ) -> case diff b a of [] -> diffChildren (index + 1) efore fter diffs -> At index (batchIfNecessary diffs) :: diffChildren (index + 1) efore fter
Again a fair amount of code, but the idea is pretty simple - we traverse both trees simultaneously and record changes as we see them. This code could be made more sophisticated/performant, but I tried to not make it too complicated.
I also tried to remove pointless wrapping structures, so that the changes output is easy on the eyes.
OK, let's get the diffing wired up into our update function:
render : SafeHtml -> SafeHtml -> Cmd msg -> Cmd (Maybe msg) render before after cmd = case diff before after of [] -> Cmd.map Just cmd changes -> changes |> batchIfNecessary |> At 0 |> encodeChange |> renderPort |> (\renderCmd -> Cmd.batch [ renderCmd, Cmd.map Just cmd ]) update : (msg -> model -> ( model, Cmd msg )) -> (model -> Html msg) -> Maybe msg -> PrivateModel model msg -> ( PrivateModel model msg, Cmd (Maybe msg) ) update userUpdate view maybeMsg model = case maybeMsg of Just msg -> let ( newModel, newCmd ) = userUpdate msg model.userModel ( handlers, safeView ) = extractListeners "" (view newModel) in ( { userModel = newModel, handlers = handlers, view = safeView }, render model.view safeView newCmd ) Nothing -> ( model, Cmd.none )
And that's basically it:
I've prepared a Gist version for easier reading of the completed source code.
Discussion
Now all this is neat, but so what? I wrote this for a few reasons:
It's meant as a bit more advanced tutorial on some cool ports techniques.
You are not tied to the built in rendering system. If you want to build your own rendering engine in Elm, I've just shown you how. I hope this can encourage some experimentation with cool techniques. One of the popular ones these days is to not diff against a Virtual DOM, but against the real DOM. You can try that pretty easily.
You can customise the way the rendering works. Need a crazy component (say a map library) to work within your tree? All you need to do is implement a new Node constructor.
After reading this, you should be much better equiped to go and read the real implementation. It's much harder to read, but most of the concepts are the same.
However, a few caveats are in order.
This will not beat the native version in performance anytime soon. We need to convert all of the Elm datastructures into native JS ones and that is pretty costly.
I've not widely tested this. It's meant as educational material, not a production ready library.
Hope you enjoy playing with it. Let me know on the Elm slack if you can use this for some cool experiments :)
0 notes
Photo

New Post has been published on https://programmingbiters.com/multi-step-registration-form-using-codeigniter-mysql-jquery/
Multi-step Registration form using Codeigniter, MySQL, jQuery

Sometimes we need to capture lots of user details during registration process and as a result we got long forms on web page. So the best solution is to break the form into smaller logical section and present it into a multi-step registration form. This type of mult-step registration form always improves usability on web page comparing to very long form.
I am going to explain how to convert very long form into multi-step registration form using Codeigniter, MySQL and jQuery.
Prerequisite
Netbeans 8.2 XAMPP in Windows Codeigniter 3.1.7
Go through below steps
Step 1. Create a table called user in MySQL database. Create database roytuts in MySQL server if it does not exist already.
1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE TABLE `users` (
`id`INT(11) NOT NULL AUTO_INCREMENT,
`name`VARCHAR(100) NOT NULL,
`password` VARCHAR(255) NULL,
`email`VARCHAR(100) NULL,
`phone`INT(10) NOT NULL,
`gender`VARCHAR(6) NOT NULL,
`dob`VARCHAR(10) NOT NULL,
`address`VARCHAR(255) NOT NULL,
PRIMARY KEY (`id`)
)
COLLATE=‘utf8_general_ci’
ENGINE=InnoDB;
Step 2. Now first configure Codeigniter 3. with Netbeans 8.2 IDE if you have not configured already. Setup Codeigniter, Netbeans and XAMPP in Windows
Step 3. Open application/config/autoload.php file and autoload few things
1
2
$autoload[‘libraries’] = array(‘database’, ‘form_validation’);
$autoload[‘helper’] = array(‘html’, ‘url’, ‘file’, ‘form’);
Step 4. Open application/config/database.php file and make required changes for database configurations
1
2
3
‘username’ => ‘root’,
‘password’ => ”,
‘database’ => ‘roytuts’,
If you have password for the database then put appropriate password.
Step 5. Create Model class for database operations
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
/**
* Description of User
*
* @author roytuts.com
*/
class UserModel extends CI_Model
private $user_table = ‘users’;
function __construct()
parent::__construct();
function insert_user($name, $password, $email, $phone, $gender, $dob, $address)
$data = array(‘name’ => $name, ‘password’ => md5($password), ’email’ => $email, ‘phone’ => $phone, ‘gender’ => $gender, ‘dob’ => $dob, ‘address’ => $address);
$result = $this->db->insert($this->user_table, $data);
if ($result !== NULL)
return TRUE;
return FALSE;
Step 6. Create Controller class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php
/**
* Description of User
*
* @author roytuts.com
*/
defined(‘BASEPATH’) OR exit(‘No direct script access allowed’);
class UserController extends CI_Controller
function __construct()
parent::__construct();
$this->load->model(‘usermodel’);
public function index()
if ($this->input->post(‘finish’)) required’);
$this->form_validation->set_rules(‘dob’, ‘Date of Birth’, ‘trim else
$this->load->view(‘user’);
Step 7. Create View file for user input
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
<html>
<head>
<title>Multi Step Registration</title>
<style>
body
font-family:tahoma;
font-size:12px;
#signup-step
margin:auto;
padding:0;
width:53%
#signup-step li
list-style:none;
float:left;
padding:5px 10px;
border-top:#004C9C 1px solid;
border-left:#004C9C 1px solid;
border-right:#004C9C 1px solid;
border-radius:5px 5px 0 0;
.active
color:#FFF;
#signup-step li.active
background-color:#004C9C;
#signup-form
clear:both;
border:1px #004C9C solid;
padding:20px;
width:50%;
margin:auto;
.demoInputBox
padding: 10px;
border: #CDCDCD 1px solid;
border-radius: 4px;
background-color: #FFF;
width: 50%;
.signup-error
color:#FF0000;
padding-left:15px;
.message
color: #00FF00;
font-weight: bold;
width: 100%;
padding: 10;
.btnAction
padding: 5px 10px;
background-color: #F00;
border: 0;
color: #FFF;
cursor: pointer;
margin-top:15px;
label
line-height:35px;
</style>
<script src=“http://code.jquery.com/jquery-1.10.2.js”></script>
<script>
�� function validate()
var output = true;
$(“.signup-error”).html(”);
if ($(“#personal-field”).css(‘display’) != ‘none’)
if (!($(“#name”).val()))
output = false;
$(“#name-error”).html(“Name required!”);
if (!($(“#dob”).val()))
output = false;
$(“#dob-error”).html(“Date of Birth required!”);
if ($(“#password-field”).css(‘display’) != ‘none’)
if (!($(“#user-password”).val()))
output = false;
$(“#password-error”).html(“Password required!”);
if (!($(“#confirm-password”).val()))
output = false;
$(“#confirm-password-error”).html(“Confirm password required!”);
if ($(“#user-password”).val() != $(“#confirm-password”).val())
output = false;
$(“#confirm-password-error”).html(“Password not matched!”);
if ($(“#contact-field”).css(‘display’) != ‘none’)
if (!($(“#phone”).val()))
output = false;
$(“#phone-error”).html(“Phone required!”);
if (!($(“#email”).val()))
output = false;
$(“#email-error”).html(“Email required!”);
if (!$(“#email”).val().match(/^([\w–\.]+@([\w–]+\.)+[\w–]2,4)?$/))
$(“#email-error”).html(“Invalid Email!”);
output = false;
if (!($(“#address”).val()))
output = false;
$(“#address-error”).html(“Address required!”);
return output;
$(document).ready(function ()
$(“#next”).click(function ()
var output = validate();
if (output === true)
var current = $(“.active”);
var next = $(“.active”).next(“li”);
if (next.length > 0)
$(“#” + current.attr(“id”) + “-field”).hide();
$(“#” + next.attr(“id”) + “-field”).show();
$(“#back”).show();
$(“#finish”).hide();
$(“.active”).removeClass(“active”);
next.addClass(“active”);
if ($(“.active”).attr(“id”) == $(“li”).last().attr(“id”))
$(“#next”).hide();
$(“#finish”).show();
);
$(“#back”).click(function ()
var current = $(“.active”);
var prev = $(“.active”).prev(“li”);
if (prev.length > 0)
$(“#” + current.attr(“id”) + “-field”).hide();
$(“#” + prev.attr(“id”) + “-field”).show();
$(“#next”).show();
$(“#finish”).hide();
$(“.active”).removeClass(“active”);
prev.addClass(“active”);
if ($(“.active”).attr(“id”) == $(“li”).first().attr(“id”))
$(“#back”).hide();
);
$(“input#finish”).click(function (e)
var output = validate();
var current = $(“.active”);
if (output === true)
return true;
else
//prevent refresh
e.preventDefault();
$(“#” + current.attr(“id”) + “-field”).show();
$(“#back”).show();
$(“#finish”).show();
);
);
</script>
</head>
<body>
<ul id=“signup-step”>
<li id=“personal” class=“active”>Personal Detail</li>
<li id=“password”>Password</li>
<li id=“contact”>Contact</li>
</ul>
<?php
if (isset($success))
echo ‘User record inserted successfully’;
$attributes = array(‘name’ => ‘frmRegistration’, ‘id’ => ‘signup-form’);
echo form_open($this->uri->uri_string(), $attributes);
?>
<div id=“personal-field”>
<label>Name</label><span id=“name-error” class=“signup-error”></span>
<div><input type=“text” name=“name” id=“name” class=“demoInputBox”/></div>
<label>Date of Birth</label><span id=“dob-error” class=“signup-error”></span>
<div><input type=“text” name=“dob” id=“dob” class=“demoInputBox”/></div>
<label>Gender</label>
<div>
<select name=“gender” id=“gender” class=“demoInputBox”>
<option value=“male”>Male</option>
<option value=“female”>Female</option>
</select>
</div>
</div>
<div id=“password-field” style=“display:none;”>
<label>Enter Password</label><span id=“password-error” class=“signup-error”></span>
<div><input type=“password” name=“password” id=“user-password” class=“demoInputBox” /></div>
<label>Re–enter Password</label><span id=“confirm-password-error” class=“signup-error”></span>
<div><input type=“password” name=“confirm-password” id=“confirm-password” class=“demoInputBox” /></div>
</div>
<div id=“contact-field” style=“display:none;”>
<label>Phone</label><span id=“phone-error” class=“signup-error”></span>
<div><input type=“text” name=“phone” id=“phone” class=“demoInputBox” /></div>
<label>Email</label><span id=“email-error” class=“signup-error”></span>
<div><input type=“text” name=“email” id=“email” class=“demoInputBox” /></div>
<label>Address</label><span id=“address-error” class=“signup-error”></span>
<div><textarea name=“address” id=“address” class=“demoInputBox” rows=“5” cols=“50”></textarea></div>
</div>
<div>
<input class=“btnAction” type=“button” name=“back” id=“back” value=“Back” style=“display:none;”>
<input class=“btnAction” type=“button” name=“next” id=“next” value=“Next” >
<input class=“btnAction” type=“submit” name=“finish” id=“finish” value=“Finish” style=“display:none;”>
</div>
<?php echo form_close(); ?>
</body>
</html>
Step 8. Open file application/config/routes.php and change the default controller
$route['default_controller'] = 'usercontroller';
Step 9. Now run the application

If you try to submit the form without filling data then you will see validation errors.
Once you fill the form with all input fields and you will find one row has been inserted into the database table and you will see the below response above the form
User record inserted successfully
Thanks for reading.
0 notes
Text
N screen, Cloud가 필요없는 사람들
N screen이나 Cloud 이야기가 많이 나왔던 것에 비하여 이 영역에서 크게 성공한 서비스가 많지 않은 것 같다. 멋들어진 사���는 많지만, 정말 대중적인 성공은 얼마나 되는가?
N screen의 중요성은 스마트폰이 대중화되면서 대두되었던 것으로 기억한다. 얼마전까지만 해도 '컴퓨터'라는 것은 한 가정에 하나 있을까 말까 한 것이었는데, 이제는 컴퓨터라고 불리는 것들이 너무나 많아졌다. (모든 가정이 그런건 절대 아니지만) 거실에 데스크탑, 각자가 가지고 있는 랩탑, 주머니 속에는 스마트폰, '소파에 앉은' 시간까지 쪼개어 비집고 들어온 태블릿까지. 컴퓨터가 많아지니 이 얼마나 불안한가? 뭔가 하나로 묶어야한다는 인식이 자리잡기 시작한다.
서로 파일은 어떻게 주고받지? 사진은? 설정은?
하면서, 서로 'Seamless'하게 왔다갔다 하는게 필요하다고 생각하기 시작했을 것이다. 많은 사람들은 이걸 기회라고 생각했을테고... 그런데 그 중심에 어쩌면 이런 생각이 숨어있을지도 모른다.
'스마트폰으로 할 일이 있고, PC로 할 일이 있다'
그렇지... 스마트폰은 화면이 작고 입력하기가 어려우니까, 결국 PC에서 해야하는 일은 따로 있는데, 이걸 매끄럽게 하려면 스마트폰과 PC를 어떻게든 싱크시켜야한다고 생각한 것이다. 스마트폰은 화면이 작고 입력이 어렵다. 적어도 나에게는.
하지만, 모바일 디바이스를 손에 쥐고 스스로를 혹독하게 훈련시킨 이들은, 모바일 상에서 뭔가를 하는데 별로 불편함을 느끼지 않는다는 것을 알게되었다. (이들은 너무나 바빠서 'PC 앞에 앉아 볼' 시간이 없었고, 유일하게 허락된 컴퓨터였던 스마트폰에 온 힘을 기울였다) 내 키보드 타자 속도와 그들의 스마트폰 타이핑 속도가 별반 차이가 나지 않았고, '위치'를 기억하고 움직이는 그들의 손놀림은 나의 마우스 포인팅보다 훨씬 빨랐다. 순식간에 3~4개의 카톡 대화방을 만들고 3~4명과 동시에 채팅을 하는 것이, 그 작은 디바이스 안에서 일어나는 일이었다. 평소 PC에 메신저 창을 3~4개 띄워놓고 대화하는, 나의 그 모습이 그대로 거기 있었다. (이것이 포스트 휴먼인가...)
이들에겐 N screen이나 Cloud가 한동안 필요하지 않을 것 같다. 왜냐면 이들은, PC에서 할 일과 모바일에서 할 일을 구분하지 않으며, 모든 일을 모바일에서 시작해서 모바일에서 끝낸다. 생산, 수정, 저장, 전송까지 모두 다. 내가 당연히 PC에서 할 거라고 생각했던 일들을 모바일에서 다 하고 있었으며, 불편��하기는 커녕 PC보다 훨씬 편하다고 했다. 이들은 현재 살아가는데 있어서 데스크탑이나 랩탭을 켜지 않아도 문제가 없다. (중학생, 고등학생, 대학생 저학년 정도임) 이들은 데스크탑을 켤 때 오히려 생경함을 느낀다고 했다. 페이스북도, 트위터도 데스크탑 화면으로 보면 너무 복잡해서 뭘 해야할지 모른다고 한다.
혹시, N screen 서비스는, 모바일이 끝끝내 불편하고 답답한 이들을 위한 위안으로 남는 것은 아닐까. (에버노트의 팬인 나 역시도 아마 끝끝내 불편하고 답답한 한 사람이리라)
3 notes
·
View notes
Photo

New Post has been published on http://programmingbiters.com/laravel-5-login-with-linkedin-using-socialite-package/
Laravel 5 login with Linkedin using Socialite Package
Laravel 5 provide very easy way to implement login with your Linkedin account and register with your Linkedin id. Laravel 5 provide us Socialite package that is help to social authentication. In this post we will make example same as like bellow preview and you can do that easily by using few following step:
Step 1: Installation
In first step we will install Socialite Package that provide Linkedin api to connect with Linkedin account. So, first open your terminal and run bellow command:
composer require laravel/socialite
After install above package we should add providers and aliases in config file, Now open config/app.php file and add service provider and aliase.
'providers' => [ .... LaravelSocialiteSocialiteServiceProvider::class, ], 'aliases' => [ .... 'Socialite' => LaravelSocialiteFacadesSocialite::class, ],
Step 2: Create Linkedin App
In this step we need Linkedin client id and secret that way we can get information of other user. so if you don’t have Linkedin app account then you can create from here : Linkedin Developers App. you can find bellow screen :
Now you have to click on Create Application and fill form details and create new application. after created application you can see following slide:
and you can copy client id and secret.
Now you have to set app id, secret and call back url in config file so open config/services.php and set id and secret this way:
config/services.php
return [ .... 'linkedin' => [ 'client_id' => 'id', 'client_secret' => 'secret', 'redirect' => 'http://learnl52.hd/auth/linkedin/callback' ], ]
Step 3: Create Linkedin Login
In this step first we have to create migration for add linkedin_id in your user table. so le’s craete new migration and bellow column this way:
Migration
Schema::table('users', function ($table) $table->string('linkedin_id'); );
After adding linkedin_id column first we have to add new route for linkedin login. so let’s add bellow route in routes.php file.
app/Http/routes.php
Route::get('linkedin', function () return view('linkedinAuth'); ); Route::get('auth/linkedin', 'AuthAuthController@redirectToLinkedin'); Route::get('auth/linkedin/callback', 'AuthAuthController@handleLinkedinCallback');
After add route, we need to add method of linkedin auth that method will handle linkedin callback url and etc, first put bellow code on your AuthController.php file.
app/Http/Controllers/Auth/AuthController.php
namespace AppHttpControllersAuth; use AppUser; use Validator; use AppHttpControllersController; use IlluminateFoundationAuthThrottlesLogins; use IlluminateFoundationAuthAuthenticatesAndRegistersUsers; use Socialite; use Auth; use Exception; class AuthController extends Controller use AuthenticatesAndRegistersUsers, ThrottlesLogins; protected $redirectTo = '/'; public function __construct() $this->middleware('guest', ['except' => 'logout']); protected function validator(array $data) max:255 protected function create(array $data) return User::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => bcrypt($data['password']), ]); public function redirectToLinkedin() return Socialite::driver('linkedin')->redirect(); public function handleLinkedinCallback() try $user = Socialite::driver('linkedin')->user(); $create['name'] = $user->name; $create['email'] = $user->email; $create['linkedin_id'] = $user->id; $userModel = new User; $createdUser = $userModel->addNew($create); Auth::loginUsingId($createdUser->id); return redirect()->route('home'); catch (Exception $e) return redirect('auth/linkedin');
Ok, now at last we need to add blade view so first create new file linkedinAuth.blade.php file and put bellow code:
resources/views/linkedinAuth.blade.php
@extends('layouts.app') @section('content') <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <div class="panel panel-default"> <div class="panel-heading">Login</div> <div class="panel-body"> <form class="form-horizontal" role="form" method="POST" action=" url('/login') "> !! csrf_field() !! <div class="form-group"> <label class="col-md-4 control-label">E-Mail Address</label> <div class="col-md-6"> <input type="email" class="form-control" name="email" value=" old('email') "> </div> </div> <div class="form-group"> <label class="col-md-4 control-label">Password</label> <div class="col-md-6"> <input type="password" class="form-control" name="password"> </div> </div> <div class="form-group"> <div class="col-md-6 col-md-offset-4"> <div class="checkbox"> <label> <input type="checkbox" name="remember"> Remember Me </label> </div> </div> </div> <div class="form-group"> <div class="col-md-6 col-md-offset-4"> <a href=" url('auth/linkedin') " class="btn btn-primary"> <strong>Login With Linkedin</strong> </a> <button type="submit" class="btn btn-primary"> Login </button> <a class="btn btn-link" href=" url('/password/reset') ">Forgot Your Password?</a> </div> </div> </form> </div> </div> </div> </div> </div> @endsection
Ok, now you are ready to use open your browser and check here : URL + ‘/linkedin’.
قالب وردپرس
0 notes