Can choices in a list be changed after it has been been rendered? - w2ui

I have a w2ui form that contains a w2ui Drop List of choices. The choices will be different depending on what the user selected to bring up the form. My question is: can the contents of a Drop List be changed after it has been rendered?
With standard HTML controls, I would do something like this:
$("#mySelect option[value='xyz']").remove();
or
$("#mySelect").append('<option value="abc">abc</option>');
Can these kinds of operations be done with a w2ui Drop List? Any example code?

In w2ui 1.5 you can use $jQueryElement.w2field() to access the w2fild object - and then manipulate it.
Example:
var field = $("#my_input").w2field();
field.options.items = ["my", "new", "items"];
// optionally: pre-select first item
field.setIndex(0);
// if you do NOT use "setIndex" you need to call "refresh" yourself!
// field.refresh();
Note: setIndex() internally calls refresh() - so as stated above, you do not need to call refresh yourself in that case.
If you want to completely clear/empty your field, you can call field.reset().
Edit: after clarification that it's about a form field:
// Note: ``this`` refers to the w2form
// ``field[8]`` refers to a field of type "select"
this.fields[8].options.items = ["my", "new", "items"];
this.record = {
field_select: 'new'
};
this.refresh();

Related

frappe trigger field update via custom script

I'm customizing an existing DocType(quotation) and I've added fields to the Quotation Item child table which affect the amount field of an Item. By default, i.e. before customizations, the grand_total, and Quotation net_totals get calculated as soon as an Item changes. But now that I have custom fields, how can I call the hypothetical "refresh" functions that do the default calculation?
Here is my current Custom Script that updates the Item amount on Quotation Item child table:
frappe.ui.form.on("Quotation Item", "paint", function(frm, doctype, name) {
let row = locals[doctype][name];
let rate = row.labour + row.trim + row.paint + row.spares;
row.rate = rate;
let total = rate * row.qty
row.amount = total;
refresh_field("items");
});
There are few techniques to achieve your goal.
The most effective one will depend on the HOOKS, specially :
doctype_js
override_doctype_class
As the first will allow you to run your js code along with the original doc js code.
And the second will allow you to override the original doc class and will give you the capability to call the hypothetical method you override.
You can check the below links for more details:
doctype_js
override_doctype_class

Can't get changed property from attachPropertyChange

I would like to know which property in the JSON model has changed when modified by a view.
For a test I took OpenUI5 walkthrough example and added the following lines in the application controller
oProductModel.attachPropertyChange( function(oEvent){
console.log("event: ", oEvent);
}, this);
When I change a property in the text input, the function in the attachPropertyChange is called but oEvent object is empty as I print it in console.
I know I could connect to text input change event, but I would like to use attachPropertyChange in case there would be multiple views of the same model.
As far as I understood, you'd like to avoid using the change event of the Input control because there is no information about which property in the model has changed. However, you can still get all the relevant information within the change handler via:
oControl.getBinding(/*controlPropertyName*/).getPath() to get the name of the bound property, or
oControl.getBindingContext(/*modelName*/).getPath(/*suffix*/) to get the path of the bound context. The getPath here awaits an optional suffix that will be appended to the context path with a "/" in between.
Combine those two APIs to get an absolute path in case the property binding was relative. E.g.:
onInputChange: function (event) {
const inputControl = event.getSource();
const property = inputControl.getBinding("value").getPath(); // "myProperty"
const absolutePath = inputControl.getBindingContext(/*modelName*/).getPath(property) // "/0/myProperty"
// ...
},
You can use change event for all input field in UI, and write event handling method in the controller. You will get the property as well as value in the oEvent of the event handling method easily. I hope you understood.

Passing grid panel data via request payload to REST controller

