Formly - How can I link the field to the model of a custom template - angular8

I am using Angular 8 with Formly for creating a select input type with search. As a solution, I found that ng-select will suit me well.
The question is how can I link the [model] directive to the Formly field which uses my custom template ?
Note: I do not want to use a plain text, like model.my.special.key - I need to give it something general. This way I can use it with other fields that I want to make use of this selector with search.
In more details what I did so far:
I have created a new #Component that extends FieldType and has a template that uses the selector for displaying the options it receives as input
#Component({
selector: "SelectorWithSearchModule",
template: `
<ng-select
[items]="to.options"
bindLabel="label"
bindValue="code"
labelForId="code"
placeholder="{{ to.label }}"
[multiple]="to.multiple"
clearAllText="Clear"
(change)="onChange($event)"
[(ngModel)]="what-to-put-in-here?"
>
</ng-select>
`
})
export class SirutaModule extends FieldType implements OnInit {}
I have declared this new Component in app.module.ts and extented the Formly types with it:
eg:
import { SelectorWithSearchModule } from "./types/selector/special-selector.component"; // declaration
...
FormlyModule.forRoot({ // from here begins the Formly definition
types: [
{
name: "special-selector",
component: SelectorWithSearchModule
}
]
I am using the special-selector name when defining new Formly fields
{
className: "col-8",
type: "special-selector",
key: "my.hierarchy.key",
templateOptions: {
label: "some label here",
options: ["option_one", "option_two"]
}
}
I want that in the end, the key my.hierarchy.key to contain the value user selected in my custom template, but it is not very clear how.

Related

Spartacus Configurable Product Integration: Custom Normalizer nested data being overridden

I am trying to implement a custom normalizer to the configurable product feature module. I have to include a custom field in the Attributes datatype. Currently only the OccConfigurationVariantNormalizer is available, which is quite high level form a data's point of view.
My problem occurs with the execution order of the normalizers. The default normalizer ist this: https://github.com/SAP/spartacus/blob/develop/feature-libs/product-configurator/rulebased/occ/variant/converters/occ-configurator-variant-normalizer.ts which is being called after my custom normalizer. Hence, the convertGroup() function is overriding my custom attribute field.
Here is my implementation:
#Injectable({
providedIn: 'root'
})
export class CustomConfiguratorNormalizerService extends OccConfiguratorVariantNormalizer{
convertAttribute(sourceAttribute: CustomOccAttribute, attributeList: CustomAttribute[]): void{
super.convertAttribute(sourceAttribute, attributeList);
attributeList[attributeList.length - 1].customField = sourceAttribute.customField;
}
}
Extending the original Normalizer seemed like the most promising solution for the time being, and is working quite like intended. So the customField ist being present at this point in time of execution.
Afterwards the OccConfiguratorVariantNormalizer kicks in, which is defining a new Attribute array in convertGroup(), erasing my custom attribute:
convertGroup([...]) {
const attributes: Configurator.Attribute[] = [];
if (source.attributes) {
source.attributes.forEach((sourceAttribute) =>
this.convertAttribute(sourceAttribute, attributes)
);
}
[...]
};
convertAttribute(
sourceAttribute: OccConfigurator.Attribute,
attributeList: Configurator.Attribute[]
): void {
const attribute: Configurator.Attribute = {
name: sourceAttribute.name,
label: sourceAttribute.langDepName,
required: sourceAttribute.required,
uiType: this.convertAttributeType(sourceAttribute.type),
values: [],
groupId: this.getGroupId(sourceAttribute.key, sourceAttribute.name),
userInput: sourceAttribute.formattedValue,
maxlength:
sourceAttribute.maxlength + (sourceAttribute.negativeAllowed ? 1 : 0),
numDecimalPlaces: sourceAttribute.numberScale,
negativeAllowed: sourceAttribute.negativeAllowed,
numTotalLength: sourceAttribute.typeLength,
selectedSingleValue: null,
images: [],
hasConflicts: sourceAttribute?.conflicts?.length > 0 ? true : false,
};
[...]
};
If my custom normalizer was the only one I could imagine it would work, which is why I tried to inject it like this:
{
provide: VARIANT_CONFIGURATOR_NORMALIZER,
useClass: CustomConfiguratorNormalizerService,
multi: false,
}
Throwing me Error: Multi-providers mixed with single providers.
Also, using the documentation from https://sap.github.io/spartacus-docs/connecting-to-other-systems/ I cannot get it to work without extending the original Normalizer, since target will always be undefined, which probably would not be the case if my custom normalizer came in second.
I feel like this https://github.com/SAP/spartacus/issues/9046 could be related.
Any help very much appreciated :)
I was able to solve this myself. Following the reference structure for spartacus applications at https://sap.github.io/spartacus-docs/reference-app-structure/ the problem disappeared.
My best guess is that it has to do with the import order of the modules. In my current working version I import the FeaturesModule last, which seems to solve the problem.

