How can I bind kendo ui grid to its datasource in a this MVC SPA? - mvvm

Using the following source as a template: https://github.com/Simba-Mupfunya/Kendo-UI-SPA-Template-vs2013-MVC5
I am trying to bind other Kendo UI controls like the grid and scheduler to the model in the javascript "code behind". What I have tried...
HTML:
<div data-role="grid"
date-scrollable="true"
data-editable="true"
data-toolbar="['create', 'save']"
data-columns="[
{ 'field': 'Name', 'width': 50 }
, { 'field': 'Phone' }
, { 'field': 'Email' }
]"
data-bind="source: contacts,
visible: isVisible,
events: {
save: onSave
}"
style="height: 200px">
</div>
js:
define([
'text!views/contacts/contacts.html'
], function (html) {
var contactDataSource = new kendo.data.DataSource({
data: [
{ Name: "Jim Dandy", Phone: "555-1234", Email: "jim.dandy#gmail.com" }
, { Name: "Joe Coffee", Phone: "555-1234", Email: "joe.coffee#gmail.com" }
, { Name: "Ham Son", Phone: "555-1234", Email: "ham.son#gmail.com" }
, { Name: "Dan Fooey", Phone: "555-1234", Email: "dan.foo#gmail.com" }
],
schema: {
model: {
fields: {
Name: { type: "string" }
, Phone: { type: "string" }
, Email: { type: "string" }
}
}
}
});
//contactDataSource.read();
var viewModel = kendo.observable({
title: 'Contacts'
, contacts: contactDataSource
});
kendo.bind(html, viewModel);
var view = new kendo.View(html, {
model: viewModel,
show: function (e) {
kendo.fx(this.element).fade('in').duration(500).play();
}
});
return view;
});

