How to pass boolean attributes into Vue render function data object? - html

The data object for Vue render functions takes HTML attributes and their arguments:
{
// Same API as `v-bind:class`
'class': {
foo: true,
bar: false
},
// Same API as `v-bind:style`
style: {
color: 'red',
fontSize: '14px'
},
...
How do you render an element that takes attributes that don't take arguments (there's probably a proper expression for this but I don't know it). An example would be:
<f7-list accordion>
<f7-list-item>
...
My attempt isn't working:
render: function (createElement){
return createElement('f7-list', {{'attrs': { 'accordion': true }},
this.tree.map(function(item){return createElement('f7-list-item', item.name)}))
How does one include these attributes?

Can you pass it as a prop and then use it? You can try like this I think. (Untested)
render(createElement) {
return createElement('f7-list', {
props: {
'attrs': {'accordion':true}
}
})
}
Or perhaps simply
render(createElement) {
return createElement('f7-list', {
attrs: {
'accordion': true
}
})
}

Related

Merge mixin in vue

I'm working in vue/quasar application.
I've my mixin like this in my view.cshtml
var mixin1 = {
data: function () {
return { data1:0,data2:'' }
}
,
beforeCreate: async function () {
...}
},
methods: {
addformulaire(url) {
},
Kilometrique() { }
}
}
And I want merge with my content in js file (it's to centralize same action an severals cshtml)
const nomeMixins = {
data: function () {
return { loadingcdt: false, lstclt: [], filterclient: [], loadingdoc: false, lstdoc: [], filterdoc: [] }
},
computed: {
libmntpiece(v) { return "toto"; }
},
methods: {
findinfcomplemtX3(cdecltx3, cdedocx3) {
},
preremplissagex3: async function (cdecltx3, cdedocx3) {
}
}
}
};
I want merge this 2 miwin in one. But when I try assign or var mixin = { ...mixin1, ...nomeMixins };
I've only mixin1 nothing about methods,data from my js file nomeMixins but merging failed cause I've same key in my json object. I'm trying to make a foreach but failed too
Someone try to merge to mixin / json object with same key in the case you've no double child property ?
You cant merge mixins in that way. the spread syntax will overwrite keys e.g data, computed, methods etc and final result will not be suitable for your purpose.
refer documentation for adding mixins in your component. Also note that You can easily add multiple mixins in any component, so I don't think combination of two mixins will be any useful.
UPDATE
reply to YannickIngenierie answer and pointing out mistakes in this article
Global Mixins are not declared like this
// not global mixin; on contrary MyMixin is local
// and only available in one component.
new Vue({
el: '#demo',
mixins: [MyMixin]
});
Local Mixins are not declared like this
// NOT local mixin; on contrary its global Mixin
// and available to all components
const DataLoader = Vue.mixin({....}}
Vue.component("article-card", {
mixins: [DataLoader], // no need of this
template: "#article-card-template",
created() {
this.load("https://jsonplaceholder.typicode.com/posts/1")
}
});
Point is refer documentation first before reading any article written by some random guy, including me. Do slight comparison what he is saying whats in documentation.
After working and searching... I find this one And understand that I can add directly mixin in my compoment (don't laught I'm begging with vue few months ago)
my custommiwin.js
const DataLoader = Vue.mixin({
data: function () {
return { loadingcdt: false, lstclt: [], filterclient: [], loadingdoc: false, lstdoc: [], filterdoc: [] }
},
methods: {
filterClt: async function (val, update, abort) {
if (val.length < 3) { abort(); return; }
else {//recherche
this.loadingcdt = true;
let res = await axios...
this.loadingcdt = false;
}
update(() => {
const needle = val.toLowerCase();
this.filterclient = this.lstclt.filter(v => v.libelle.toLowerCase().indexOf(needle) > -1 || v.id.toLowerCase().indexOf(needle) > -1);
})
},
filterDocument: async function (val, update, abort, cdecltx3) {
if (!cdecltx3 || val.length < 3) { abort(); return; }
else {//recherche
this.loadingdoc = true;
let res = await axios({ ...) }
this.loadingdoc = false;
}
update(() => {
const needle = val.toLowerCase();
this.filterdoc = this.lstdoc.filter(v => v.id.toLowerCase().indexOf(needle) > -1);
})
},
}
});
and in my compoment.js I add this
mixins: [DataLoader],
I include all my js file in my cshtml file

How create dynamic form controls and fetch data from it in Angular

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.

React: Reaching Nested State Items w/ Reducer

I'm working with some deeply nested state objects, and thanks to a lot of wisdom from the SO community, have switched to useReducer to manage the state. I can't figure out the syntax to reach deeply nested items or consecutive objects in arrays. Here's my state object:
const initialState = {
customer: [
{
name: "Bob",
address: "1234 Main Street",
email: "bob#mail.com",
phone: [
{
mobile: "555-5555",
home: "555-5555"
}
]
},
{
name: "Reggie",
address: "5555 Pineapple Lane",
email: "reggie#mail.com",
phone: [
{
mobile: "123-4567",
home: {
fax: "444-4444",
evening: "222-2222"
}
}
]
}
]
}
I need to reach our second customer, Reggie, and change both his address and home evening phone number (customer[1].address & customer[1].phone[1].home.evening)
I've tried this with my useReducer case statements, but no luck. . .I get a TypeError: Cannot read property 'object' of undefined when I call on these functions (presumably from my dropdown menu, who's value is set to this props I'm trying to change):
case "SET_CUST1_ADDRESS":
return {
...state,
customer: [
{ ...state.customer[1], address: value }
]
};
onChange={({ target: { value } }) =>
dispatch({ type: "SET_CUST1_ADDRESS", value })
}
And the second for the evening number. . .
case "SET_CUST1_PHONE_HOME_EVENING":
return {
...state,
customer: [
{
...state.customer[1],
phone: [
{
...state.customer[1].phone[0],
home: {
...state.customer[1].phone[0].home,
evening: value
}
}
]
}
]
};
onChange={({ target: { value } }) =>
dispatch({ type: "SET_CUST1_PHONE_HOME_EVENING", value
})
}
I understand the need to work with flattened state objects to make all this much easier, but unfortunately the data structure I'm working with is immutable. Tips/tricks/comments welcome. Thanks!
UPDATE:
It seems there is an unresolved conflict between using these two case statements, which is causing the TypeError: Cannot read property 'object' of undefined during my state changes. When I try to change the state of two different array objects in the same array, I get the error. Apparently these two cases cannot exist simultaneously:
case "SET_CUST_ADDRESS":
return {
...state,
customer: [
{ ...state.customer[0], address: value }
]
};
case "SET_CUST1_ADDRESS":
return {
...state,
customer: [
state.customer[0],
{ ...state.customer[1], address: value }
]
};
These two case statements seem to be causing an error with one another when I call the drop down function on either or, resulting in the aforementioned error. I don't see the connection. Any further input would be appreciated.
If I understand your question you miss this line:
case "SET_CUST1_PHONE_HOME_EVENING":
return {
...state,
customer: [
state.customer[0],
{
...state.customer[1],
phone: [
{
...state.customer[1].phone[0],
home: {
...state.customer[1].phone[0].home,
evening: value
}
}
]
}
]
};
I mean that you forgot to insert the customer[0] again to the state.
if this was not helpful- you can try to console.log the state before and after ans see which changes you don't want - and which code needs to be fixed according to that.
edit:
you can use something like this:
const stateCustomers = state.customer.slice();
let customer = stateCustomers.find(c => <your condition to find the desired customer>);
customer.phone[0].home.evening = value;
return {
...state,
customer: stateCustomers
};
Update:
Incase anyone is having a similar problem and having a hard time working with nested items, I would like to suggest first reading up on state immutability as recommended to me in my reddit thread on this topic.
I also found (via recommendation) a super way to deal with this type of nested state via something called immer. It's as easy as making an import statement and then wrapping your case statement in a 'produce' function, which makes a draft of your state that you can directly reference instead of spreading out all layers to make a change. So, instead of having something like this:
case "SET_CUST1_PHONE_HOME_EVENING":
return {
...state,
customer: [
state.customer[0],
{
...state.customer[1],
phone: [
{
...state.customer[1].phone[0],
home: {
...state.customer[1].phone[0].home,
evening: value
}
}
]
}
]
};
You can instead have something like this, which IMO is much cleaner and easier to read/write:
import { produce } from 'immer';
...
case "SET_CUST1_PHONE_HOME_EVENING":
return produce(state, draft => {
draft.customer[1].phone[0].home.evening = value
});
And boom! You're done.

Methods undefined when babel transpiled ES6 to ES5

I'm having a issue with babel transpile down to ES5. For the majority of my
application all other classes are compiled down correctly.
One of the classes however has a problem. When it is transpiled, then none of the methods are present on the instances anymore.
When the class constructor is executed, it raises a exception:
Uncaught TypeError: this.basket.setCurrency is not a function
Here is the class.
export class Basket extends ItemSet {
static get currencies() {
return [
{ code: 'gbp', symbol: '£', title: 'Pounds' },
{ code: 'usd', symbol: '$', title: 'US Dollars' },
{ code: 'eur', symbol: '€', title: 'Euros' }
];
}
constructor(currency, ...args) {
super(...args);
this.store = window.localStorage;
this.setCurrency(currency);
this.load();
}
setCurrency(code) {
// Only set the currency if it's valid for our Basket
Basket.currencies.forEach((currency) => {
if (currency.code == code) {
this.currency = currency;
this.store.cxCurrency = JSON.stringify(this.currency);
}
});
}
... <snip> ...
}
The class that it's extending, ItemSet can be found in basket-weaver:
https://github.com/andrewebdev/basket-weaver/blob/master/src/items.js#L72-L80
export class ItemSet extends Array {
getTotal(...args) {
let subTotals = this.map(item => { return item.getTotal(...args); });
if (!subTotals) throw "Cannot call getTotal() on an empty ItemSet";
return sum(...subTotals);
}
}
Finally, here is the code that is being generated by babel when it's
transpiled, just pasting the relevant part for brevity:
var Basket =
/*#__PURE__*/
function (_ItemSet3) {
babelHelpers.inherits(Basket, _ItemSet3);
function Basket() {
babelHelpers.classCallCheck(this, Basket);
return babelHelpers.possibleConstructorReturn(this, (Basket.__proto__ || Object.getPrototypeOf(Basket)).apply(this, arguments));
}
babelHelpers.createClass(Basket, [{
key: "setCurrency",
value: function setCurrency(code) {
var _this7 = this;
// Only set the currency if it's valid for our Basket
Basket.currencies.forEach(function (currency) {
if (currency.code == code) {
_this7.currency = currency;
_this7.store.cxCurrency = JSON.stringify(_this7.currency);
}
});
}
}, {
... <snip lots of other methods & properies> ...
}]);
return Basket;
}(_items.ItemSet);
_exports.Basket = Basket;
Last bit of background: I'm using polymer build to do the transpiling
since my components are mostly polymer elements.
Does anyone know what may be causing this?
Turns out this is because es5 would not let you extend arrays. There are some workarounds which I found here: https://stackoverflow.com/a/46898347/433267
I've implemented this in basket-weaver on a special es5-compat branch.

How to set an observer on an array property in polymer element?

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.