control flow of dhtmlx schedular for saving data via custom field - calendar

I have added custom text field in dhtmlschedular.js but
I am unable to trace the path. how can my custom field value get on controller.
or complete flow of dhtmlx for saving the data to db.
I done so far :-
lightbox: {
sections: [
{name: "description", height: 200, map_to: "text", type: "textarea", focus: true },
{ name:"prabhu", height:200, map_to:"txt1", type:"dhiraj"},
{name: "time", height: 72, type: "time", map_to: "auto"}]
dhiraj: {
render:function(sns){
return "<div class='dhx_cal_ltext' style='height:60px;'>Text <input id='txt1' name='txt1' type='text'><br/>Details <input id='calendarDetailId' name='txt2' type='text'></div>";
},
set_value:function(node,value,ev){
node.childNodes[1].value=value||"";
node.childNodes[4].value=ev.details||"";
},
get_value:function(node,ev){
ev.location = node.childNodes[4].value;
return node.childNodes[1].value;
},
focus:function(node){
var a=node.childNodes[1]; a.select(); a.focus();
}
}
};

You can add custom field without modifying sources of the component (i'd recomend avoid modifying them whenever it's possible)
If you need to add another control into the details form, you can overwrite scheduler.configuration.lightbox.sections object (in your page/script file, not in the sources)
The 'map_to' property defines property of the data object(event) to be mapped to the input.
If control should be mapped to several fields, you can use map_to:"auto" and retreive needed fields from event object manually.
The code on your page may look like following:
//defining a control
scheduler.form_blocks["dhiraj"]={
render:function(sns){
return "<div class='dhx_cal_ltext' style='height:60px;'>" +
"Text <input id='txt1' name='txt1' type='text'><br/>" +
"Details <input id='calendarDetailId' name='txt2' type='text'>" +
"</div>";
},
set_value:function(node,value,ev){
//get values from event manually
node.childNodes[1].value = ev.txt1 || "";
node.childNodes[4].value = ev.txt2 || "";
},
get_value:function(node,ev){
//assign values to event object
ev.txt1 = node.childNodes[1].value;
ev.txt2 = node.childNodes[4].value;
},
focus:function(node){
var a=node.childNodes[1]; a.select(); a.focus();
}
};
//add control to the details form
scheduler.locale.labels.section_prabhu = "";//labels for inputs are defined as 'seciton_'+inputName
scheduler.config.lightbox.sections = [
{name:"description", height:200, map_to:"text", type:"textarea" , focus:true},
{name:"prabhu", height:200, map_to:"auto", type:"dhiraj"},//here it is
{name:"time", height:72, type:"time", map_to:"auto"}
];
//init scheduler after control is defined
scheduler.init('scheduler_here',new Date(),"week");
New input will show value of 'txt1' and 'txt2' properties, as can be seen in the definition. When changes are saved, component will send all event values to the server. Custom values also will be sent.
Depending on how you handle update on the server side, you should either add custom fields to configuration of dhtmlxConnector, or retreive them from GET/POST parameters.
http://docs.dhtmlx.com/scheduler/lightbox_editors.html
http://docs.dhtmlx.com/scheduler/custom_lightbox_editor.html

Related

How do you create dynamic local store based on record property