I have a user form that is created in extjs framework. The user form has many user fields which are being passed as part of request payload to the REST controller.
I am trying to add a grid panel(most likely in a tabular format with multiple rows & columns) to the user form.
But I am not sure how to pass the grid panel data as part of request payload to the REST controller.
I will post more code if any more details are needed.
Any help would be appreciated. Thanks.
Ext.define('soylentgreen.view.admin.UserForm', {
extend : 'Ext.form.Panel',
alias : 'widget.userform',
bodyStyle : 'padding:5px 5px 0',
// some userform elements like firstname,lastname, go here.....
name : 'userTeamGrid',
xtype : 'gridpanel',
id : 'userTeamGrid',
itemId : 'userTeamGrid',
multiSelect : true,
selModel : Ext.create(
'Ext.selection.CheckboxModel',
{
injectCheckbox : 'first',
mode : 'MULTI',
checkOnly : false
}),
anchor : '100%',
width : '700px',
height : 250,
flex : 1,
store : 'userTeamStore',
var user = form.getRecord();
form.updateRecord(user);
user.save({
callback : function(records, operation){
//reset the (static) proxy extraParams object
user.getProxy().extraParams = {
requestType: 'standard'
}
if(!operation.wasSuccessful()){
var error = operation.getError();
IMO, That's one of the most complicated yet common issues we face in ExtJS. It has to be solved often, but most solutions have to be slightly different depending on the exact requirements.
Grids are bound to a full store, not to a single record. So if you want to get data from a record (e.g. as an array) into a grid and vice versa, you have to have a mapping between the data from the store and the value in a single field of a record. To e.g. get all data from the store, the generic solution is
store.getRange().map(function(record) { return record.getData(); });
If you need only the grid selection (maybe you want to use a checkboxselection model or similar) and/or only certain fields of the records, you have to change this code to your needs, e.g.
grid.getSelectionModel().getSelection().map(function(record) {return record.get('Id'); });
So, your record is bound to the form, and a form consists of fields. How do you get the grid data to be accepted as part of the form?
You have to add a hiddenfield to your form and, depending on your requirement, overwrite some of the four functions setValue, getValue, getModelData, getSubmitValue. Usually, the hiddenfield tries to convert its value to a string, but obviously, you need to allow for arrays there; and you want to modify the grid whenever setValue is called, or read the value from the grid whenever one of the getters is called. The four functions are used for the following:
setValue should be used to modify the grid based on the value you find in the record.
getValue is used in comparison operations (check whether the value has changed, which means the form is dirty and has to be submitted, etc.) and if you call form.getValues. You should return some value based on the grid state in it.
getModelData is used by the form.updateRecord method; it should return a value that the model field's convert method can work with.
getSubmitValue is used by the form.submit method, it should return a value that can be safely transmitted to the server (has to be a string if the form doesn't have sendAsJson:true set)
I cannot give you an exact recipe for these implementations, as they are specific to your requirements.

MVC how do i populate dropdownlist without using a model

I am using mvc 4, i have a form in a view (not binded to a model), its using standard html elements.
I want to populate a dropdownlist from a list value (i.e return from controller action)
also based on the selection of a value from the first dropdownlist i want to populate a second dropdownlist
can someone please guide
for the first dropdownlist you loop thru all the available options and add in <option> tags inside <select> for the second dropdownlist you need to either make bunch drop downs and hide/show them, or you create one big list and remove invalid entries base on the first list's selection. You would definitely need to do javascript for 2nd list.
If you don't want to use a Model (you should though), you will have to add the items to ViewData
I'll stub something out for you and you can complete the rest.
Inside your controller, create a list object of what you need. If you are using EntityFrameWork this will look familiar.
var list = context.Table.ToList();
List<System.Web.Mvc.SelectListItem> ddlItems = new List<System.Web.Mvc.SelectListItem>();
foreach (var item in list)
{
ddlItems.Add(new SelectListItem(){ Text = item.Text, Value = item.Value.ToString()});
}
ViewData["DDLItems"] = ddlItems;
#Html.DropDownListFor(x => x.LeagueId,
new SelectList((System.Collections.IEnumerable)ViewData["DDLItems"], "Value", "Text")
, "--Select League--", new { id = "league" })
You can define your second dropdownlist with just a placeholder until the cascade effect happens.
#Html.DropDownListFor(x => x.DivisionId, Enumerable.Empty<SelectListItem>(),
"--Select Division--", new { id = "ddlDivision" })
Your going to need to use JQuery and fire and event when the dropdown changes, and then use Ajax to make a call back to the controller. Theres 2348239 examples online about making Ajax calls, know how to do that because it's done all the time in MVC.
I'll let you figure that part out. One hint, inside the Ajax call you can pass data to the controller. something like this data: { leagueId: value } where value is the value of the dropdownlist you want to cascade off of. leagueId must match type and name of the parameter your controller will expect.
Return a Json object from your controller like so...
public JsonResult GetDivisions(int leagueId)
{
var division = //similar to before, fill a list.
return Json(divisions, JsonRequestBehavior.AllowGet);
}
And then in the success function of your Ajax call, you will populate the Second dropdownlist.
success: function (data) {
$.each(data, function (index, item)
$('#ddlDivision')
.append($('<option></option>')
.val(item.Value)
.html(item.Text))
item.Value and item.Text can be anything, just as long as the Json you return as the properties of Text and Value
IE...
var divisions = (from x in context.Division
select new
{
Text = league + " " + x.Region,
Value = x.DivisionId
}).ToList();

OpenERP: Pass context from server (back-end) to form (front-end)

Question about dynamic views in OpenERP.
Inside a form view I have a tree view of a one2many field. In this tree view I'd like to hide/show the entire column (not just individual cells) depending on the contents of the parent form view. E.g. a column should be hidden if in the [parent's] form view a certain field is filled or a checkbox is ticked.
AFAIK, the only way to hide the entire column is to use context:
<field name='my_column' invisible="not context.get('showMyColumn',False)">
My question is: How to pass context from server (back-end) to form (front-end)?
I know how to do the opposite (pass context from form to server). And I know how to pass context from button's action function to form:
return {'type': 'ir.actions.act_window', ..., 'context': ctx_updated}
But I'd like to know how to update form's context from the write() method of the form view's object or from it's on_change method of the object's field my column listens to.
Thanks,
Anton
If you mean ony this example
"Updated the post: E.g. a column should be hidden if in the [parent's] form view a certain field is filled or a checkbox is ticked."
then I think the best way is to:
<field name="value_ids" attrs="{'invisible':['|',('parent.text_field','not in',['']),('parent.mandatory','=', True)]}">
So field is invisible if text in parent's field is set, or checkbox is checked.
If you wish to make it more complicated, you can create additional functional field in your parent's object
def _visible(self, cr, uid, ids, name, args, context=None):
result = {}
for obj in self.browse(cr, uid, ids, context=context):
result[obj.id] = True
return result
_columns = {
'visible': : fields.function(_visible, type='boolean',string='Visible'),
there you define when it should be visible, and when not.
In your parent's view you add that field
And after you can just call
<field name="value_ids" attrs="{'parent.invisible':[('parent.visible','=',True)]}">
I would do it in that way.