Is it possible to reflect changes to polymer component property without update the value via .set(path, value).
For example I have object with overrided setter which based on this value apply new value to other field.
Polymer({
is: 'x-element',
properties: {
form: {type: Object,
value: {
set name(v) {
this._name = v;
this.additional = v; // change the different property
},
get name() {
return this.name;
},
set additional(v) {
// process v
this._additional = v; // field does not reflect
},
get additional() {
return this._additional;
}
},
reflectToAttribute: true
}
})
Related
I'm very new to Angular and stuck on a problem.
When the component loads I have two input fields as terminal ID(textfield) and terminal type(dropdown). After selecting value from dropdown, onChange method makes an API call and gets data for me. The data is in this format:
data={
"productResults":[
{
"productId":"1",
"productName":"credit"
},
{
"productId":"2",
"productName":"debit"
}
],
"metaResultList":[
{
"customizationType":"time",
"customizationValue":"0420",
"longDescription":"Item1"
},
{
"customizationType":"code",
"customizationValue":"N",
"longDescription":"Auto Close",
"customizationCodeDetail":{
"customizationCodeDetails":[
{
"value":"N",
"shortDescription":"None"
},
{
"value":"H",
"shortDescription":"host Auto Close"
}
]
}
},
{
"customizationType":"bool",
"customizationValue":"Y",
"longDescription":"Block Account"
},
{
"customizationType":"bool",
"customizationValue":"N",
"longDescription":"Block Sales"
},
{
"customizationType":"number",
"customizationValue":"55421",
"longDescrption":"Max Value"
}
]
}
What i did is when i get data after selecting from dropdown, I have two sections here below my these two firlds which will shown with ngIf I get data:
1st: Product Section- Where i used ngFor to iterate over data.productResults like this and displayed the toggles
<div *ngFor="let product of data.productResultResults">
<label>{{product.productName}}</label>
<ion-toggle></ion-toggle>
</div>
2nd : Others Section: Here I iterate over metaResultList to display textfield if customizationType is time or number, dropdown if customizationType is code and toggle if customizationType is bool.
<div *ngFor="let other of data.metaResultList">
<div *ngIf="other.customizationType==='time'||other.customizationType==='number'">
<label>{{other.longDescription}}</label>
<input type="text" value="other.customizationValue"/>
</div>
<div *ngIf="other.customizationType==='bool'">
<label>{{other.longDescription}}</label>
<ion-toggle></ion-toggle>
</div>
<div *ngIf="other.customizationType==='code'">
<label>{{other.longDescription}}</label>
<dropdown [datalist]="autoCloseDropDown"></dropdown>
</div>
</div>
This dropDown is my custom dropdown and I'm passing value as H for host Auto Close and N for None and I'm getting options in dropdown(I did that logic after getting results).
After clicking on submit button below , I want my data to be in this format
{
"tid":"3",
"terminalType":"gateway",
"products":[
{
"productId":"1",
"productname":"credit"
}
],
"customizations":[
{
"customizationName":"Item1",
"customizationValue":"0420"
},
{
"customizationName":"Block Account",
"customizationValue":"Y"
},
{
"customizationName":"Block Sales",
"customizationValue":"N"
},
{
"customizationName":"Max Value",
"customizationValue":"54556"
}
]
}
Now my question is how to make formcontrols and as these fields are generating after i make decision in terminal type dropdown, but i already made the formgroup in ngOnInit.How should I make the form control names dynamic. How will I insert only those value in products array whose value is Y in form. IS there any way I can instantiate formgroup after making change in dropdown?Also there might be case that i won't get certain fields like type=bool sometime from api, so i won't show that in FrontEnd
Any help would be appreciated.
If you want to generate dynamic formControl by an object or an array, you can use this method below:
public builder(data: any): FormGroup | FormArray {
if (Array.isArray(data)) {
const formGroup = [];
for (const d of data) {
formGroup.push(this.builder(d));
}
return this.formBuilder.array(formGroup);
} else {
const formGroup = {};
Object.keys(data).forEach((key) => {
if (typeof data[key] === 'object' && data[key] && !Array.isArray(data[key])) {
Object.assign(formGroup, {
[key]: this.builder(data[key]),
});
} else if (Array.isArray(data[key])) {
Object.assign(formGroup, {
[key]: this.formBuilder.array([]),
});
data[key].forEach((newData: any) => {
formGroup[key].push(this.builder(newData));
});
} else {
Object.assign(formGroup, { [key]: [data[key]] });
}
});
return this.formBuilder.group(formGroup);
}
}
Now, if you want to generate an object or an array dinamic, you can call that's method with the parameter data as an object or an array. For an example:
ngOnInit(): void {
// will be generate a form array generatedForm in formGroup
const dynamicArray = [{ id: 1, name: 'John Doe'}, { id: 2, name: 'Jane Doe'}];
this.formGroup = this.formBuilder.group({
generatedForm: this.builder(dynamicArray)
});
// look at the form array
console.log(this.formGroup.get('generatedForm'));
// will be generated a formGroup by an object
const dynamicObject = { id: 1, name: 'John Doe'};
this.formGroup = <FormGroup>this.builder(dynamicObject)
// dynamic formGroup by an object
console.log(this.formGroup);
}
For an example: You can check at: https://stackblitz.com/edit/angular-ivy-7fm4d1
Ok, now you can try your own with that's builder.
The history property defined in my sma-canvas element:
static get properties() {
return {
history: {
type: Array,
value: () => {
return [];
},
notify: true
}
};
}
static get observers() {
return [
'_historyChanged(history.*)'
];
}
_historyChanged() {
console.log("History Changed!");
this._redrawCanvas(this.$.canvasEl, this.history);
}
I'm trying to set the history property from another element:
<dom-module id="sma-create-studio">
<template>
<sma-canvas id="shapesCanvasEdit" history={{shapesOnCanvasEdit}}></sma-canvas>
</template>
<dom-module>
...
static get properties() {
return {
shapesOnCanvasEdit: {
type: Array,
value: () => {
return [];
},
notify: true
}
};
}
_selectFrame(e) {
// this.shapesOnCanvasEdit = e.detail.frame.shapes;
// this.shapesOnCanvasEdit.splice(0, this.shapesOnCanvasEdit.length);
// this.shapesOnCanvasEdit.push(e.detail.frame.shapes);
this.set('shapesOnCanvasEdit', e.detail.frame.shapes);
// this.splice('shapesOnCanvasEdit', 0, this.shapesOnCanvasEdit.length, e.detail.frame.shapes);
// this.splice('shapesOnCanvasEdit', 0, this.shapesOnCanvasEdit.length);
// this.push('shapesOnCanvasEdit', e.detail.frame.shapes);
// this.notifyPath('shapesOnCanvasEdit');
// this.notifySpice('shapesOnCanvasEdit');
}
I need to call _historyChanged (the observer in sma-canvas), whenever I change the value of shapesOnCanvasEdit. As you can see from the last code snippet, I tried multiple ways to do that, but all in-vain.
Can anybody please help me in this regard? I have no clue, what I'm doing wrong.
SOLUTION 1
static get properties() {
return {
active: {
type: Boolean,
// Observer method identified by name
observer: '_activeChanged'
}
}
}
// Observer method defined as a class method
_activeChanged(newValue, oldValue) {
this.toggleClass('highlight', newValue);
}
https://www.polymer-project.org/2.0/docs/devguide/observers#change-callbacks
From my experience in 1.93, adding a simple observer for an array updates the array if the length changes. (Change Boolean to Array in the example above.)
SOLUTION 2
You can observe splices:
static get observers() {
return [
'usersAddedOrRemoved(myArray.splices)'
]
https://www.polymer-project.org/2.0/docs/devguide/observers#array-observation
}
It only fires if the array length changes. To force update, reset it array myArray with this.set('myArray', []), if you're desperate.
SOLUTION 3
The truly desperate solution. Add a secondary int variable that you update every time there is a change in the array. So _selectFrame() should first update your array history and then update the integer, and both should be properties in sma-canvas. Set an observer on that integer variable in your sma-canvas element, as shown in solution 1.
I have element and I put in it object with data for it.
<my-element data="{{item.content}}"></my-element>
For now I have not done this so I have all in data property declaration.
properties: {
data: {
type: Object,
value: {
active: false,
name: "Some name."
...
}
}
}
I also have to click handler on child element which should toggle the boolean value.
_handleClickBlock: function () {
this.data.active = !this.data.active;
}
I need to set [active] attribute on my-element to data.active value and make changes every time, because I use it in my CSS styles like :host[active] ....
How can I achieve it? I tried this.setAttribute('active', this.data.active) in event handler, but it change the value only once; console log was toggling.
Edit:
I have tried use this code:
observers: [
'_activeChange(data.active)'
],
_activeChange: function(newValue) {
this.active = newValue;
console.log("change");
},
_handleClickBlock: function () {
console.log("------ click");
this.data.active = !this.data.active;
console.log(this.data.active);
}
But in my console output is only this, so observer is not called.
------ click
false
------ click
true
Try including 'active' as a property in you my-element and set reflectToAttribute to true.
properties: {
data:{...}
active: {
type: Boolean,
reflectToAttribute: true
}
}
observers: [
'_activeChange(data.active)'
],
_activeChange: function(newValue) {
this.active = newValue;
}
Edit: Please go through how to change the path such that observers are notified. You need to update data.active as below
this.set('data.active', !this.data.active);
Below jsbin:
http://jsbin.com/qupoja/4/edit?html,output
I'm using a form to edit Model. I receive data in JSON. Here's my problem:
I receive data for Checkbox only in INT: 1 or 0, I cannot change it. JSON:
{ "checkboxValue": 1 }
This field in ExtJS model is defined as INT type. Model:
{name: "checkboxValue", type: Ext.data.Types.INT},
then I set values to form this way:
formCmp.loadRecord(loadedStore.getAt(0));
and my checkbox is set correctly: when I receive 1 it's checked, 0 - unchecked.
But when I try to save record and send data to server this way:
form.updateRecord(form.getRecord());
record.save();
I need Checkbox to have also INT value - 1 or 0. But it has only BOOL value - true or false so when JSON is sent that value is NaN.
{
"checkboxValue": NaN
}
I think, that function .updateRecord(..) go through all elements and when it gets to checkbox it tries to get value, and the value is BOOL
Does anybody know how to make checkbox' output value INT?
Ext.form.Basic.updateForm uses getFieldValues method to retrieve new data for the updated record, while getFieldValues method returns only boolean values for checkboxes regardless of such properties as inputValue or uncheckedValue. So I would use convert function for the model's field to transform provided boolean value into an integer in a way like that:
Ext.define('MyModel', {
extend: 'Ext.data.Model',
fields: [
{
name: 'flag',
type: 'int',
convert: function (v, record) {
return typeof v === 'boolean' ? (v === true ? 1 : 0) : v;
}
}
],
...
});
Here is a complete jsfiddle
I think it can be done with some simple overrides
Ext.create('Ext.form.Panel', {
bodyPadding: 10,
width: 300,
title: 'Pizza Order',
items: [
{
xtype: 'fieldcontainer',
fieldLabel: 'Toppings',
defaultType: 'checkboxfield',
items: [
{
boxLabel : 'Topping?',
name : 'topping',
id : 'checkbox1',
// include these two properties in your checkbox config
uncheckedValue: 0,
setValue: function(checked) {
var me = this;
arguments[0] = checked ? 1 : me.uncheckedValue;
me.callParent(arguments);
return me;
}
}
]
}
],
renderTo: Ext.getBody()
});
Based on the following data
public var dataArray:ArrayCollection = new ArrayCollection( [
{ label: "None", data: '' },
{ label: "Some Data", data:
{
label: "String tested at IF", data: "20"
}
}
]);
I then create a new ArrayCollection obj based on the position a user has selected from a dropdown list
var skillArrayCollection:ArrayCollection = ArrayCollection(runtimeComponents[dataArray[classSelect.selectedIndex].data]);
I am looping through an ArrayCollection object like so:
for each (var item:Object in skillArrayCollection[charSkillOne.selectedIndex]) {
if (item.label == "somelabel") {
ret = ret + Number(item.data);
}
}
When the if statement does its check 'it matches the string test and returns the correct result; IT also returns the error: ReferenceError: Error #1069: Property label not found on String and there is no default value.
I do not understand how flex will error and return the result i have asked for?
In your first object here:
{ label: "None", data: '' },
{ label: "Some Data", data:
{
label: "String tested at IF", data: "20"
}
}
data is a string, in your second object, data is an object with a label property.
When your loop goes through each element there the first would cause this error because when you call object.data.label on it you're trying to get a label property of a string, hence the error (for the second object if it were to get there it would not error). As others state the question leaves out the contents of runtimeComponents so it's difficult to say this is for sure the issue but it seems right.