This doesn't answer your specific problem, however in your 'real' implementation, I think you'll need to get the data from a real datasource? In this case you probably want the data to come via an MVC or Web Api controller. To do this, you need to manually configure an AJAX call in your datasource. See this (you'll need to use the 'read' property of the 'transport' object that will automatically employ AJAX to make the call)
http://docs.telerik.com/kendo-ui/framework/datasource/basic-usage#creating-a-datasource-for-remote-data

Related

ExtJS: How to add new object to Store and send POST request to RESTful server

I am creating a Todo application with ExtJS 6. My application should fetch data, here are Todos list from a Server using RESTful web service, and add new Todo task and send this new task to store in the Server. For the fetching part, it was successfully to show the data from the server, but I still facing a problem with the adding data and send back to the server.
Here are components that I have:
A View called ToDoList which contains 1 textfield and 1 button to add new Todo task:
Ext.define('ToDo.view.toDoList.ToDoList',{
extend: 'Ext.panel.Panel',
requires: [
'ToDo.view.toDoList.ToDoListController',
'ToDo.view.toDoList.ToDoListModel'
],
xtype: 'app-todoList',
controller: 'todoList',
viewModel: {
type: 'todoList'
},
items: [{
xtype: 'container',
items: [{
xtype: 'container',
layout: 'hbox',
cls: 'task-entry-panel',
defaults: {
flex: 1
},
items: [{
reference: 'newToDo',
xtype: 'textfield',
emptyText: 'Enter a new todo here'
},
{
xtype: 'button',
name: 'addNewToDo',
cls: 'btn-orange',
text: 'Add',
maxWidth: 50,
handler: 'onAddToDo'
}]
}]
}]
});
My ViewModel called ToDoListModel which contains store object named todos, which including 3 parameters: id, desc and done.
Ext.define('ToDo.view.toDoList.ToDoListModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.todoList',
stores:{
todos: {
fields: [{
name: 'id',
type: 'string'
},
{
name: 'desc',
type: 'string'
},
{
name: 'done',
type: 'boolean'
}],
autoLoad: true,
sorters: [{
property: 'done',
direction: 'ASC'
}],
proxy: {
type: 'rest',
url: 'http://localhost:8082/RegistrationForm/tasks/',
reader: {
type: 'json'
},
writer: {
type: 'json'
}
}
}
}
});
And My ViewController called: ToDoListController looks like:
Ext.define('ToDo.view.toDoList.ToDoListController', {
extend: 'Ext.app.ViewController',
alias: 'controller.todoList',
views: ['ToDo.view.toDoList.ToDoList'],
init: function(){
var me = this;
this.getViewModel().data.todos.load(function(records){
Ext.each(records, function(record){
console.log(record);
me.addToDoToView(record);
});
});
Ext.getBody().on('click', function(event, target){
me.onDelete(event, target);
}, null, {
delegate: '.fa-times'
});
},
onAddToDo: function () {
var store = this.getViewModel().data.todos;
var desc = this.lookupReference('newToDo').value.trim();
**HOW CAN I ADD NEW OBJECT TO STORE AND PERFORM POST REQUEST HERE TO THE SERVER**
},
addToDoToView: function(record){
this.view.add([{
xtype: 'container',
layout: 'hbox',
cls: 'row',
items: [{
xtype: 'checkbox',
boxLabel: record.get('desc'),
checked: record.get('done'),
flex: 1
},
{
html: '<a class="hidden" href="#"><i taskId="'+ record.get('id') + '" class="fa fa-times"></i></a>'
}]
}]);
},
onDelete: function(event, target){
var store = this.getViewModel.data.todos;
var targetCmp = Ext.get(target);
var id = targetCmp.getAttribute('taskId');
store.remove(store.getById('id'));
store.sync({
success: function(){
this.view.remove(targetCmp.up('.row').id)
},
scope: this
});
}
});
And when the user click to the Add Button, the function onAddToDo() in the Controller will be call, here it will get the new task from the textfield (here desc variable), create a new Todo object and then send this object to the server using POST request. My service in the server will look like:
//ADD new Todo
#RequestMapping(value = "/tasks/", method = RequestMethod.POST)
public ResponseEntity<Void> createUser(#RequestBody Todo todo, UriComponentsBuilder ucBuilder) {
//add this new Todo object to the database
return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
}
What I would like to do is after user entering new Task, I will pack it as new object, structure look like: {'id'=1, 'desc'='what user entered','done'=false} and send it as the RequestBody of the POST method to the server. Can anyone guide me how can I do that? Thank you
You can add the record to the store by using add() function.
store.add({id:1, desc:'what user entered',done:false});
Then you can call sync() function which will call the create api of the store and send the inserted data to server.
store.sync();
sync() synchronizes the store with server in 3 cases that are new record creation, record updation and record deletion. So here when we add a new record and call sync(), it will send only the newly added record to the server as it is not present at server to the URL specified in store

How to save/update object in collection and embed object in object?