I have an integer field on backend. This field is an offset (CPI Index delay) that could takes values from 0 -> N. On frontend I want to display a combo box with this value / label:
{key: 0, label: "0 - 21/06/2020"}
{key: 1, label: "1 - 21/06/2021"}
{key: 2, label: "2 - 21/06/2022"}
{key: 3, label: "3 - ... "}
The number of option is calculated this way: rent_end_date.year - first_due_date.year. Each time the first_due_date change, the options of the combo box must be calculated again.
To achieve this I've initialed a variabile indexOffsetChoices with relative formula to calculate choices each time a first_due_date changes:
viewModel: {
type: 'rentsdetailformmodel',
data: {
hideRentsDetailGrids: false,
indexOffsetChoices: []
},
formulas: {
calculateIndexOffsetChoices: {
bind: '{detailRecord.first_due_date}',
get: function (firstDueDate) {
var detailRecord = this.get('detailRecord'),
indexOffset = detailRecord.get('index_offset'),
rentEndDate = detailRecord.get('end_date');
if (indexOffset !== undefined && rentEndDate !== undefined) {
var choices = [];
for (i = 0; i < (rentEndDate.getFullYear() - firstDueDate.getFullYear()); i++) {
var RawCPIDate = new Date(firstDueDate.setFullYear(firstDueDate.getFullYear() + i)),
CPIDateFmt = Ext.Date.format(new Date(RawCPIDate), 'd/m/Y'),
label = i + ' - ' + CPIDateFmt;
choices.push({"key": i, "label": label});
}
this.indexOffsetChoices = choices;
}
}
}
}
},
Then I added my combo box with an inline local store that point to the viewModel variable indexOffsetChoices:
items: [{
xtype: 'combobox',
name: 'index_offset',
reference: 'index_offset',
fieldLabel: 'CPI Application Delay',
valueField: 'key',
displayField: 'label',
queryMode: 'local',
bind: '{detailRecord.index_offset}',
store: {
type: 'store',
storeId: 'rent__index_offset',
idProperty: 'key',
fields: ['key', 'label'],
data: this.indexOffsetChoices
}
}]
But the data are not loaded. This is the right approach? How do you create local store based on records / dynamic data? What I'm missing?
You did the hardest way sir! The 'data' property in viewModel is actually not used to store large of array data except you will work hard to modify them.
Setting dynamic options in combobox can be achieved by updating (or re-setting) it's options (using yourcombofield.setOptions([/*new array data*/]) like here ) or it's data store (using yourcombofield.getStore().setData([/* new array data*/]) like here) or if you declare your store in viewModel 'stores' property like here and have linked to your combobox's store property, you don't need to access combobox again, just modify the array data of the store and your combobox's options which has been linked to the store will be updated automatically. The important thing, try to give the easiest way to modify you array data inside the store.
Now, let's talk about when and where will you place your code to update this combobox's data? This is about the event's owner which you want to listen (and also affects your case). You said "Each time the first_due_date change". If first_due_date is affected by another field, just put your code inside it's change/keyup/keydown listeners and then re-treat you data by filtering,removing,adding, etc and the last, update the old data with the new data where ever the data has been stored before.

How to prevent Kendo MultiSelect from losing values after editing in a grid template?

I have a grid that displays a comma-separated list of values, and it has an array that gets used in a template editor for the grid. (On the server, I transform the comma-separated list to an array for the Kendo multiselect AngularJS directive). I have almost everything working: display, edit, and adding values in the multiselect.
There's just one weird thing happening: after I add a value in the multiselect, click Save in the editor, and reopen the editor, the multiselect then only displays one of the most-recently entered values. I know that the values are there and going through the pipeline, because the values make it into the database. I can refresh the page, open the editor, and all the values display in the multiselect correctly, including the one I just added.
It's as if kendo "forgets" most of the values when I reopen the editor. How can this be prevented? Does the MultiSelect need to be rebound to the values? If so, how?
I have tried adding this onChange event, but it had no effect. I've added valuePrimitive to no effect. I tried specifying k-rebind, but it caused an error.
Here's the directive being used in the text/x-kendo-template:
<select kendo-multi-select
id="zipCode"
k-placeholder="'Enter zip codes...'"
style="width: 225px"
k-on-change="dataItem.dirty=true"
k-auto-bind="false"
k-min-length="3"
k-enforce-min-length="true"
k-data-source="options.zipCodeDataSource"
k-data-text-field="'text'"
k-filter="'startsWith'"
k-filtering="options.zipCodeFiltering"
k-no-data-template="'...'"
k-ng-model="dataItem.zipArray"
k-highlight-first="true" />
And this is the DataSource:
options.zipCodeDataSource = new kendo.data.DataSource({
severFiltering: true,
transport: {
read: {
url: serviceUrl + "ZipCode/Get",
type: "GET",
dataType: "json",
contentType: jsonType,
data: function (e) {
// get your widget.
let widget = $('#zipCode').data('kendoMultiSelect');
// get the text input
let filter = widget.input.val();
// what you return here will be in the query string
return {
filter: filter
};
}
},
},
schema: {
data: "data",
total: "Count",
model:
{
id: "text",
fields: {
text: { editable: false, defaultValue: 0 },
}
},
parse: function (response) {
return response;
}
},
error: function (e) {
}
});
If I display {{dataItem.zipArray}} in a <pre> all of the expected values are there.
I wonder if something needs to be added to the edit event handler in the kendo grid definition, but I'm not sure what it would be. I've had to do binding like that for the dropdownlist directive.
edit: function (e) {
if (e.model.marketId === 0) {
e.container.kendoWindow("title", "Add");
} else {
e.container.kendoWindow("title", "Edit");
}
// re-bind multi-select here??
// These two lines actually cause the multiselect to lose pre-existing items in dataItem.zipArray
// var multiselect = kendo.widgetInstance(angular.element('#zipCode'));
// multiselect.trigger('change');
}
...
Update:
This dojo demonstrates the issue.
Run the dojo
Edit the first record in the Contracts grid
Add a zip code such as 22250
Click Save
Then click Edit on the first row again
Only zip code 22250 is displayed in the editor
Also, I notice that if I change k-min-length="3" to k-min-length="1", then the issue goes away. But in the scenario I'm working on, it needs to be 3.
This seems to be an issue with kendo itself. Back then this issue was reported here.
Ok based on the reply from telerik, this issue has been fixed in 2017 R3 SP1 release (2017.3.1018). You can verify it by using this updated dojo example:
http://dojo.telerik.com/IREVAXar/3

