changing model property value is changing the same property in other object - javascript

im new to vue , i want user to be able to add some specific social media links to the page and edit some properties like it's text
i have 2 objects in my data , models and defaults
defaults contains selectable option for social media links and their initial values
basically i copy the default value into models and let the user customize the model via inputs
data () {
return {
models : [] ,
defaults : {
twitter : { id : null , placeholder : 'my twitter' , icon : 'twitter' , text : null , 'link' : null } ,
instagram : { id : null , placeholder : 'my instagram' , icon : 'instagram' , text : null , 'link' : null } ,
tiktok : { id : null , placeholder : 'my tiktok' , icon : 'tiktok' , text : null , 'link' : null } ,
} ,
}
} ,
so there a select menu for user to select which social he wants to add to the page
Select :
<ul >
<li v-for="(social, index ) in defaults" :key="index">
<a #click="appendSocial(index)">
{{ index }}
</a>
</li>
</ul>
here is my #click="appendSocial(index)" function
appendSocial(type){
let typedefault = this.defaults[type];
this.models.push(typedefault)
},
and finally i show my models to user and provide an input for editing it's text via v-model
<div v-for="(model, index) in models" v-bind:key="model.id">
<a class="button-preview">
{{ model.text === null ? model.placeholder : model.text }}
</a>
<label>Text</label>
<input type="text" v-model="model.text" :key="index" :placeholder="model.placeholder">
</div>
so here is the problem , for some reason changing the models properties will change the same property in defaults ... and changing defaults properties will change the same property models !!
like if i add a twitter menu to the page and change it's text (model.text) to abc via v-model ... it will also change defaults.twitter.text to abc
to better demonstrate the problem i've added some console log to my appendSocialfunction
appendSocial(type){
let typedefault = this.defaults[type];
console.log(`-------- default object for ${type} -----------`);
console.log(typedefault);
this.addElement(typedefault);
},
here is the result
1 - i've selected a twitter link and added to my models , you can see defaults.twitter.text is null
2 - i've change my model.text (i suppose it would be models[0].text) to abc
3 - i've added another twitter link to my page ... this time defaults.twitter.text is also abc
also changing defaults properties will effect all the models the has been getting their value from that default object
like if i change the defaults.instagram.text to xyz all my models which have got their initial values from defaults.instagram will also change their text to xyz
it's like they are referencing each other , but i haven't passed the value between 2 objects by reference
im not sure what's happening here and how can i prevent this ?

This is because
let typedefault = this.defaults[type];
this.models.push(typedefault)
Is storing the reference to the object into your this.models array. And so if you mutate the element, you're by default changing the base object. A quick and dirty way of doing a deep clone is the following.
let typedefault = this.defaults[type];
let clonedObj = JSON.parse(JSON.stringify(typedefault));
this.models.push(clonedObj)
Note: Lodash library does have a proper deep clone functionality.
https://www.geeksforgeeks.org/lodash-_-clonedeep-method/

Related

Overriding combo box for filtering items into the combobox, how to use this.filter item

I am using UI5 framework v1.38.39 and I want to do a custom combobox (no sap.m.Combobox but custom.comboboxsearch (which include sap.m.combobox and sap.m.comboboxbase)) where I can search what is inside (searching by contain) from key and text so I have something like follow in the custom control:
sap.ui.define([
"sap/m/ComboBox",
"sap/m/ComboBoxBase"
],
function (oComboBox, oComboBoxBase) {
"use strict";
/*some stuff*/
sValue = oEvent.target.value;
aVisibleItems1 = this.filterItems({
property: "text",
value: sValue
});
aVisibleItems2 = this.filterItems({
property: "key",
value: sValue
});
/*some stuff*/
My problem is that I don't have 2 filter, the second simply replace the first one then I don't have the items from the property text only from key, I would like to have both then I tried :
sValue = oEvent.target.value;
aVisibleItems1 = this.filterItems({
property: ["text","key"],
value: sValue
});
which give me the error :
Uncaught TypeError: p.charAt is not a function
at f.d.filterItems (ComboBox.js:6)
ComBox.js is a core file of UI5, then I tried :
sValue = oEvent.target.value;
aVisibleItems1 = this.filterItems({
property: "key" || "text",
value: sValue
});
Which also didn't work because take in consideration only key and no text, does someone has a solution ?
PS: As I use dynamic binding I suppose I cannot include items in view they are bind via another controller.
the view is :
<!--some stuff-->
<Label text="{i18n>MyText}" />
<Custom:ComboBoxSearch id="mycustombox" selectionChange='onChange'>
<core:Item key="{key}" text="{text}" />
</Custom:ComboBoxSearch>
<!--some stuff-->
items are added from the controller
mhm, i dont know if i understand your question right but i think it is this what you want.
<ComboBox
id="combobox1"
showSecondaryValues= "true"
items="{
path: '/yourdata',
sorter: { path: 'text' }
}">
<core:ListItem key="{key}" text="{text}" additionalText = "{key}"/>
</ComboBox>
and in your controller it should look like this:
this.getView().byId("combobox1").setFilterFunction(function(sTerm, oItem) {
return oItem.getText().match(new RegExp(sTerm, "i")) || oItem.getKey().match(new RegExp(sTerm, "i"));
});
so you can search for the key or the text it does not matter
sap.ui.ComboBox has a property called filterSecondaryValues which filters both the text and additionalText.
If you set 'additionalText' of the ListItem aggregation to the value of key (let showSecondaryValiues false if you dont wanna show them) and it should work?
<ComboBox
showSecondaryValues= "false"
filterSecondaryValues="true"
items="{
path: '/CountriesCollection'
}">
<core:ListItem key="{key}" text="{text}" additionalText="{key}"/>
</ComboBox>