How to use SVG version of Twemoji in Vue?

I am using twemoji in my Vue project.
I have included the twemoji package and it all works well.
I have a directive like so:
import twemoji from 'twemoji'
Vue.directive('emoji', {
inserted (el) {
el.innerHTML = twemoji.parse(el.innerHTML)
}
})
and I use it like this:
<template>
<div v-emoji class="hello">
Hello there!βœ¨πŸ£πŸ˜‹
</div>
</template>
This all works really well. But the default twemoji extension is PNG which renders a bit blurry on mobile phones. I would like to use the SVG option instead.
The official twemoji docs says that I can use SVGs:
Object as parameter
Here's the list of properties accepted by the optional object that can be passed to the parse function.
{
callback: Function, // default the common replacer
attributes: Function, // default returns {}
base: string, // default MaxCDN
ext: string, // default ".png"
className: string, // default "emoji"
size: string|number, // default "36x36"
folder: string // in case it's specified
// it replaces .size info, if any
}
But I do not know how to declare these options when I define my directive.
Here is a fiddle: https://codepen.io/tdkn/pen/yEmazB
Thanks!
Passing the optional object with properties size and ext did the trick for me. This will generate the svg URL
import twemoji from 'twemoji'
Vue.directive('emoji', {
inserted (el) {
el.innerHTML = twemoji.parse(el.innerHTML, { size: 'svg', ext: '.svg' })
}
})

Component name as string

I have a registered custom filter component like:
import Filter from "../components/DataTableComponent/StringFilter";
components: {
Filter
},
I can use this component in a vue2-datatable column like:
var col : { title:"Foo", field:"Bar", comp: Filter }
Basically, I want to give this component a name as a string from my service and render it is like one like above.
var col : { title:"Foo", field:"Bar", comp: "Filter" }
Requirement is that you are trying to send component object to the wherever you want to send. So it's simply a basic concept to store it to the instance you working currently and use it dynamically.
Service sends you component name - "Filter" and you are importing the same name component to your file.
Just store it to a object then after like this
import Filter from "../components/DataTableComponent/StringFilter";
// store it to local object
const componentReference = { Filter }
// Let say service sent you this name
const componentNameSentByService = "Filter"
//reference it to using the name dynamically using object
var col : { title:"Foo", field:"Bar", comp: componentReference[componentNameSentByService] }

How to define QML component inline and override a property?

