Unable to submit custom field, which extends dataview - javascript

I have a fiddle which demonstrates this strange behaviour. In a nutshell, I have a custom field which extends Ext.DataView. I need to extend dataview, because this field is supposed to have dynamic contents. This is how my field is defined:
Ext.define('Ext.ux.SimpleList', {
extend: 'Ext.DataView',
alias: 'widget.simplelist',
requires: [
'Ext.XTemplate'
],
itemSelector: 'div.record',
isFormField: true,
getFieldIdentifier: function () {
return this.name;
},
getModelData: function() {
var me = this;
var data = {};
data[me.getFieldIdentifier()] = me.getValue();
return data;
},
getSubmitData: function() { return {}; },
submitValue: true,
isValid: function () { return true; },
isDirty: function () { return true; },
validate: function () { return true; },
isFileUpload: function() { return false; },
constructor: function(config) {
Ext.applyIf(config, {
tpl: [
'{% var name = this.owner.name; %}',
'<tpl for=".">',
'<div class="record"><input record-id="{id}" type="checkbox" name="{[name]}" /> {label}</div>',
'</tpl>',
{compiled: true}
]
});
this.callParent([config]);
},
getValue: function () {
var cb = this.el.select("input").elements, i, res = [];
for (i in cb) {
if (cb.hasOwnProperty(i) && cb[i].checked) {
res.push(cb[i].getAttribute("record-id"));
}
}
return res;
},
setValue: function (values) {
//not yet implemented
}
});
And this is how I add this field to a form:
Ext.create("Ext.form.Panel",{
renderTo: Ext.getBody(),
items:[{
xtype: "textfield",
name: "text"
},{
xtype: "simplelist",
name: "list",
store: {
fields: ["id", "label", "checked"],
data: [{"id": "1", "label": "One"},{"id": "2", "label": "Two"}]
}
},{
xtype: "button",
text: "Submit",
handler: function () {
var frm = this.up("form").getForm();
console.log(frm.getFieldValues()); // it' ok
//simplelist field is not submitted
this.up("form").getForm().submit({
url: "/"
});
}
}]
});
As you can see, when I submit the form I log to the console form field values. And what is interesting about that, is that I see my custom field among those field values. So, I have a field with isFormField set to true, this field is in the list returned by form getFields() method and this field is also among those values returned by form getFieldValues() method, but still this field is not submitted. What is wrong with that and how can I fix it?

Your code uses basicForm.getFieldValues(), which calls basicForm.getValues() with some parameters, while the form while submitting uses the same method with different parameters. One of those parameters is useDataValues, which decides whether to use the getModelData or getSubmitData.
You are returning empty object in your getSubmitData method, which prevents it to correctly get the values.
All you need to change, for both methods to work in your current state, is this:
getSubmitData: function() {
return this.getModelData();
}

Related

Trigger function in moment column is sorted in GridX

I use SingleSort module with Gridx. I can't find (I already checked SingleSort documentation and found nothing) a way to react on sort event. I need info about which (and how) column is sorted. I know how get info about sorting (getSortData method) but i don't know how make react in moment sort is made. I can't made onRender-event function because after sorting i will send that info to webapi get new data and again render Grid so event will be triggered again.
define([
"dojo/Evented",
"dojo/_base/declare",
"dojo/store/Memory",
"gridx/core/model/cache/Sync",
"gridx/Grid",
"gridx/modules/ColumnResizer",
'gridx/modules/Focus',
'gridx/modules/RowHeader',
'gridx/modules/select/Row',
'gridx/modules/select/Column',
'gridx/modules/select/Cell',
"gridx/modules/SingleSort"
], function (Evented, declare, Memory, Cache, Grid, ColumnResizer, Focus, RowHeader, SelectRow, SelectColumn, SelectCell, Sort) {
return declare([Evented], {
_counter: 1,
_topOffset: 100,
constructor: function () {
},
initialize: function () {
this._initGrid();
this._resizeGridContainer();
},
clear: function () {
var store = new Memory({ data: [] });
this._grid.setStore(store);
this._counter = 1;
},
_initGrid: function () {
var _this = this;
var store = new Memory({
data: []
});
var structure = [
{
id: 'operationType', field: 'operationType', name: dojo.byId(this._operationTypeId).value
},
{
id: 'transportationType', field: 'transportationType', name: dojo.byId(this._transportationUnitTypeId).value
},
{
id: 'transportationTypeLength', field: 'transportationTypeLength', name: dojo.byId(this._transportationLengthId).value
}
]
this._grid = Grid({
id: this._gridId,
cacheClass: Cache,
store: store,
structure: structure,
modules: [
ColumnResizer,
Sort
],
selectRowTriggerOnCell: true
});
this._grid.placeAt(this._gridPlaceholderId);
this._grid.startup();
},
_resizeGridContainer: function () {
var _this = this;
var container = dojo.byId(this._gridContainerId);
var height = container.parentElement.clientHeight - this._topOffset;
require(["dojo/dom-style"], function (domStyle) {
domStyle.set(_this._gridContainerId, "height", height + "px");
})
},
});
});
you need to tap on to the gridx SingleSort module sort method like this
this._grid.connect( this._grid.Sort, 'sort', function(colId, isDescending, skipUpdateBody){
alert(colId+" "+isDescending+" "+skipUpdateBody);
});
this will trigger the event whenever a grid column is sorted. Hope this helps.