How to update Angular scope properties when they are passed into directives?

I have functionality where developers can add custom Angular views where they can bind properties to the $scope.settings object. When clicking on the save button the $scope.settings object will be converted to JSON and saved to the database. Something like this will be the result:
{
"name": "bob",
"age": "25"
}
As long as I add elements like <input type="text" ng-model="settings.name" /> everything goes as expected.
But, now I want to add directives like this:
<umb-property property="property in properties">
<umb-editor model="property"></umb-editor>
</umb-property>
With the following code:
$scope.properties = [
{
label: 'Name',
alias: 'name',
view: 'textbox',
value: $scope.settings.name
},
{
label: 'Age',
alias: 'age',
view: 'number',
value: $scope.settings.age
}
];
The 'editor' directive loads views in place based on the 'view' property. The views are third party. The editors are loaded in a dialog. After submission of the settings dialog, the following line of code will convert the settings to JSON:
$scope.dialog = {
submit: function (model) {
var settingsJson = JSON.stringify(model.settings);
},
close: function (oldModel) {
//
}
};
In this case I cannot parse the $scope.settings to JSON, because the $scope.settings.name is not updated anymore. The $scope.editorModel.value was updated instead.
How can I bind the $scope.editorModel.value to $scope.settings.name?
I don't want to end up with a solution where I must update all $scope.settings values with the corresponding values from the editor models, because I want to support the dynamic way to convert the $scope.settings to a JSON value in the database.
For example I dont want to do: $scope.settings.name = $scope.properties[0].value
Use property accessors:
for (var i=0; i<$scope.properties.length; i++) {
$scope.settings[$scope.properties[i].alias] = $scope.properties[i].value;
};
HTML
<div ng-repeat="prop in properties">
<input ng-model="prop.value">
</div>

Sencha Touch 2.3: Remove validations from hidden form fields