CQ5 nested multi-field with select option

I will need to add the values that editor writes them in textfield from dialog panel into a <select> option. I have created the multi-field option for the dialog component, but not sure how I should get values in the <select> it self.
I'm using the JS widget for multi-field, you can find it here. I know that the multi-field JS should return a JSON array of options, how do I achieve this in my case?
Here's my XML markup:
<developer
jcr:primaryType="cq:Widget"
title="Data"
xtype="panel">
<items jcr:primaryType="cq:WidgetCollection">
<developer
jcr:primaryType="cq:Widget"
fieldDescription="Click the '+' to add a new data"
fieldLabel="Dev Data"
name="./devdata"
xtype="multifield">
<fieldConfig
jcr:primaryType="cq:Widget"
xtype="devprofile"/>
</developer>
</items>
</developer>
JavaScript that is adding the textfield for multi-field
this.developerName = new CQ.Ext.form.TextField({
fieldLabel : "Developer's Name",
allowBlank: true,
width : 400,
listeners : {
change : {
scope : this,
fn : this.updateHidden
},
dialogclose : {
scope : this,
fn : this.updateHidden
}
}
});
this.add(this.developerName);
And the markup:
<c:if test="${not(empty(developerName))}">
<select id="use-names" class="js-example-basic-multiple">
<option>Example</option>
<option>${developerName}</option>
</select>
</c:if>
Please let me know if you need more detailed shared.
ACS commons has pretty good working example of custom multifield. Take a look at https://adobe-consulting-services.github.io/acs-aem-commons/features/widgets.html
Just replace textarea with your dropdown and you will have what you need..

ng-model won't reflect changes?

I have built a simple shopping cart sample :
Those 3 lines are created via ng-repeat.
My goal :
I want the yellow part to become red when the relevant quantity ( red arrow) is more than 3.
So here is what I did : (http://jsbin.com/eXOgOpA/4/edit)
<div ng-repeat='item in items'>
<span ng-class='{isMoreThan3: IsMoreThan3()}'>{{item.title }}</span>
<input ng-model='item.quantity'>
...
</div>
Where IsMoreThan3 is a function which :
$scope.IsMoreThan3=function (){return $scope.item.quantity>3;};
Where
.isMoreThan3
{
color:red;
}
but it doesn't work.( calculations are ok , but the color is never red).
Question :
How can I fix my controller code to yield the right value for the model ?
In other words :
How can the controller , know the current item.quantity ?
nb
I know that I can put the logic into the markup But I don't want that. I want the controller to return a true/false value.
Try:
<span ng-class='{isMoreThan3: IsMoreThan3(item)}'>{{item.title }}</span>
JS:
$scope.IsMoreThan3=function (item){
return item.quantity>3;
};
The reason is ng-repeat will create its own scope, accessing $scope.item will not access the current item in the loop

Remove Item from Array in Meteor.js

I have a collection called Rulesets - each ruleset has an array of "rules". I have the following html which displays each ruleset and each rule:
<template name="rulesets">
{{#each rulesets}}
{{>rulesetSingle}}
{{/each}}
</template>
<template name="rulesetSingle">
{{#each rules}}
<p class="rule-description">{{this}}
x
</p>
{{/each}}
</template>
I want to be able to remove the rule when the user clicks the "rule-delete-btn".
I have the following javascript to do this:
Template.rulesetSingle.event({
'click .rule-delete-btn': function(e){
e.preventDefault();
var array = Rulesets.rules;
var index = array.indexOf(this);
array.splice(index, 1);
}
});
The delete isn't working because the "array" declaration isn't pulling a valid array. How can I find and store the array that contains "this" which is the current rule that needs to be deleted?
Assuming your Rulesets.rules array is an array of objects like :
Rulesets : { { title: 'title1',
rules : [ {name : 'rule1', description : 'description1'},
{name : 'rule2', description : 'description2'}
]
},
{ title: 'title2',
rules : [ {name : 'rule1', description : 'description1'},
{name : 'rule2', description : 'description2'}
]
} }
First, in your rulesetSingle template, you must assign the _id of that particular document to the <a> like :
x
Second thing, you are trying to remove rule entry using Array.splice(), this is not possible since the rules is a document inside the collection. You must do update query as Rulesets.update().
If you have done allow update on the server, you can delete this array entry from within the event handler, otherwise you must do Meteor.call() by passing rule an the _id of parent document.
So, the event handler will look something like :
'click .rule-delete-btn': function(e) {
e.preventDefault();
var rule = this;
var id = e.currentTarget.name;
Meteor.call('removeRule', id, rule);
}
On the server:
Meteor.methods({
removeRule: function(id, rule){
Rulesets.update({_id: id}, {$pull : {rules : rule}});
}
});

Ext JS Search possibility in MultiSelect ComboBox

I want to know that how in ExtJS MultiSelect ComboBox i can search for a value. Like if i have entered 's' then a list of items starting with a must be displayed and the item selector should select if it matches with 's'.
This is the code i tried.....
cbCfg = {
name : property.columnname,
hideOnSelect : false,
triggerAction : 'all',
mode : 'local',
width : comboFieldSize,
store : new Ext.data.SimpleStore({
id : 0,
fields : ['strValue','strText'],
data : data
}),
listWidth : 400,
valueField : 'strValue',
displayField : 'strText'
};
field = new form.MultiCombo(cbCfg);
thnkz in advance,
just:-)
I assume that MultiSelect ComboBox extends functionality of Ext.form.ComboBox.
You could try going with triggerAction : 'query', which is the default - it should filter out non-matching entries. Although that could be a little bit counter-intuitive for the user.
Another possible approach is adding typeAhead : true property - should trigger suggesting suggestions based on the text entered by user.

Categories