Saving Only the changed record on a BackGrid grid?

I am in the process of learning Backbone.js and using BackGrid to render data and provide the end user a way to edit records on an Microsoft MVC website. For the purposes of this test grid I am using a Vendor model. The BackGrid makes the data editable by default (which is good for my purpose). I have added the following JavaScript to my view.
var Vendor = Backbone.Model.extend({
initialize: function () {
Backbone.Model.prototype.initialize.apply(this, arguments);
this.on("change", function (model, options) {
if (options && options.save === false) return;
model.url = "/Vendor/BackGridSave";
model.save();
});
}
});
var PageableVendors = Backbone.PageableCollection.extend(
{
model: Vendor,
url: "/Vendor/IndexJson",
state: {
pageSize: 3
},
mode: "client" // page entirely on the client side.
});
var pageableVendors = new PageableVendors();
//{ data: "ID" },
//{ data: "ClientID" },
//{ data: "CarrierID" },
//{ data: "Number" },
//{ data: "Name" },
//{ data: "IsActive" }
var columns = [
{
name: "ID", // The key of the model attribute
label: "ID", // The name to display in the header
editable: false, // By default every cell in a column is editable, but *ID* shouldn't be
// Defines a cell type, and ID is displayed as an integer without the ',' separating 1000s.
cell: Backgrid.IntegerCell.extend({
orderSeparator: ''
})
}, {
name: "ClientID",
label: "ClientID",
cell: "integer" // An integer cell is a number cell that displays humanized integers
}, {
name: "CarrierID",
label: "CarrierID",
cell: "number" // A cell type for floating point value, defaults to have a precision 2 decimal numbers
}, {
name: "Number",
label: "Number",
cell: "string"
}, {
name: "Name",
label: "Name",
cell: "string"
},
{
name: "IsActive",
label: "IsActive",
cell: "boolean"
}
];
// initialize a new grid instance.
var pageableGrid = new Backgrid.Grid({
columns: [
{
name:"",
cell: "select-row",
headercell: "select-all"
}].concat(columns),
collection: pageableVendors
});
// render the grid.
var $p = $("#vendor-grid").append(pageableGrid.render().el);
// Initialize the paginator
var paginator = new Backgrid.Extension.Paginator({
collection: pageableVendors
});
// Render the paginator
$p.after(paginator.render().el);
// Initialize a client-side filter to filter on the client
// mode pageable collection's cache.
var filter = new Backgrid.Extension.ClientSideFilter({
collection: pageableVendors,
fields: ['Name']
});
// REnder the filter.
$p.before(filter.render().el);
//Add some space to the filter and move it to teh right.
$(filter.el).css({ float: "right", margin: "20px" });
// Fetch some data
pageableVendors.fetch({ reset: true });
#{
ViewBag.Title = "BackGridIndex";
}
<h2>BackGridIndex</h2>
<div id="vendor-grid"></div>
#section styles {
#Styles.Render("~/Scripts/backgrid.css")
#Styles.Render("~/Scripts/backgrid-select-all.min.css")
#Styles.Render("~/Scripts/backgrid-filter.min.css")
#Styles.Render("~/Scripts/backgrid-paginator.min.css")
}
#section scripts {
#Scripts.Render("~/Scripts/underscore.min.js")
#Scripts.Render("~/Scripts/backbone.min.js")
#Scripts.Render("~/Scripts/backgrid.js")
#Scripts.Render("~/Scripts/backgrid-select-all.min.js")
#Scripts.Render("~/Scripts/backbone.paginator.min.js")
#Scripts.Render("~/Scripts/backgrid-paginator.min.js")
#Scripts.Render("~/Scripts/backgrid-filter.min.js")
#Scripts.Render("~/Scripts/Robbys/BackGridIndex.js")
}
When the user edits a row, it successfully fires the hits the model.Save() method and passes the model to the save Action, in this case BackGridSave and it successfully saves the record that changed, but seems to save all of the vendors in model when only one of the vendors changed. Is there a way from the JavaScript/Backbone.js/BackGrid to only pass one Vendor - the vendor that changed?
Update: I realized that it is not sending every vendor, but it is sending the same vendor multiple times as though the change event was firing multiple times.
I guess I answered my own question. Well, at least I am getting the desired result. I just added a call to off after the first on. Seems like this would not be necessary though.
var Vendor = Backbone.Model.extend({
initialize: function () {
Backbone.Model.prototype.initialize.apply(this, arguments);
this.on("change", function (model, options) {
if (options && options.save === false) return;
model.url = "/Robbys/BackGridSave";
model.save();
model.off("change", null, this); // prevent the change event from being triggered many times.
});
}
});