I am doing form validations in Sencha Touch 2.3. My model looks like following.
Ext.define('net.omobio.dialog.dialogcc.model.StockTransferDetails', {
extend: 'Ext.data.Model',
config: {
fields: ['to_msisdn','to_profile_id','transfer_lob','transfer_item_status','transfer_product','transfer_qty','transfer_req_type','transfer_item_type','transfer_pack_type'],
validations: [
{ type: 'presence', field: 'to_msisdn' },
{ type: 'presence', field: 'to_profile_id' },
{ type: 'exclusion', field: 'transfer_lob', list: ['null'] },
{ type: 'exclusion', field: 'transfer_req_type', list: ['null'] },
{ type: 'exclusion', field: 'transfer_item_type', list: ['null'] },
{ type: 'exclusion', field: 'transfer_pack_type', list: ['null'] }
]
}
});
Following is a code segment that I use in my controller to remove validations from hidden form fields but no luck.
var form1 = me.getStockTransferRequestPage();
var model = Ext.create("net.omobio.dialog.dialogcc.model.StockTransferDetails", form1.getValues());
// validate form fields
var errors = model.validate();
if (!errors.isValid()) {
// loop through validation errors and generate a message to the user
errors.each(function (errorObj){
//errorString += errorObj.getField() + " " + errorObj.getMessage();
console.log('7777777777777777777 '+errorObj.getField());
if (!Ext.getCmp(errorObj.getField().toString()).isHidden()) {
var s = Ext.String.format('field[name={0}]',errorObj.getField());
form1.down(s).addCls('invalidField');
}
});
Ext.Msg.alert('','stock_transfer.errors.required_fields_empty');
}
I would be much appreciated if anyone could help me to solve this.
Thank you
so there are multiple ways to achieve this, my preference even though some folks won't like it, but it will always work.
I did the following override to solve this problem, tried my best not to affect the normal flow of validation.the first two overrides have to be added somewhere to your overrides folder, you only have to add them once for the whole app.
Ext.Define('Ext.form.field.BaseOverride', {
override: 'Ext.form.field,Base',
/* traverse up and look for a hidden Parent/Ancestor */
isParentHidden: function () {
return this.up('[hidden=true]');
}
/* override isValid basic method to consider skipValidateWhenHidden property, when skipValidateWhenHidden is set to true code should check if the elementor it's Parent/Ancestors is hidden */
isValid: function () {
var me = this,
disabled = me.disabled,
isHidden = me.isHidden(),
skipValidateWhenHidden = !!me.skipValidateWhenHidden,
validate = me.forceValidation || !disabled,
isValid = validate ? me.validateValue(me.processRawValue(me.getRawValue())) : disabled;
if (isValid || !skipValidateWhenHidden) {
return isValid;
}
if (skipValidateWhenHidden) {
isHidden = isHidden ? true : me.isParentHidden();
if (isHidden) {
return skipValidateWhenHidden;
}
}
return isValid;
}
});
and eventually you'll be able to do the following, which is set the property to true on any field so if its not visible for the user, it will survive validation
{
itemId: 'City',
cls: 'AddressCity',
xtype: 'textfield',
emptyText: emptyCityText,
skipValidateWhenHidden: true,
},
another approach is to add a show()/Hide() listener on the fields container to enable/disable the children, disabling the fields would make them skip validation, but i'm not a big fan of managing button states and wiring listeners.
Note
Ext.getCmp() takes component id
errorObj.getField().toString() returns model field name, It won't
return id of the component (hidden) field.
So if model field name matches with hidden field id, It will work. Otherwise it won't work.

ExtJS: Display model in Ext.grid.property.Grid

I'd like to display the persistent fields (those defined in my model file) in a property grid.
Property Grid:
Ext.define('ATCOM.view.InspectorProperties', {
extend : 'Ext.grid.property.Grid',
alias : 'widget.inspectorProperties',
cls : 'property-grid',
height : 150,
listeners : {
beforerender : function() {
// Rename the first column
var cols = this.getView().getHeaderCt().getGridColumns();
cols[0].setText("Property");
},
beforeedit : function(e) {
// Read-only
return false;
}
},
source : {} // Start with no items
});
I load items like so in a select event (in a controller), where record is our model object and getInfo() is the property grid:
var source = {};
source.id = record.get('id');
source.start = record.get('start');
source.end = record.get('end');
this.getInfo().setSource(source);
Model:
Ext.define('ATCOM.model.Shift', {
extend : 'Ext.data.Model',
fields : [ 'id', {
name : 'start',
type : 'date',
}, {
name : 'end',
type : 'date',
}, 'position', 'controller' ],
hasMany : {
model : 'ATCOM.model.ShiftAlloc',
name : 'allocations'
}
});
Is there a better way to go about this so all non-associative fields (excluding allocations in my case) are automatically sent to the property grid? It might also be possible to read the fields with ATCOM.model.Shift.getFields() and iterate over those checking for persistent:false; to keep the remaining keys, but how do I get the class reference from an instance - as in, how do I get ATCOM.model.Shift from one of its instances so I can call getFields()?
EDIT:
For finding the class name: http://docs.sencha.com/ext-js/4-1/#!/api/Ext.Base-static-method-getName
It may work to say setSource(record.data). I am just playing with this now; it seems to show the right information, though you may lose control over the details of which fields to enable for editing, etc.

Resources