I am working on a simple list builder, i can create and edit the list, basicly i have a modal where i build the list after that i can edit it, my issue is if i edit and change the number of items inside the list without pressing the edit, the changes to the list apply. all the rest that i change and don't press edit, works well.
Here is a example:
my modal edition:
data () {
return {
newItem: '',
listItem: 0,
showNewItem: false,
bulletList: {
currentSymbol: '•',
key: 'List',
items: [
],
ordered: false,
numbering: 38
},
listOptions: [
{ 'symbol': '•', 'number': 37 },
{ 'symbol': '1', 'number': 1 }
]
}
},
in my edition the only thing that can change is the bulletList object, so when i press the edition section it loads the object like this:
mounted () {
console.log('MOUNTED', this.bulletList.items)
var sectionKey = this.$store.getters.getCurrentEditionKey
this.bulletList =_.clone(this.$store.getters.getDocumentAttributes[sectionKey])
}
when i add a item to edit my bulletList and close the configuration modal it added(it shouldn't) the only thing i do when i add a new item is to push it to the local object inside my component, and not the one that i copied from vuex store, strange issue any help? it has something to do with the array behaviour?
removeItem () {
this.bulletList.items.splice(this.listItem, 1)
this.listItem = 0
},
addNewItem () {
this.bulletList.items.push(this.newItem)
this.newItem = ''
},
Related
I'm using dabeng/OrgChart in a react app. When the user clicks on a node Jquery populates an input box. An example of this can be seen here.
I want react to do this instead of Jquery so I can save the selected node in state instead of the inputbox holding a value.
I've tried things like using references, tried to see if I can call a react method within the jquery function but no joy.
Maybe a publisher/subscriber model would be the solution but not sure how to implement something like that.
class OrgTreeBase extends Component {
state = {
list: {
'name': 'Ball game',
'children': [
{
'name': 'Football',
'children': [
{
'name': 'man united',
'children': [
{ 'name': 'fred' },
{ 'name': 'ander herrera' },
{ 'name': 'scott mctominay' },
]
}
]
},
{
'name': 'Basketball',
'children': [
{'name': 'lakers'},
{ 'name': 'warriors' },
]
},
{ 'name': 'Volleyball' }
]
},
currentLevel: 0,
currentItem: [],
selectedItem: '',
};
componentDidMount() {
this.$el = $(this.el);
let datasource = this.state.list;
this.getId = () => {
return (new Date().getTime()) * 1000 + Math.floor(Math.random() * 1001);
}
//creates the initial chart with the following settings
var oc = $('#chart-container').orgchart({
'data': datasource,
'chartClass': 'edit-state',
'exportButton': true,
'exportFilename': this.state.name,
'parentNodeSymbol': 'fa-th-large',
'createNode': function ($node, data) {
$node[0].id = this.getId;
},
})
//when node is selected
oc.$chartContainer.on('click', '.node', function () {
var $this = $(this);
console.log("this is : " + $this.find('.title').text());
$('#selected-node').val($this.find('.title').text()).data('node', $this);
});
...
onNodeSelected = (node) => {
console.log(node + " was selected");
}
I want to see this.onNodeSelected() called when a node is clicked.
onNodeSelected = (node) => {
console.log(node + " was selected");
}
Thanks in advance for any help you can give me on this.
IMO you're already committed to using jQuery with this library. So I don't see why you can't just do something like this let's say:
//somewhere above
const self = this;
//when node is selected
oc.$chartContainer.on('click', '.node', function () {
var $this = $(this);
console.log("this is : " + $this.find('.title').text());
$('#selected-node').val($this.find('.title').text()).data('node', $this);
//set state and save node here on click. Or the node's text value
self.setState({})
});
Since, this isn't within your requirements I should say that the reason I suggest this is because the DOM appears to be generated by the orgChart library and adding react listeners to that is difficult. And there isn't any reason why you can't call react setState inside a jQuery click function.
It would also be wrong of me to suggest this without adding the warning that jQuery and React are both pretty large libraries that do largely similar-ish things. You probably already know that but, I still want to suggest finding a different organization library or make your own with React.
Suppose I have an array feedsArray, the example value may look like this:
this.feedsArray = [
{
id: 1,
type: 'Comment',
value: 'How are you today ?'
},
{
id: 2,
type: 'Meet',
name: 'Daily sync up'
}
]
Suppose I have registered two components: Comment and Meet, Each component has a prop setting as the following:
props: {
feed: Object
}
and the main component has the following definition:
<component v-for="feed in feedsArray" :feed="feed" :key="feed.id" :is="feed.type"></component>
As you can see, it uses is property to select different component. My question is, how to detect feed object change in the child component ? Like when I set
this.feedsArray[0] = {
id: 1,
type: 'Comment',
value: 'I am not ok'
}
How can the Comment component detect the changes ? I tried to add a watcher definition in the child component like the following:
watch: {
feed: {
handler (val) {
console.log('this feed is changed')
},
deep: true
}
},
But it doesn't work here. Anyone know how to solve this ?
Do not assign directly to an array using index - use splice() instead, otherwise JavaScript can not detect that you have changed the array.
If you want to change only the value of an already existing key of an object - then simply update it e.g. this.feeds[0].value = 'I am not okay any more';
This works for existing keys only - otherwise you have to use this.$set(this.feeds[0], 'value', 'I am not okay any more');
Below is the function I have used to fetch more photos from a service provider once clicked on more button:
showMore: function(){
this.$.resultList.collection.fetch({strategy:"merge",rpp:50});
}
This will call the fetch method defined in collection,
fetch: function(opts) {
this.params = {
feature: this.methodType || "popular",
image_size: 3,
sort: "created_at",
rpp: opts && opts.rpp || 25
};
return this.inherited(arguments);
},
This is working fine, but the problem is once more button is clicked, it should fetch another set of 25 photos and append it to my collection, but what actually happening is sometimes, it shows only say 5 photos along with previous photos .
What I understand by "merge" strategy is, if the records received is same as previous records, it will take only those records which are different from previously fetched records and updates the primarykey of the duplicate records.So one reason i am able to figure out is that, may be, it is fetching 25 photos next time, but because most of them are same as before, it is showing only those which are different from the previous fetched photos.
If i go with the "add" strategy, it works fine for one time, i.e, it shows another set of 25 photos appended to the collection, most of them are again same. But if click on more button one more time, no records are being added to the collection.No idea why this is happening.
How should i approach, if i want to fetch only new photos and append it to the collection.
Using the merge strategy is the right approach. Your description of merge is mostly accurate except that it doesn't update the primary key but instead updates the data for existing records with the same primary key.
It's difficult to say why using "add" doesn't always work. If the records don't have a primary key (which is id by default), "add" and "merge" should always add the records to the collection (unless you're using mergeKeys). If they do have a primary key, it's possible that you're trying to add duplicate records which Enyo will complain about and abort. Check your console log.
Without code, the only other suggestion is to set breakpoints and step through enyo.Collection.merge.
Here's an example of fetching records into a collection. If you comment out setting the id, merge and add strategies will always add records. If you comment out the merge strategy, the code will eventually error when requesting more records.
enyo.kind({
name: "ex.MockSource",
kind: "enyo.Source",
fetch: function(rec, opts) {
if(rec instanceof enyo.Model) {
rec.setObject(Faker.Helpers.createCard());
} else if (rec instanceof enyo.Collection) {
var count = opts && opts.count || 25;
var cards = [];
for(var i=0;i<count;i++) {
var card = Faker.Helpers.createCard();
// artificial id to force merges
card.id = enyo.now()%40;
cards.push(card);
}
opts.success(cards);
}
}
});
enyo.store.addSources({
mock: "ex.MockSource"
});
enyo.kind({
name: "ex.App",
kind: "Scroller",
bindings: [
{from: ".data", to: ".$.list.collection"},
{from: ".data.length", to: ".$.count.content", transform: function(v) {
return enyo.format("Displaying %s records", v);
}}
],
components: [
{name: "count"},
{name: "list", kind: "DataRepeater", components: [
{kind: "onyx.Item", components: [
{name: "name"},
{name: "phone"}
], bindings: [
{from: ".model.name", to: ".$.name.content"},
{from: ".model.phone", to: ".$.phone.content"}
]}
]},
{kind: "onyx.Button", content: "More", ontap: "moreTapped"}
],
create: enyo.inherit(function(sup) {
return function() {
sup.apply(this, arguments);
this.set("data", new enyo.Collection({
defaultSource: "mock"
}));
this.fetchRecords();
};
}),
fetchRecords: function() {
this.data.fetch({
count: 5,
strategy: "merge"
});
},
moreTapped: function() {
this.fetchRecords();
}
});
new ex.App().renderInto(document.body);
I use the function at the bottom to (re)create a store every time a grid within a (popup) window renders. However I don't understand why typeDetails is different from what gets logged on the longish line with Ext.pluck (based on https://stackoverflow.com/a/5709096/34806).
The former console log always prints what I expect, in the default case [{"label":"","value":""}], or when typeDetails is pre-populated something such as:
[{"label":"foo","value":"bar"},{"label":"what","value":"ever"}]
But the latter console.log always indicates an empty array, and I always get an empty grid. Could the line with pluck be at fault? That answer, though not the accepted or top-scoring, has been up-modded over 20. What else can I do to work through this.
listeners: {
render: {
fn: function (grid) {
var typeDetails = this.typeDetails || [{ 'label': '', 'value': ''}];
var store = Ext.create('Ext.data.Store', {
fields: ['label', 'value'],
data: [typeDetails]
});
console.log(Ext.encode(typeDetails));
console.log(Ext.encode(Ext.pluck(grid.store.data.items, 'data')));
grid.reconfigure(store);
}
}
}
UPDATE/OUTPUT
In response to Evan's comment "In the case where it defaults data, the store data will be [[{label: '', value: ''}]]" below is what is actually directly copied/pasted from my console:
[{"label":"","value":""}]
[]
However I think this is because the logging is before grid.reconfigure. Nevertheless shifting my console/logging as follows:
grid.reconfigure(store);
console.log(Ext.encode(this.typeDetails));
console.log(Ext.encode(typeDetails));
console.log(Ext.encode(Ext.pluck(grid.store.data.items, 'data')));
Results in the following mysterious output in the non-default case (when this.typeDetails is pre-populated):
[{"label":"foo","value":"bar"},{"label":"what","value":"ever"}]
[{"label":"foo","value":"bar"},{"label":"what","value":"ever"}]
[{"label":"","value":""}]
It's an editable grid, and the non-default values can only exist after the default empty row, so it's as though that empty row is being retained.
The following alternative approach works:
listeners: {
render: {
fn: function (grid) {
var store = Ext.create('Ext.data.Store', {
fields: ['label', 'value'],
data: []
});
if (this.typeDetails) {
for (var i = 0, n = this.typeDetails.length; i < n; i++) {
store.add(this.typeDetails[i]);
}
}
else {
store.add({ 'label': '', 'value': '' });
}
grid.reconfigure(store);
}
}
}
I have the following code (sloppy, I know...first javascript app). I am trying to get a combobox to populate with a list of features that fall under a given release (as selected in the first combobox). Almost everything is now working correctly, except everytime I click the feature combobox for the first time, it loads ALL features and completely ignores the filter. Even if I change the release box first, the feature box still populates with all features only on first click. Subsequent times it shows the correctly filtered features.
Even stranger, I've tried writing the total records in the Feature Store to the console, so I can see when this happens. When the feature combobox is first created, it has the correct number of records in it. However, as soon as I click the feature combobox for the first time, it triggers the "load" listener of the combobox, and pulls in all the features, ignoring the filter completely.
I'm so boggled, I've tried so many things to debug this, and at this point have no other options. Does anyone have any ideas as to why it would load the correct data first, then reload it and ignore the filters on first click?
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
var relComboBox = Ext.create("Rally.ui.combobox.ReleaseComboBox", {
fieldLabel: 'Choose a Release',
width: 300,
listeners: {
ready: function(combobox) {
this._releaseRef = combobox.getRecord().get("_ref");
this._loadFeatureInfo();
},
select: function(combobox) {
this._releaseRef = combobox.getRecord().get("_ref");
this._loadFeatureInfo();
},
scope: this
}
});
this.add(relComboBox);
},
_loadFeatureInfo: function() {
var featureStore = Ext.create("Rally.data.WsapiDataStore", {
model: "portfolioitem/Feature",
fetch: ["Name", "_ref"],
autoLoad: true,
filters: [
{
property: "Release",
operator: "=",
value: this._releaseRef
}
],
listeners: {
load: function(store) {
this._updateFeatureBox(store);
},
scope: this
}
});
},
_createFeatureBox: function(featureStore) {
this._featureComboBox = Ext.create("Rally.ui.combobox.ComboBox", {
fieldLabel: 'Choose a Feature to move',
store: featureStore,
listeners: {
select: function (combobox) {
this._featureRef = combobox.getRecord().get("_ref");
//calls method to get and display children of this feature in a grid
},
scope: this
}
});
this.add(this._featureComboBox);
},
_updateFeatureBox: function(featureStore) {
if (this._featureComboBox === undefined) {
this._createFeatureBox(featureStore);
} else {
this._featureComboBox.clearValue();
this._featureComboBox.bindStore(featureStore);
//calls method to get and display children of this feature in a grid
}
}
This is probably an issue caused by the featureStore loading twice: once when you created it, and the combobox also tells it to load again once the user opens the combobox to pick a Feature.
I encountered a very similar issue with a combobox on Stories, and I'd betcha dollars to donuts that Matt Greer's answer:
Strange Load behavior with Rally.ui.combobox.ComboBox
To my question, is the answer to yours too...