Unable to create a delete button in Meteor using reactive-table

I building a sortable table in Meteor with Reactive-Table and having trouble getting my delete button to work for removing entries from the table.
Please see my javascript code below:
Movies = new Meteor.Collection("movies");
if (Meteor.isClient) {
Template.body.events({
"submit .new-movie": function (event) {
var title = event.target.title.value;
var year = event.target.year.value;
var genre = event.target.genre.value;
Movies.insert({
title: title,
year: year,
genre: genre
});
event.target.title.value = "";
event.target.year.value = "";
event.target.genre.value = "0";
return false;
}
});
Template.moviestable.events({
"click .deletebtn": function (event) {
Movies.remove(this._id);
}
});
Template.moviestable.helpers({
movies : function () {
return Movies.find();
},
tableSettings : function () {
return {
showFilter: false,
fields: [
{ key: 'title', label: 'Movie Title' },
{ key: 'year', label: 'Release Year' },
{ key: 'genre', label: 'Genre' },
{ key: 'edit', label: 'Edit', fn: function () { return new Spacebars.SafeString('<button type="button" class="editbtn">Edit</button>') } },
{ key: 'delete', label: 'Delete', fn: function () { return new Spacebars.SafeString('<button type="button" class="deletebtn">Delete</button>') } }
]
}
}
});
}
Can anyone tell me what I'm doing wrong?
In the reactive tables docs, there's an example of how to delete rows from the table. Adapting the example in the docs for your needs, your event should look like this:
Template.moviestable.events({
'click .reactive-table tbody tr': function (event) {
event.preventDefault();
var objToDelete = this;
// checks if the actual clicked element has the class `deletebtn `
if (event.target.className == "deletebtn") {
Movies.remove(objToDelete._id)
}
}
});
The problem you are having is that you are trying to find the _id property on the button click instead of the row click.
If you do console.log(this) on your button click event (as you have it in your question above) you will get something like this Object {key: "delete", label: "", fieldId: "2", sortOrder: ReactiveVar, sortDirection: ReactiveVar} which does not contain the property _id
It is easier to register the row click, where the row object is the actual document you are trying to delete, and then check if the event's target has the delete class you added.

Set value in Backbone.Form after fetch from the server

Hi I am newbie in Backbone and JS.
I need to fetch data from the server and put this value as the default to planProviderMode choice radio button. So i fetch the data in defaultMode variable. But the call is a asynchronous and I receive a value after the planProviderMode: defaultMode.value code has been executed.
var PlanProviderMode = Backbone.Model.extend({
url: '/rest/enrollment/step/plan-provider-mode'
});
var defaultMode = new PlanProviderMode;
defaultMode.fetch({
success: function() {
// recieve correct data
}
});
var formItems = new Backbone.Form({
template: tpl,
className: 'wizard-content',
schema: {
planProviderMode: {
template: formTpl,
type: 'Radio',
options: [
{
label: 'By Plan',
val: 'BY_PLAN',
custom: {
img: '../resources/img/by-plan.jpg'
}
},
{
label: 'By Provider',
val: 'BY_PROVIDER',
custom: {
img: '../resources/img/by-provider.jpg'
}
}
]
}
},
//here i put the default value
data: {
planProviderMode: defaultMode.value
}
});
How could I set the fetched value to the form.
Please let me know if the question is not described well. Thanks.
you should write such code in success callback function which is executed only after the fetch request is completed succesfully.
var PlanProviderMode = Backbone.Model.extend({
url: '/rest/enrollment/step/plan-provider-mode'
});
var defaultMode = new PlanProviderMode;
defaultMode.fetch({
success: function() {
// recieve correct data
//here you put the default value
data: {
planProviderMode: defaultMode.value
}
}
});
var formItems = new Backbone.Form({
template: tpl,
className: 'wizard-content',
schema: {
planProviderMode: {
template: formTpl,
type: 'Radio',
options: [
{
label: 'By Plan',
val: 'BY_PLAN',
custom: {
img: '../resources/img/by-plan.jpg'
}
},
{
label: 'By Provider',
val: 'BY_PROVIDER',
custom: {
img: '../resources/img/by-provider.jpg'
}
}
]
}
}
});
Backnone has a function called "parse" that helps you in this cases.
This function will always be called from backbone before the data from server populate the model.
Take a read in parse method.
There's a answer here.
how to override Backbone's parse function?
Hope it helps

