Relation is DS.PromiseMany instead model and data - ajax

What I need is:
1) Represent list of partners
App.Partner= DS.Model.extend(
{
name: attr(),
site: attr(),
contacts: DS.hasMany('partner-contact', { async: true, polymorphic: true })
}
);
2) and names of partners contacts
App.PartnerContact = DS.Model.extend(
{
name: attr(),
phone: attr(),
post: attr(),
email: attr(),
partnerId: attr(),
partner: DS.belongsTo('partner')
}
);
So I have route:
App.CallsMyContactsRoute = Ember.Route.extend({
//needs: "partner-contacts",
model: function () {
return this.store.find('partner');
}
});
And template:
{{#each partner in model}}
<tr class="gradeA even" role="row">
<td class="sorting_1">{{partner.name}}</td>
<td>{{partner.name}}</td>
<td>{{partner.phone}}</td>
<td class="center">
{{partner.contacts}}
{{#each contact in partner.contacts}}
{{contact.name}}
{{/each}}
</td>
<td class="center">A</td>
</tr>
{{/each}}
But {{partner.contacts}} is DS.PromiseManyArray
And it ever doesn't make a query to server.
Answer from server (Partners)
{
"Partner":[
{
"id":4,
"name":"CompanyNamej",
"site":"www.no-site.ru"
},
{
"id":13,
"name":"test",
"site":"sdasdasd"
},
{
"id":14,
"name":"",
"site":""
}
]
}
What is the problem?
DEBUG: Ember : 1.10.0
vendor.js:16928DEBUG: Ember Data : 1.0.0-beta.15
vendor.js:16928DEBUG: jQuery : 2.1.3

Related

Button click not working in Vue in datatable ajax render

I am a newbie in Vuejs.
I have a users table which is showing data using Server-side processing in Datatable. I am trying to add a click event which will call a vue function. But the click function is not working at all. I tried use these methods. But none of them are working.
v-on:click="functionName"
v-on:click="$emit('functionName')"
#click="functionName"
HTML part
<table class="table table-bordered data-table dataTable-load">
<thead class="thead-light">
<tr>
<th style="width: 80px;">No</th>
<th>Name</th>
<th>Email</th>
<th>Phone</th>
<th>Type</th>
<th>Created</th>
<th>Last Updated</th>
<th width="280px">Action</th>
</tr>
</thead>
<tbody></tbody>
</table>
<div>
<span v-html='data'></span>
</div>
SCRIPT part
var dt = $('.dataTable-load').DataTable({
processing: true,
serverSide: true,
ajax: {
url: "{{ url('user/getdata') }}",
type: "post",
data: {"_token": "{{ csrf_token() }}"}
},
columns: [
{name: 'id', data: 'id', },
{name: 'name', data: 'name'},
{name: 'email', data: 'email'},
{name: 'phone', data: 'phone'},
{name: 'usertype', data: 'usertype',
"render": function (data, type, row) {
if (row.usertype == 'M') {
return 'Manager';
} else {
return 'Staff';
}
}
},
{name: 'created_at', data: 'created_at'},
{name: 'updated_at', data: 'updated_at'},
{
title: 'msg',
"render": function (data, type, row) {
return '<button v-on:click="showModal" #click="showModal">the button</button>';
}
}
]
});
dt.on('order.dt search.dt', function () {
dt.column(0, {search: 'applied', order: 'applied'}).nodes().each(function (cell, i) {
cell.innerHTML = i + 1;
});
}).draw();
App.js
const app = new Vue({
el: '#app',
data: {
data: '',
},
methods: {
showModal() {
this.data = 'Now click on me <a href=\'#\' #click.prevent=\'alert("yo")\'> here </a>';
},
},
});
Please let me know how to do it correctly. Thanking you in advance.

How to show nested array data in vuejs component

I'm running the following query:
$users=User::with('roles.permissions')->get()->unique();
this query returns result set:
Array = [
{
created_at: "2019-01-11 09:27:02",
deleted_at: null,
email: "admin#example.com",
email_verified_at: null,
id: 1,
name: "ADMIN",
roles: [
{id: 1, name: "Admin", slug: "Admin", description: "This is Super-Admin Role", created_at: "2019-01-11 09:27:02",
permissions: [
{id:1, name:"Create,slug:"Create"},
{id:1, name:"Read",slug:"Read"},
{id:1, name:"Delete",slug:"Delete"},
],
},
],
},
]
returns user details with roles I want to show this result set in my Vue Component table.
this my vue component read method
read:function(){
axios.get('/userlist')
.then(response=>{
console.log(response.data);
})
}
This is my table
<table class="table table-bordered">
<thead>
<th>no.</th>
<th>Name</th>
<th>E-mail</th>
<th>Roles</th>
<th>Permissions</th>
<th>Action</th>
</thead>
<tbody>
<tr v-for="(user,key) in users">
<td>{{++key}}</td>
</tr>
</tbody>
</table>
How to show user,roles and permissions separately in html table.
You should store your API result in your component data. But you need to prepare your component data to receive your users.
data() {
return {
users: []
}
}
Now, you should make your function update this brand new data.
read:function(){
axios.get('/userlist')
.then(response=>{
this.users = response.data;
})
}
Now, I assume that you want to display user's roles as a concatenated string. Then, you need a function to do this.
methods: {
getRoles: function(roles) {
let rolesString = ''
roles.forEach((role, index) => {
if (index != 0)
rolesString += ', '
rolesString = rolesString + role.name
})
return rolesString
},
getPermissionsFromRoles: function(roles) {
let permissionsList = []
roles.permissions.forEach((permission) => {
if (permissionsList.indexOf(permission.name) != -1) {
permissionsList.push(permission.name)
}
})
let permissionsString = ''
if (permissionsList.length > 0) {
permissionsList.forEach((permission, index) => {
if (index != 0)
permissionsString += ', '
permissionsString += permission
})
}
return permissionsString
}
}
Then, you can use this function in your template to handle your user roles.
<table class="table table-bordered">
<thead>
<th>no.</th>
<th>Name</th>
<th>E-mail</th>
<th>Roles</th>
<th>Permissions</th>
</thead>
<tbody>
<tr v-for="(user,key) in users">
<td>{{key}}</td>
<td>{{user.name}}</td>
<td>{{user.email}}</td>
<td>{{getRoles(user.roles)}}</td>
<td>{{getPermissionsFromRoles(user.roles)}}</td>
</tr>
</tbody>
</table>
rendering in the template will look like:
<td v-for="role in roles">{{role}}</td>
you will also need to have roles in your data:
data() {
return {
roles: []
}
}
and finally make your function update the data
function(){
.then(response=>{
this.roles = response.data
})
}

How to use vuetify's custom sort?

I'd like to use custom-sort in my data table. My goal is to sort the table DESC as opposed to the default ASC. But I don't know-how.
This is the start of my data table component:
<v-data-table
:headers="headers"
:items="acts"
hide-actions
class="elevation-1"
>
<template slot="items" slot-scope="props">
<td>{{ props.item.id }}</td>
<td>{{ props.item.name }}</td>
<td class="text-xs-center">{{ props.item.provider.id }}</td>
<td class="text-xs-center">{{ props.item.category.name }}</td>
<td class="text-xs-center">{{ props.item.budget }}</td>
<td class="text-xs-center">{{ props.item.location.name }}</td>
<td class="text-xs-center">{{ props.item.deets }}</td>
<td class="text-xs-center">{{ props.item.keeping_it_100 }}</td>
<td class="text-xs-center"><img width="50" height="50" :src="props.item.inspiration.inspiration"></td>
<td class="justify-center layout px-0">....
And this is the script I'm using:
<script>
export default {
data () {
return {
dialog: false,
customerSort: {
isDescending: true,// I tried this? as the kabab format throws an error
},
headers: [
{ text: 'ID', value: 'id'},
{ text: 'Name', value: 'name' },
{ text: 'Provider', value: 'provider' },
{ text: 'Category', value: 'category' },
{ text: 'Budget', value: 'budget' },
{ text: 'Country', value: 'location', sortable: true },
{ text: 'Keeping it 100%', value: 'keeping_it_100', sortable: false },
{ text: 'deets', value: 'deets', sortable: false },
{ text: 'inspiration', value: 'inspiration', sortable: false },
{ text: 'Cover', value: 'cover', sortable: false },
{ text: 'Actions', value: 'actions', sortable: false }
],
According to docs it is a function prop. But I haven't found an example on how to pass it.
This is a screenshot of the function...
You can use a function like this-
customSort(items, index, isDesc) {
items.sort((a, b) => {
if (index === "date") {
if (!isDesc) {
return compare(a.date, b.date);
} else {
return compare(b.date, a.date);
}
}
});
return items;
}
Where the compare is a function which compares a.date and b.date and returns 1 or -1
isDesc is a variable passed by the table which tells in what order does the user want to sort it. If you want to sort in desc, just use !isDesc in the if-else condition
To use this in your template just use
<v-data-table
:headers="headers"
:items="Data"
:custom-sort="customSort"
>
<template slot="items" slot-scope="props">
<td class="font-weight-black">{{ props.item.date }}</td>
<td class="text-xs-right">{{ props.item.time }}</td>
<td class="text-xs-right">{{ props.item.name }}</td>
</template>
</v-data-table>
To make sure your other fields still work with the normal sort use
customSort(items, index, isDesc) {
items.sort((a, b) => {
if (index === "date") {
if (!isDesc) {
return dateHelp.compare(a.date, b.date);
} else {
return dateHelp.compare(b.date, a.date);
}
} else {
if (!isDesc) {
return a[index] < b[index] ? -1 : 1;
} else {
return b[index] < a[index] ? -1 : 1;
}
}
});
return items;
}
Although it's an old question ...
For special sorting of only one column, you could use the property sort in the headers array.
See also https://vuetifyjs.com/en/api/v-data-table/#headers
Like so:
// in data ...
headers: [
...
{
text: "Date",
sortable: true,
value: "date",
sort: (a, b) => a.time_stamp - b.time_stamp
},
...
]
use it like
<v-data-table
:headers="headers"
...
>
Based on this answer code about custom-filter, I tried using custom-sort.
Please refer to this answer if you apply it to your code.
By the following code, I have confirmed sorting when I click 'Calories' header.
My CodePen
new Vue({
el: '#app',
data() {
return {
food: [
{ name: 'Bakchoi', type: 'vegetable', calories: 100 },
{ name: 'Pork', type: 'meat', calories: 200 },
{ name: 'Chicken Thigh', type: 'meat', calories: 300 },
{ name: 'Watermelon', type: 'fruit', calories: 10 },
],
headers: [
{ text: 'Name', align: 'left', value: 'name' },
{ text: 'Food Type', align: 'left', value: 'type' },
{ text: 'Calories', align: 'left', value: 'calories' },
],
search: '',
};
},
methods: {
customSort(items, index, isDescending) {
// The following is informations as far as I researched.
// items: 'food' items
// index: Enabled sort headers value. (black arrow status).
// isDescending: Whether enabled sort headers is desc
items.sort((a, b) => {
if (index === 'calories') {
if (isDescending) {
return b.calories - a.calories;
} else {
return a.calories - b.calories;
}
}
});
return items;
}
}
})
<script src="https://unpkg.com/vue#2.4.2/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#0.15.2/dist/vuetify.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vuetify#0.15.2/dist/vuetify.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons">
<div id="app">
<v-app>
<v-select
label="Food Type"
:items="['vegetable', 'meat', 'fruit']"
v-model="search"
></v-select>
<v-data-table
:headers="headers"
:items="food"
:search="search"
:custom-sort="customSort"
hide-actions
>
<template slot="items" scope="{ item }">
<td>{{ item.name }}</td>
<td>{{ item.type }}</td>
<td>{{ item.calories }}</td>
</template>
</v-data-table>
</v-app>
</div>
NOTE: the following answer is for Vuetify 1.5.x
A little late to the party here, if all you want to do is sort descending by a single field, then custom-sort it not what you want to use, you're better off using the :pagination.sync prop
Custom sort is used when you want to change the behaviour of the comparison function (e.g sorting based off the reverse or lowercase version of a string, or proper sorting of date strings in the format 'DD-MM-YYYY').
If you want to use the default descending functionality, use the :pagination.sync prop, like so:
<v-data-table
:headers="headers"
:items="acts"
:pagination.sync="pagination"
>
<template v-slot:items="props">...</template>
</v-data-table>
In your script, set pagination:
data () {
return {
pagination: {
sortBy: 'id', // The field that you're sorting by
descending: true
}
}
}
This specifies that you want the table to be initially sorted by descending id - id can be changed to any field name in the dataset.
It's worth noting that this only specifies the default behaviour, and if you have sorting enabled for your other headers, users can still sort the table by any field.
To Build on the response provided by bhaskar
I had to edit the last code sample to the following in order to work on vuetify 2.x. The code sorts the date columns by their epoch time which is stored under the time_stamp key. The code also allows the default sorting of numbers and strings (strings are sorted alphabetically)
customSort(items, index, isDesc) {
items.sort((a, b) => {
if (index[0] == "date") {
if (!isDesc[0]) {
return a.time_stamp - b.time_stamp;
} else {
return b.time_stamp - a.time_stamp;
}
} else if (!(isNaN(a[index[0]]))) {
if (!isDesc[0]) {
return (a[index[0]] - b[index[0]]);
} else {
return (b[index[0]] - a[index[0]]);
}
} else {
if (!isDesc[0]) {
return (a[index[0]] < b[index[0]]) ? -1 : 1;
} else {
return (b[index[0]] < a[index[0]]) ? -1 : 1;
}
}
});
return items;
}
In vuetify 2 just use sortBy="date" and update: sort-desc
<v-data-table
:headers="headers"
:items="acts"
:pagination.sync="pagination"
sortBy="date"
update: sort-desc
>

VueJs 2 click doesn't seem to work

I'm not getting any errors and it's compiling, so I'm not sure what I'm doing wrong. I've searched on the subject with no success.
I have BulkExpenses.vue which pulls and shows some expense records, then BulkExpense.vue is a nested component that displays each record. I want to click the trash icon and have it emit the expense ID back to the parent component to run a delete script. I'm doing this in another project and it's working perfectly, so I'm not sure what I'm doing wrong here.
There's also a Lookup component which is a TypeAhead for pulling trips to connect to the Expense, but that's working.
BulkExpenses.vue
<template>
<div>
<form action="#" #submit.prevent="createBulkExpense()" class="publisher bt-1 border-fade bg-white" autocomplete="off">
<table>
<thead>
<tr>
<td><input v-model="bulk_name" type="text" placeholder="Name"></td>
<td><input id="bulk_expensed_at" type="text" name="bulk_expensed_at" placeholder="Date Expense Incurred"></td>
<td><input id="bulk_type_id" type="text" name="bulk_type" placeholder="Type"></td>
<td>
<lookup
source="/api/lookup/trip"
placeholder="Search your trips"
filter-key="name"
:start-at="3"
v-on:selected-link="onSelectedTrip"
:modellink="modellink">
</lookup>
</td>
<!-- <td><input id="bulk_tags" type="text" name="bulk_tags" placeholder="Tags"></td> -->
<td><input id="bulk_vendor" type="text" name="bulk_vendor" placeholder="Vendor"></td>
<td><input id="bulk_method" type="text" name="bulk_method" placeholder="Payment Method"></td>
<td><input id="bulk_total" type="text" name="bulk_total" placeholder="Total Amount"></td>
<td><input id="bulk_paidby_user_id" type="text" name="bulk_paidby" placeholder="Paid By"></td>
<td><input id="bulk_status" type="text" name="bulk_status" placeholder="Payment Status"></td>
<td><input id="bulk_notes" type="text" name="bulk_notes" placeholder="Notes"></td>
</tr>
</thead>
<tbody>
<expense v-for="expense in expenses"
:key="expense.id"
:expense="expense"
#expense-deleted="deleteExpense($event)">
</expense>
</tbody>
</table>
</form>
</div>
</template>
<script>
// import CommentsManager from './CommentsManager.vue';
var axios = require("axios");
import lookup from './Lookup.vue';
import expense from './BulkExpense.vue';
export default {
components: {
lookup, expense
},
data: function() {
return {
modellink: {
"name": "n/a",
"description": "",
"id": null,
"model": "n/a"
},
bulk_trip: {
"name": "n/a",
"description": "",
"id": null
},
selectName: "",
bulk_name: "",
bulk_expensed_at: "",
bulk_type: "",
bulk_tags: "",
bulk_vendor: "",
bulk_method: "",
bulk_total: "",
bulk_paidby: {
name: "",
id: ""
},
bulk_status: "",
bulk_notes: "",
expense: {
id: 1,
name: "",
expensed_at: "",
type: {
id: "",
name: ""
},
trip: {
id: "",
name: ""
},
tags: [],
vendor: "",
method: "",
total: "",
paidby: {
id: "",
name: ""
},
status: {
id: "",
name: ""
},
notes: ""
},
expenses: [
{
id: 1,
name: "",
expensed_at: "",
type: {
id: "",
name: ""
},
trip: {
id: "",
name: ""
},
tags: [],
vendor: "",
method: "",
total: "",
paidby: {
id: "",
name: ""
},
status: {
id: "",
name: ""
},
notes: ""
}
]
};
},
created() {
this.fetchExpenses();
},
methods: {
// onSelectedLink: function (talink) {
// // alert(JSON.stringify(talink.description, null, 4));
// this.modellink = talink
// },
onSelectedTrip: function (talink) {
// alert(JSON.stringify(talink.description, null, 4));
this.bulk_trip = talink
this.modellink = talink
},
fetchExpenses() {
axios.get('/api/expense').then((res) => {
//alert(JSON.stringify(res.data[0], null, 4));
this.expenses = res.data;
});
},
createExpense() {
axios.post('/api/expense', {name: this.expense.name, vessel_id: Laravel.vesselId, expensed_at: this.expense.expensed_at })
.then((res) => {
this.expense.content = '';
// this.expense.user_id = Laravel.userId;
// this.task.statuscolor = '#ff0000';
this.edit = false;
this.fetchExpenses();
})
.catch((err) => console.error(err));
},
deleteExpense(expense) {
console.log(expense.id);
alert(expense.id);
axios.delete('/api/expense/' + expense.id)
.then((res) => {
this.fetchExpenses()
})
.catch((err) => console.error(err));
},
}
}
</script>
BulkExpense.vue
<template>
<tr>
<td><input v-model="expense.name" type="text" name="name"></td>
<td><input v-model="expense.expensed_at"></td>
<td v-if="expense.type"><input v-model="expense.type.name"></td>
<td>
<trip-select v-bind:tripId="expense.trip_id" selectName="trip_id"></trip-select>
</td>
<td><input v-model="expense.vendor"></td>
<td><input v-model="expense.method"></td>
<td><input v-model="expense.total"></td>
<td v-if="expense.paidby"><input v-model="expense.paidby.name" ></td>
<td v-if="expense.status"><input v-model="expense.status.name" ></td>
<td><input v-model="expense.notes"></td>
<td>
<a class="text-lighter hover-light" v-on:click="deleteExpense" href="#"><i class="fas fa-trash"></i></a>
</td>
</tr>
</template>
<script>
import TripSelect from './TripSelect.vue';
import typeahead from './Typeahead.vue';
export default {
name: 'expense',
components: { TripSelect, typeahead },
props: {
bulk_name: {
type: String,
required: false
},
expense: {
required: true,
type: Object,
default: function () {
return {
id: 1,
name: "",
expensed_at: "",
type: {
id: "",
name: ""
},
trip: {
id: "",
name: ""
},
tags: [],
vendor: "",
method: "",
total: "",
paidby: {
id: "",
name: ""
},
status: {
id: "",
name: ""
},
notes: ""
}
}
}
},
data() {
return {
}
},
methods: {
deleteExpense() {
alert(this.expense.id)
this.$emit('expense-deleted', {
'id': this.expense.id,
});
}
}
}
</script>
While it may not directly answer your question, this will for sure work. Also, this solves a fundamental problem you have which is an example of an anti-pattern.
Instead of creating another method inside of your component that emits to the parent, just emit from the click handler in the child. Also, just send the expense back and forth, so you're not property-juggling:
No need to declare $event as you can already have it by default if you want it.
<expense v-for="expense in expenses" :key="expense.id" :expense="expense" #expense-deleted="deleteExpense"></expense>
Then in your click handler, just emit the action and pass the expense back:
<a class="text-lighter hover-light"
#click="$emit('expense-deleted', expense)"
href="#">
<i class="fas fa-trash"></i>
</a>
This will address those fundamental problems above and likely your issue in the process.
Also I see you mixing v-on:click and #click, but they're the same thing, one is just shorthand for the other. I'd suggest just #click for cohesion.
Finally, here's an MCVE for this approach.
Edit
Just in case you're curious, you can delete it from your existing collection after your promise resolves by doing this:
this.expenses.slice(this.expenses.findIndex(o => o.id === expense.id), 1)
Since our expense will always exist in our collection, we never have to worry about checking for existence, so you can use a one liner.
Or you can replace the entire array, but you'll need to use Vue.set for this:
Vue.set(this, 'expenses', this.expenses.filter(o => o.id !== expense.id))

How to use server side option in Angular DataTables with the Angular way example?

I'm trying to use Angular DataTables with the server side processing option, but when I try to enable it in their "Angular way example", only the first request gets rendered, the subsequent requests (paging, ordering, searching) are sent but they never update the table.
After a little digging, I found an unrelated user contributed note that suggests that you override the ajax option with your own function to handle the server side call.
The trick here is to return an empty array to the DataTables callback, so it won't use its renderer to render the table. That will be done by Angular. It's also a good idea to specify the columns names to the server.
ngOnInit(): void {
var that = this;
this.dtOptions = {
pagingType: 'full_numbers',
serverSide: true,
processing: true,
ajax: (dataTablesParameters: any, callback) => {
that.http
.post<DataTablesResponse>('/api/Persons', dataTablesParameters, {})
.subscribe(resp => {
that.persons = resp.data;
callback({
recordsTotal: resp.recordsTotal,
recordsFiltered: resp.recordsFiltered,
data: [],
});
});
},
columns: [
{ data: "id" },
{ data: "firstName" },
{ data: "lastName" },
],
};
}
Since DataTables will think there are no rows to display, it will show the "No data available" message. The simplest way to handle it is to hide it with CSS. You can add it to your global styles.css:
.dataTables_empty {
display: none;
}
then show it yourself in the template:
<tr *ngIf="persons?.length == 0">
<td colspan="3" class="no-data-available">No data!</td>
</tr>
So here's the complete code. Tested with Angular 5.0.0, datatables.net 1.10.16 and angular-datatables 5.0.0:
angular-way-server-side.component.ts
import { Component, OnInit } from '#angular/core';
import { HttpClient, HttpResponse } from '#angular/common/http';
import { DataTablesResponse } from '../datatables/datatables-response';
import { Person } from './person';
#Component({
selector: 'app-angular-way-server-side',
templateUrl: 'angular-way-server-side.component.html',
styleUrls: ['angular-way-server-side.component.css'],
})
export class AngularWayServerSideComponent implements OnInit {
dtOptions: DataTables.Settings = {};
persons: Person[];
constructor(private http: HttpClient) { }
ngOnInit(): void {
var that = this;
this.dtOptions = {
pagingType: 'full_numbers',
serverSide: true,
processing: true,
ajax: (dataTablesParameters: any, callback) => {
that.http
.post<DataTablesResponse>('/api/Persons', dataTablesParameters, {})
.subscribe(resp => {
that.persons = resp.data;
callback({
recordsTotal: resp.recordsTotal,
recordsFiltered: resp.recordsFiltered,
data: [],
});
});
},
columns: [
{ data: "id" },
{ data: "firstName" },
{ data: "lastName" },
],
};
}
}
angular-way-server-side.component.html
<table datatable [dtOptions]="dtOptions" class="row-border hover">
<thead>
<tr>
<th>ID</th>
<th>First name</th>
<th>Last name</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let person of persons">
<td>{{ person.id }}</td>
<td>{{ person.firstName }}</td>
<td>{{ person.lastName }}</td>
</tr>
<tr *ngIf="persons?.length == 0">
<td colspan="3" class="no-data-available">No data!</td>
</tr>
</tbody>
</table>
angular-way-server-side.component.css
.no-data-available {
text-align: center;
}
person.ts
export class Person {
id: number;
firstName: string;
lastName: string;
}
datatables-response.ts
export class DataTablesResponse {
data: any[];
draw: number;
recordsFiltered: number;
recordsTotal: number;
}
src/styles.css
.dataTables_empty {
display: none;
}
The server side is implemented pretty much the same way as if you were using DataTables with JavaScript/jQuery. You can see a very simple sample implementation in PHP.

Resources