I'm trying and failing at something seemingly simple: define a simple text formatting component inline, then instantiate it multiple times with different text. Here's the code
Item {
.
.
.
Component {
id: favButtonLabelText
Text {
text: "Blah!"
color: StyleSingleton.xNavPrimaryText
font.family: StyleSingleton.xNavTextFont
font.pointSize: 28
}
}
.
.
.
Loader { sourceComponent: favButtonLabelText; text: "Diameter" }
At the Loader line, the text property is invalid. Attempts to define a property or alias on the component are rejected with "Component objects cannot declare new properties".
The only example I find in the docs, shows overriding the x property of a Rectangle defined in an inline component. It seem to me overriding the text property of a Text element is analogous.
How can I do this?
Since Loader sets itself as the context object for the component it is loading, you could define a property in it and use it in the loaded Item.
However, you have to use a property name not used by your item, otherwise it will be shadowed by your item's own property and there's no easy way to access a context property explicitly.
Component {
id: favButtonLabelText
Text {
text: foobar
}
}
Loader {
sourceComponent: favButtonLabelText
property string foobar: "Diameter"
}
As GrecKo already said, it is possible to use a custom property of the Loader that has another name like in his example foobar.
If you don't do any fancy reparenting of the loaded Item it is also possible to use the same name, and reference it with parent.property
Component {
id: textComponent
Text {
// Use "parent" to reference the property of the parent,
// which is by default the Loader
text: parent.text
}
}
Column {
Repeater {
model: ['hallo welt', 'hello world', 'Bonjour monde', 'δ½ ε₯½δΈ–η•Œ']
delegate: Loader {
property string text: modelData
sourceComponent: textComponent
}
}
}
As of Qt 5.15, a new feature has been added: inline Components
As the name suggests, it allows to define an component inline, with the benefits of:
You can create an instance of the component, without the overhead of using a Loader.
You can use the component type in property declarations.
You can refer to the component in other files than the one it is defined in.
Item {
.
.
.
component FavButtonLabelText: Text {
property int aCustomProp: 0
text: "Blah!"
color: StyleSingleton.xNavPrimaryText
font.family: StyleSingleton.xNavTextFont
font.pointSize: 28
}
.
.
.
FavButtonLabelText { text: "myNewText"; aCustomProp: 5 }

ExtJs 4... How to extend Extjs 4 components?

can somebody help me with how to extend extjs components using extjs version 4. I am looking for a proper syntax for the same. please help..!!
Following is an example code of extending textfield in ExtJS 4.
Other then using the existing configs and methods, this extended component also has a new config property created and a new method created & associated with an event.
The purpose of component is simple that it displays the label in red color if the value is mandatory, modifies the background color of the field if its readOnly and also changes the background color of the field when focussed.
The code is properly commented. Hope it helps.
Ext.define('Ext.pnc.Textfield', {
extend: 'Ext.form.field.Text',//Extending the TextField
alias: 'widget.pnctextfield',//Defining the xtype
config:{
focusCls:'focusClassFieldPnC',//Providing value for existing config property.
testConfig:'testConfigValue'//Creating a new config. Accessor functions will be created for this one by ExtJS engine
},
constructor:function(cnfg){
this.callParent(arguments);//Calling the parent class constructor
this.initConfig(cnfg);//Initializing the component
this.on('beforerender',this.beforeRender);//Associating a new defined method with an event
},
//Defining a method below and associating this with an event in the constructor above
beforeRender:function(){
if(!this.allowBlank){
this.labelStyle = 'color:#FF0000';
}
if(this.readOnly){
this.fieldCls = 'readOnlyClass';
}
},
//Over-riding a function which already exists in parent class. Note that this has not been associated with any event in constructor as it already defined in parent class
afterRender:function(){
console.log('after render function');
this.callParent();//Calling the parent class method. This can be omitted if not required and is not a must
}
});
.readOnlyClass{
background:#FF0000 !important
}
.focusClassFieldPnC{
background:#00ff00 !important
}
Ext.define('myApp.Grid', {
extend: 'Ext.Grid',
alias: 'widget.mygrid'
....
....
}
now you can use xtype:'mygrid'
Ext.define('BS.view.MyGrid' , {
extend: 'Ext.grid.Panel',
alias: 'widget.my-grid',
// Non-complex config types (booleans, integers, strings) go here
width: 1000,
autoHeight: true
initComponent: function() {
Ext.apply(this, {
// complex configs (objects / arrays) go here
columns: colModel,
});
this.callParent(arguments);
}
});
why not see the src of extjs4's components such as Grid,Table ...
and here are docs:
http://docs.sencha.com/ext-js/4-0/#/guide/components <== important
http://docs.sencha.com/ext-js/4-0/#/guide/class_system
Ext.define('My.custom.Component', {
extend: 'Ext.Component'
});