I have two collections Colors and Cars.
In the car possibly to choose the color.
How to save/update object in collection so that embed Color object in Car object?
Cars = new Mongo.Collection('cars');
Cars.attachSchema(new SimpleSchema({
colorId: {
label: 'Color',
type: String,
autoform: {
options: function () {
return Colors.find().map(function (p) {
return {label: p.colorName, value: p._id};
});
},
label: false
}
},
color: {
type: Object,
},
'color._id': {
type: String,
autoform: {
omit: true,
},
},
'color.colorName': {
type: String,
autoform: {
omit: true,
},
},
'color.colorCode': {
type: String,
autoform: {
omit: true,
},
},
}));
Colors = new Mongo.Collection('colors');
Colors.attachSchema(new SimpleSchema({
colorName: {
type: String,
label: "Color Name",
max: 20,
},
colorCode: {
type: String,
optional: true,
label: "Color Code",
autoform: {
afFieldInput: {
type: "color"
}
}
},
}));
I try use
AutoForm.hooks({ insertCarForm: {before: {
but it did not work
There are several ways that you can achieve this and the solution largly depends on any relevant packages that you might be using. It's hard to give a working example without seeing your existing code that creates new 'cards'. Nevertheless, here is an example using the core Meteor API.
Assuming you have some form Template defined (which I have called 'manageCar'), you would do something like this.
Define a Meteor Method to handle inserting/updating the Car.
Meteor.methods({
updateCar: function(carDoc) {
check(carDoc, { /* carDoc schema */ });
const color = Colors.findOne(carDoc.colorId);
carDoc.color = color;
if (carDoc._id) {
Cars.update(carDoc._id, {
$set: {
colorId: carDoc.colorId,
color: carDoc.color,
}
})
} else {
Cars.insert(carDoc);
}
},
});
Add an event handler for the form submission that calls the defined Method.
Template.manageCar.events({
'click .js-save-car'(event, instance) {
const data = {
_id: event.target._id.value,
colorId: event.target.colorId.value
};
Meteor.call('updateCar', data, function(error, result) {
if (!error) {
alert('Car updated successfully');
}
});
}
});
Long story short, you just need to make sure you have access to the Color id that you are saving for the Car and then make sure you perform a find on the Color collection to retrieve the necessary Color document, then use that for your Car insert or update.
Let me know if you have any questions or need further explanation.

Get slug from object in view won't work

I have created a new object named Project, that contains a gallery and some other fields in it. In the view, I'm showing some data from it and I want to put a link to previous and next project. I already managed to get the previous project but when I try to get the slug from it, somehow it doesn't work.
This is the Project model:
var keystone = require('keystone');
var Types = keystone.Field.Types;
/**
* Project Model
* ==========
*/
var Project = new keystone.List('Project', {
map: { name: 'title' },
autokey: { path: 'slug', from: 'title', unique: true }
});
Project.add({
title: { type: String, required: true },
state: { type: Types.Select, options: 'draft, published, archived', default: 'draft', index: true },
author: { type: Types.Relationship, ref: 'User', index: true },
publishedDate: { type: Types.Date, index: true, dependsOn: { state: 'published' } },
category: { type: String, required: false },
description: { type: Types.Html, wysiwyg: true, height: 150 },
shortDescription: { type: Types.Html, wysiwyg: true, height: 100 },
credits: { type: Types.Html, wysiwyg: true, height: 100 },
galleries: { type: Types.Relationship, ref: 'Gallery', many: false },
color: { type: String, required: false }
});
Project.schema.virtual('content.full').get(function() {
return this.content.extended || this.content.brief;
});
Project.defaultColumns = 'title, state|20%, author|20%, publishedDate|20%';
Project.register();
This is the controller:
var keystone = require('keystone');
exports = module.exports = function(req, res) {
var view = new keystone.View(req, res);
var locals = res.locals;
// Set locals
locals.section = 'projects';
locals.filters = {
project: req.params.project
};
locals.data = {
projects: [],
previousProject: []
};
// Load the current project
view.on('init', function(next) {
var q = keystone.list('Project').model.findOne({
state: 'published',
slug: locals.filters.project
}).populate('galleries');
q.exec(function(err, result) {
locals.data.project = result;
next(err);
});
});
//Load other projects
view.on('init', function(next) {
var q = keystone.list('Project').model.find({state: "published", publishedDate: {$lt: locals.data.project.publishedDate}}).sort('-publishedDate').limit(1);
q.exec(function(err, results) {
locals.data.previousProject = results;
next(err);
});
});
// Render the view
view.render('project');
};
And this is the view:
<div class="container">
<p>{{{data.project.title}}}</p>
<p>—</p>
<p>{{{data.project.category}}}</p>
{{#if data.project.galleries}}
{{#each data.project.galleries.images}}
<img src="{{url}}" />
{{/each}}
{{/if}}
<p>full project: {{data.previousProject}}</p>
<p>slug: {{data.previousProject.slug}}</p>
{{#if data.previousProject}}
<a href="/projects/{{data.previousProject.slug}}" >Previous project</a>
{{/if}}
</div>
Somehow, {{data.previousProject}} shows the correct record info but when I do {{data.previousProject.slug}} it returns nothing at all. I've been scratching my head against this for hours but I can't find where is the issue. Thanks in advance!!
I finally found what the issue was: in the controller I'm using model.find whereas I should be using model.findOne if I know I only need one record and I want to directly get the values from it with .slug. Using limit(1) was not enough.

Sencha Touch: REST store and DELETE request

I'm new to Sencha Touch and I'm having problems to delete a record.
This is my model:
Ext.define('MyApp.model.Customer', {
extend: 'Ext.data.Model',
...
config: {
idProperty: 'key',
fields: [
{
name: 'key',
type: 'auto'
},
{
name: 'name',
type: 'string'
}
...
]
}
....
}
and this is an event associated with a delete customer button:
onDeleteCustomerBtnTap: function(button, e, eOpts) {
var data = this.getCustomerDetail().getData();
var store = Ext.getStore("CustomerStore");
var record = store.getById(data.key);
store.remove(record);
store.sync();
}
EDIT:
this is the store
proxy: {
type: 'rest',
url: 'http://example.com/customers',
useDefaultXhrHeader: false,
appendId: false,
reader: {
type: 'json',
idProperty: 'key',
rootProperty: 'data'
},
writer: {
type: 'json'
}
}
The problem is when it tries to sync the store. The request url is http://example.com/customers?_dc=1394840324234 instead of http://example.com/customers/10?_dc=1394840324234.
What am I doing wrong?
I finally used the erase method of Ext.data.Model. Here is my final version:
var data = this.getCustomerDetail().getData();
var store = Ext.getStore("CustomerStore");
var record = store.getById(data.key);
record.erase({
success: function() {
// ...
}
}
});
are you defining your proxy as 'rest' and not 'ajax'?
proxy: {
type: 'rest',
url : '/users'
}

How to dynamically create form.Fields from Stores in ExtJS 4

I have two stores, Assessor.store.Question and Assessor.store.Choice, along with their respective Models and Proxies. The load data from the server as intended. I also have a Panel, Assessor.view.QuizCards, with a "card" layout. This works fine and I can create dummy cards, Assessor.view.QuestionCard, and navigate through them fine using the Assessor.controller.Quiz controller.
What I need help with is programatically populating my QuizCards panel with questions and choices from the Questions and Choices stores. I've tried just about everything I can think of based on the docs and have had absolutely no success.
Specifically, I want the "value" of the "displayfield" on a QuestionCard to be the "text" property from the Question store/model. The "boxlabel" values in the "radiogroup" should come from the associated Choice store/model.
The detailed code is below. Thanks for any guidance you can provide.
Ext.define('Assessor.controller.Quiz', {
extend: 'Ext.app.Controller',
itemId: 'quizcontroller',
models: ['Question', 'Choice'],
stores: ['Question', 'Choice'],
views: ['QuestionCard'],
// Constants, kinda
NUM_QUESTIONS: 4,
// Custom Functions
/**
* create store instances
*/
createStores: function(){
if (Ext.getStore('questionstore') == null) {
var qs = Ext.create('Assessor.store.Question');
qs.load();
};
if (Ext.getStore('choicestore') == null) {
var cs = Ext.create('Assessor.store.Choice');
cs.load();
};
}, //end createStores
/**
* update buttons
*/
updateButtons: function(){
var index = this.getCardIndex();
var nb = Ext.ComponentQuery.query('#nextbutton')[0];
var pb = Ext.ComponentQuery.query('#prevbutton')[0];
var fb = Ext.ComponentQuery.query('#finishbutton')[0];
if (index<this.NUM_QUESTIONS) {
nb.enable();
fb.disable();
} else {
nb.disable();
fb.enable();
};
if (index>0){
pb.enable();
} else {
pb.disable();
};
}, //end updateButtons
/**
* get active question card index
*/
getCardIndex: function(){
return (Ext.ComponentQuery.query('quizcards')[0].getLayout().activeItem.itemId.split('-')[1]);
},
/**
* set active question card index
*/
setCardIndex: function(index){
Ext.ComponentQuery.query('quizcards')[0].getLayout().setActiveItem('questioncard-'+index);
},
/**
* start the quiz
*/
startQuiz: function(args) {
this.createQuestionCards();
var sb = Ext.ComponentQuery.query('#startbutton')[0];
sb.disable();
this.updateButtons();
},
/**
* create the UI cards with questions from server.
*/
createQuestionCards: function() {
var qc = Ext.ComponentQuery.query('quizcards')[0];
for (i=0; i<this.NUM_QUESTIONS; i++) {
card = Ext.create('Assessor.view.QuestionCard');
card.itemId = 'questioncard-' + i.toString();
qc.add(card);
};
this.updateButtons();
},
/**
* finishQuiz -- finishes and scores the quiz
* #param {Object} args
*/
finishQuiz: function(args) {
this.localState.set('quizFinished', true);
},
//
nextQuestion: function(args) {
console.log('\nnextQuestion');
var cardlayout = Ext.ComponentQuery.query('quizcards')[0].getLayout();
var activeIndex = cardlayout.activeItem.itemId.split('-')[1];
console.log(activeIndex);
if (activeIndex < this.NUM_QUESTIONS) {
activeIndex++;
this.setCardIndex(activeIndex);
};
this.updateButtons();
},
//
prevQuestion: function(args) {
console.log('\nprevQuestion');
var cardlayout = Ext.ComponentQuery.query('quizcards')[0].getLayout();
var activeIndex = cardlayout.activeItem.itemId.split('-')[1];
console.log(activeIndex);
if (activeIndex > 0) {
activeIndex--;
this.setCardIndex(activeIndex);
};
this.updateButtons();
},
//
init: function(){
this.control({
'#nextbutton': {
click: this.nextQuestion
},
'#prevbutton': {
click: this.prevQuestion
},
'#startbutton': {
click: this.startQuiz
},
'#finishbutton': {
click: this.finishQuiz
},
})
}
})
Ext.define('Assessor.view.QuizCards', {
extend: 'Ext.panel.Panel',
alias: 'widget.quizcards',
itemId: 'quizcards',
layout: 'card',
activeItem: 0,
items: []
})
Ext.define('Assessor.view.QuestionCard', {
extend: 'Ext.form.Panel',
alias: 'widget.questioncard',
layout: 'anchor',
items: [{
xtype: 'displayfield',
itemId: 'questionfield',
name: 'questionfield',
fieldLabel: 'Question',
value: ''
}, {
xtype: 'radiogroup',
itemId: 'choicegroup',
columns: 1,
vertical: true,
items: [{
boxLabel: '',
name: 'choice',
value: 1
}, {
boxLabel: (100*Math.random()),
name: 'choice',
value: 2
}, {
boxLabel: (100*Math.random()),
name: 'choice',
value: 3
}]
}]
})
Ext.define('Assessor.store.Question', {
extend: 'Ext.data.Store',
autoLoad: true,
autoSync: true,
model: 'Assessor.model.Question',
storeId: 'questionstore'
})
Ext.define('Assessor.model.Question', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'text', type: 'string'},
{name: 'resource_uri', type: 'string'}
],
proxy: {
type: 'rest',
url: '/api/v1/question/',
headers: {
'accept':'application/json',
'content-type':'application/json'
},
noCache: false,
reader: {
type: 'json',
root: 'objects',
idAttribute: 'id'
},
writer: {
type: 'json'
}
}
})
Choice model and store are similar, I'll post them if needed. Thanks
What I would suggest for the start is to make Assessor.view.QuestionCard more smart. I would rewrite initComponent there and pass record from the store during construction. This way you would have all logic for creating specific UI elements inside Assessor.view.QuestionCard and would call it something like that:
card = Ext.create('Assessor.view.QuestionCard', {
questionRecord: rec,
lastQuestion: true/false...
... whatever else you need
})