How to get Ext JS component from DOM element

Trying to create an inline edit form.
I have a form that looks like this:
var editPic = "<img src='https://s3.amazonaws.com/bzimages/pencil.png' alt='edit' height='24' width='24' style='margin-left: 10px;'/>";
var submitPic = "<img id='submitPic' src='https://s3.amazonaws.com/bzimages/submitPic.png' alt='edit' height='24' width='24'/>";
Ext.define('BM.view.test.Edit', {
extend: 'Ext.form.Panel',
alias: 'widget.test-edit',
layout: 'anchor',
title: 'Edit Test',
defaultType: 'displayfield',
items: [
{name: 'id', hidden: true},
{
name: 'name',
fieldLabel: 'Name',
afterSubTpl: editPic,
cls: 'editable'
},
{
name: 'nameEdit',
fieldLabel: 'Name',
xtype: 'textfield',
hidden: true,
cls: 'editMode',
allowBlank: false,
afterSubTpl: submitPic
}
]
});
The controller looks like this (a lot of events):
init: function() {
this.control({
'test-edit > displayfield': {
afterrender: this.showEditable
},
'test-edit': {
afterrender: this.formRendered
},
'test-edit > field[cls=editMode]': {
specialkey: this.editField,
blur: this.outOfFocus
}
});
},
outOfFocus: function(field, event) {
console.log('Lost focus');
this.revertToDisplayField(field);
},
revertToDisplayField: function(field) {
field.previousNode().show();
field.hide();
},
formRendered: function(form) {
Ext.get('submitPic').on('click', function (event, object) {
var field = Ext.get(object).parent().parent().parent().parent();
var cmp = Ext.ComponentQuery.query('test-edit > field[cls=editMode]');
});
},
editField: function(field, e) {
var value = field.value;
if (e.getKey() === e.ENTER) {
if (!field.allowBlank && Ext.isEmpty(value)){
console.log('Not permitted!');
} else {
var record = Ext.ComponentQuery.query('test-edit')[0].getForm().getRecord();
Ext.Ajax.request({
url: '../webapp/tests/update',
method:'Post',
params: {
id: record.getId(),
fieldName: field.name,
fieldValue: field.value
},
store: record.store,
success: function(response, t){
field.previousNode().setValue(value);
t.store.reload();
var text = response.responseText;
// process server response here
console.log('Update successful!');
}
});
}
this.revertToDisplayField(field);
} else if (e.getKey() === e.ESC) {
console.log('gave up');
this.revertToDisplayField(field);
}
},
showEditable: function(df) {
df.getEl().on("click", handleClick, this, df);
function handleClick(e, t, df){
e.preventDefault();
var editable = df.nextNode();
editable.setValue(df.getValue());
editable.show();
editable.focus();
df.hide();
}
},
I'm using the 'afterSubTpl' config to add the edit icon, and the accept icon.
I have listeners set up to listen on click events concerning them, but after they are clicked, I only have the element created by Ext.get('submitPic'). Now I want to have access to the the Ext field and form that surround it. The parent method only brings back other DOM elements. How do I connect between them? You can see what I tried in formRendered.
I hope someone can clarify this little bit for me.
Walk up the DOM tree until you find a component for the element's id:
getCmpFromEl = function(el) {
var body = Ext.getBody();
var cmp;
do {
cmp = Ext.getCmp(el.id);
el = el.parentNode;
} while (!cmp && el !== body);
return cmp;
}
Ext.Component.from(el) does exactly this since ExtJS 6.5.0, as I just learnt. Doc
Source
You can get the component by id, but only if your component and its dom element have the same id (they usually do):
Ext.getCmp(yourelement.id)
But this is not exactly good practice -- it would be better to set up your listeners so that the handler methods already have a reference to the component. For example, in your 'submitPic' component, you could define the click listener like this:
var me = this;
me.on({
click: function(arguments){
var cmp = me;
...
});

Categories

Resources