react set component props programaytically - javascript

I am trying to set an react (v15) component its props callback function dynamically somehow. Something like below, bud it isn't working as I wanted it to be.
The whole idea behind this is that the popup needs to return specific data for an grid item that is pressed specifically in the grid (html table).
Any suggestions how to archive this dynamic setting of a component and its props?
The code below gives this error:
TypeError: can't define property "dynamicCallback": Object is not extensible
(I guess the element props is set with Object.preventExtensions)
getGridColumnData() {
var notificationsColumnsObj = columns.GridData; // from json file, Array With columns
for (let iColumn = 0; iColumn < notificationsColumnsObj.length; iColumn++) {
if (notificationsColumnsObj[iColumn].field === 'Landlord_Name') {
notificationsColumnsObj[iColumn]['editor'] = function (container, options) {
that.refs.searchCompanyPopup.props.dynamicCallback = function (data) {
var landlordName = null;
var landlordCode = null;
if (data) {
landlordCode = data.Code;
landlordName = data.Name;
}
options.model.set('Landlord_Code', landlordCode);
options.model.set('Landlord_Name', landlordCode);
};
};
}
}
return notificationsColumnsObj;
}
<SearchPopup ref="searchPopup" data={this.state.data} />
-
Update
How I managed to tet it working in the end. I used the state to set the function used for the callback by the popup. When you click an item in the grid: notificationsColumnsObj[iColumn]['editor'] is called, then the state is set for the popup callback when it finishes to call the function.
var that;
class TheComponent extends React.Component {
constructor(props,context) {
super(props,context);
this.state={
data: {},
landlordSelectedCallback: function (data) {},
}
that = this;
}
getGridColumnData() {
var notificationsColumnsObj = columns.GridData; // from json file, Array With columns
for (let iColumn = 0; iColumn < notificationsColumnsObj.length; iColumn++) {
//only one item will match this, not multiple
if (notificationsColumnsObj[iColumn].field === 'Landlord_Name') {
notificationsColumnsObj[iColumn]['editor'] = function (container, options) {
that.setState({
landlordSelectedCallback: function (data) {
var landlordName = null;
var landlordCode = null;
if (data) {
landlordCode = data.Code;
landlordName = data.Name;
}
options.model.set('Landlord_Code', landlordCode);
options.model.set('Landlord_Name', landlordCode);
}
}, () => { //callback function, after the state is set
$(ReactDOM.findDOMNode(that.refs.searchPopup)).modal(); //shows the <SearchPopup modal
$(ReactDOM.findDOMNode(that.refs.searchPopup)).off('hide.bs.modal');
$(ReactDOM.findDOMNode(that.refs.searchPopup)).on('hide.bs.modal', function (e) {
$(ReactDOM.findDOMNode(that.refs.searchPopup)).off('hide.bs.modal');
that.closeGridCellFromEditing(); //closes the grid cell edit mode
});
});
};
}
}
return notificationsColumnsObj;
}
render() {
return (<div>[other nodes]
<SearchPopup ref="searchPopup" data={this.state.data} onModalFinished={this.state.landlordSelectedCallback} />
</div>);
}
}

It's not working for two reasons:
Because your ref is called searchPopup, not props. Per the documentation for legacy string refs, you would access that via this.refs.searchProps.
Because props are read-only.
I'm a bit surprised that the second rule is actively enforced, but that's a good thing. :-)
If you want to change the props of a child component, you do so by changing your state such that you re-render the child with the new props. This is part of React's Lifting State Up / Data Flows Down philosophy(ies).

Rather than feeding a new callback function, just keep one function but feed data to it.
getGridColumnData() {
var notificationsColumnsObj = columns.GridData; //from json file, Array With columns
for (let iColumn = 0; iColumn < notificationsColumnsObj.length; iColumn++) {
if (notificationsColumnsObj[iColumn].field === 'Landlord_Name') {
notificationsColumnsObj[iColumn]['editor'] = (function (container, options) {
this.options = options
}).bind(this);
}
}
return notificationsColumnsObj;
}
dynamicCallback = function (data) {
var landlordName = null;
var landlordCode = null;
if (data) {
landlordCode = data.Code;
landlordName = data.Name;
}
this.options.model.set('Landlord_Code', landlordCode);
this.options.model.set('Landlord_Name', landlordCode);
}
render() {
return <SearchPopup ref="searchPopup" data={this.state.data} dynamicCallback = {this.dynamicCallback.bind(this)}/>
}

Related

Why is React only rendering the last two elements in my array?

I am trying to render a series of images from a folder. I have managed to do this, but I would like to implement a system where the images go in rows of 2. This has worked, but it seems to be overwriting the other rows, because I only get the last two rendered. I would be very appreciative of any help. Here is my code:
var imageArray = [];
var imageCount = 0;
// Obtain the root
const rootElement = document.querySelector('.containerMain')
// Create a ES6 class component
class ShoppingList extends React.Component {
// Use the render function to return JSX component
render() {
return (
<div className="row" key={uuidv4()}>
{imageArray}
</div>
);
}
}
function renderImages() {
ReactDOM.render(
<ShoppingList/>,
rootElement
)
}
var folder = "Images/";
var numberOfImages = 0;
function ajaxRender() {
return $.ajax({
url : folder,
success: function (data) {
$(data).find("a").attr("href", function (i, val) {
if( val.match(/\.(jpe?g|png|gif)$/) ) {
if(imageCount===2) {
renderImages()
imageCount = 0;
imageArray = [];
} else {
imageArray.push(<img src={val} key={val}></img>)
imageCount += 1;
}
console.log(imageArray);
}
});
}
});
}
$.when(ajaxRender().done(function(){
console.log(imageCount)
}));
Any help would be greatly appreciated. Thanks in advance.
It seems like you keep resetting the array every time you put in 2 images? Can't you just make a system where if the image index is an even number, you render on one row, and if the image index is an odd number, it'll render on a row below it possibly?

What is the best way to structure my data model in nodeJS

I have a route that sends to me many requests over time, that contain data to stored in a model.
So the first request does not contain all the needed data and I need to parse each request every time new information comes and store it.
Actually I have a separate js file which contains the "model" and methods
const request = require('request-promise');
function Conversation(param) {
console.log(param)
this.endpoint ;
this.id = this.getConvId(param);
this.createdOn = Date.now();
this.to = this.getTo(param);
this.from = this.getFrom(param);
this.state = this.getState(param);
this.importance = this.getImportance(param);
this.threadId = this.getT;
this.subject = this.getSubject(param);
this.name=this.getName(param);
}
Conversation.prototype.sendIM =function sendIM(param) {
// method to send message to a user
}
Conversation.prototype.getConvId = function getConvId(param) {
return param.operationId
};
Conversation.prototype.getTo = function getTo(param) {
return param.to
}
Conversation.prototype.getDirection = function getDirection(param) {
return param.direction
}
Conversation.prototype.getState = function getState(param) {
return param.state
}
Conversation.prototype.getOperationId = function getOperationId(param) {
return param.sender[0].events[0]._embedded["service:messagingInvitation"].opertionId
}
Conversation.prototype.getImportance = function getSubject(param) {
return param.importance;
}
Conversation.prototype.getSubject = function getSubject(param) {
return param.sender[0].events[0]._embedded["service:messagingInvitation"].subject
}
Conversation.prototype.getFrom= function getFrom(param) {
return param._embedded["localParticipant"].uri
}
Conversation.prototype.getName= function getName(param) {
return param._embedded["acceptedByParticipant"].name
}
module.exports = Conversation;
In my main app file I create a new conversation object:
var Conversation= require('./Conversation');
app.post('/callback', function (req, res) {
if(req.body.sender.length==0) {
res.status(200).send();
}
if(req.body.sender.length>0) {
const conv = new Conversation(req.body.sender[0].events[0]._embedded['service:messagingInvitation']);
res.status(200).send();
}
})
the method works well when I have to fill the model once, but when I update an item it becomes complicated.
So I would like to know what is the best way to structure my files so that I will be able to add new data to my model over time ?

Updating an array object without cloning it

I'm trying to pass a pointer to a function through the parameters, so the function can update the parameters of the object. The object is bound to the view using {{tasks}} in the tasks.component.html file. Somehow, it looks like the mutations that I do in the provideAndDisplayTasks() function don't change the tasks property in tasks.component.ts at all. I've got similar code that does the same. There it correctly updates the object. I don't understand why it doesn't work in this situation.
How do I update the array, without cloning it?
tasks.component.ts:
private provideAndDisplayTasks() {
const self = this;
self.tasks.push(new Task());
console.log('self.tasks', self.tasks);
this.tasksRepository.provideTasks(self.tasks);
}
tasks-repository.ts:
public provideTasks(tasks: Task[]) {
console.log('parameter', tasks);
this.get('truelime.task/all').subscribe(
data => this.addTasksToTaskArray(tasks, data),
error => {
if (error.status === 401) {
this.router.navigateByUrl('login');
}
Logger.getInstance().logError(error);
}
);
}
private addTasksToTaskArray(tasks: Task[], tasksDto) {
const taskArray = tasksDto.truelime_tasks[0].TrueLime_Task;
for (let i = 0; i < taskArray.length; i++) {
const taskDto = taskArray[i];
const task = new Task();
task.id = taskDto.TaskID;
task.title = taskDto.TaskTitle;
task.description = taskDto.TaskDescription;
task.status = taskDto.TaskStatus;
tasks.push(task);
console.log('tasks', tasks);
}
}
Console logs:

How to update JavaScript array dynamically

I have an empty javascript array(matrix) that I created to achieve refresh of divs. I created a function to dynamically put data in it. Then I created a function to update the Array (which I have issues).
The Data populated in the Array are data attributes that I put in a JSON file.
To better undertand, here are my data attributes which i put in json file:
var currentAge = $(this).data("age");
var currentDate = $(this).data("date");
var currentFullName = $(this).data("fullname");
var currentIDPerson = $(this).data("idPerson");
var currentGender = $(this).data("gender");
Creation of the array:
var arrayData = [];
Here is the function a created to initiate and addind element to the Array :
function initMatrix(p_currentIDPerson, p_currentGender, p_currentFullName, p_currentDate, p_currentAge) {
var isFound = false;
// search if the unique index match the ID of the HTML one
for (var i = 0; i < arrayData.length; i++) {
if(arrayData[i].idPerson== p_currentIDPerson) {
isFound = true;
}
}
// If it doesn't exist we add elements
if(isFound == false) {
var tempArray = [
{
currentIDPerson: p_currentIDPerson,
currentGender: p_currentGender,
currentFullName: p_currentFullName,
currentDate: p_currentDate, currentAge: p_currentAge
}
];
arrayData.push(tempArray);
}
}
The update function here is what I tried, but it doesn't work, maybe I'm not coding it the right way. If you can help please.
function updateMatrix(p_currentIDPerson, p_currentGender, p_currentFullName, p_currentDate, p_currentAge) {
for (var i = 0; i < arguments.length; i++) {
for (var key in arguments[i]) {
arrayData[i] = arguments[i][key];
}
}
}
To understand the '$this' and elm: elm is the clickableDivs where I put click event:
(function( $ ) {
// Plugin to manage clickable divs
$.fn.infoClickable = function() {
this.each(function() {
var elm = $( this );
//Call init function
initMatrixRefresh(elm.attr("idPerson"), elm.data("gender"), elm.data("fullname"), elm.data("date"), elm.data("age"));
//call function update
updateMatrix("idTest", "Alarme", "none", "10-02-17 08:20", 10);
// Définition de l'evenement click
elm.on("click", function(){});
});
}
$('.clickableDiv').infoClickable();
}( jQuery ));
Thank you in advance
Well... I would recommend you to use an object in which each key is a person id for keeping this list, instead of an array. This way you can write cleaner code that achieves the same results but with improved performance. For example:
var myDataCollection = {};
function initMatrix(p_currentIDPerson, p_currentGender, p_currentFullName, p_currentDate, p_currentAge) {
if (!myDataCollection[p_currentIDPerson]) {
myDataCollection[p_currentIDPerson] = {
currentIDPerson: p_currentIDPerson,
currentGender: p_currentGender,
currentFullName: p_currentFullName,
currentDate: p_currentDate,
currentAge: p_currentAge
};
}
}
function updateMatrix(p_currentIDPerson, p_currentGender, p_currentFullName, p_currentDate, p_currentAge) {
if (myDataCollection[p_currentIDPerson]) {
myDataCollection[p_currentIDPerson] = {
currentGender: p_currentGender,
currentFullName: p_currentFullName,
currentDate: p_currentDate,
currentAge: p_currentAge
};
}
}
Depending on your business logic, you can remove the if statements and keep only one function that adds the object when there is no object with the specified id and updates the object when there is one.
I think the shape of the resulting matrix is different than you think. Specifically, the matrix after init looks like [ [ {id, ...} ] ]. Your update function isn't looping enough. It seems like you are trying to create a data structure for storing and updating a list of users. I would recommend a flat list or an object indexed by userID since thats your lookup.
var userStorage = {}
// add/update users
userStorage[id] = {id:u_id};
// list of users
var users = Object.keys(users);

React fixed-data-table: Uncaught TypeError: this._dataList.getSize is not a function

I'm attempting to use this example by the React developers to make a search filter for a table.
I have the table working fine with data from my backend statically. I have taken out an array for "sample" data to get the search functionality working. But I'm having a difficult time wrapping my head around how they use the "fake data" to populate their table as seen here, in contrary to "just" populating it with a test array as I want to.
Here's my source code. I want to filter through the "firstName" column, just as in Facebook's example(For simplicity). The error stems from when getSize() is called... But I suspect the issue is something else.
class DataListWrapper {
constructor(indexMap, data) {
this._indexMap = indexMap;
this._data = data;
}
getSize() {
return this._indexMap.length;
}
getObjectAt(index) {
return this._data.getObjectAt(
this._indexMap[index],
);
}
}
class NameTable extends React.Component {
constructor(props) {
super(props);
this.testDataArr = []; // An array.
this._dataList = this.testDataArr;
console.log(JSON.stringify(this._dataList)); // It prints the array correctly.
this.state = {
filteredDataList: new DataListWrapper([], this._dataList)
};
this._onFilterChange = this._onFilterChange.bind(this);
}
_onFilterChange(e) {
if (!e.target.value) {
this.setState({
filteredDataList: this._dataList,
});
}
var filterBy = e.target.value;
var size = this._dataList.getSize();
var filteredIndexes = [];
for (var index = 0; index < size; index++) {
var {firstName} = this._dataList.getObjectAt(index);
if (firstName.indexOf(filterBy) !== -1) {
filteredIndexes.push(index);
}
}
this.setState({
filteredDataList: new DataListWrapper(filteredIndexes, this._dataList),
});
}
render() {
var filteredDataList = this.state.filteredDataList;
if (!filteredDataList) {
return <div>Loading table.. </div>;
}
var rowsCount = filteredDataList.getSize();
return (
<div>
<input onChange={this._onFilterChange} type="text" placeholder='Search for first name.. ' />
{/*A table goes here, which renders fine normally without the search filter. */}
</div>
);
}
}
export default NameTable
Your problem is in _onFilterChange method.
You are doing this:
var size = this._dataList.getSize();
this._dataList is just an array, that's why getSize() does not exist in that object.
If I'm not misundertanding you should do this:
var size = this.state.filteredDataList.getSize();
The same will happend inside the loop, you are doing this:
var {firstName} = this._dataList.getObjectAt(index);
when you should do this:
var {firstName} = this.state.filteredDataList.getObjectAt(index);
Your _onFilterChange method should look something like this:
_onFilterChange(e) {
if (!e.target.value) {
this.setState({
filteredDataList: this._dataList,
});
}
var filterBy = e.target.value;
//var size = this._dataList.getSize();
var size = this.state.filteredDataList.getSize();
var filteredIndexes = [];
for (var index = 0; index < size; index++) {
//var {firstName} = this._dataList.getObjectAt(index);
var {firstName} = this.state.filteredDataList.getObjectAt(index);
if (firstName.indexOf(filterBy) !== -1) {
filteredIndexes.push(index);
}
}
this.setState({
filteredDataList: new DataListWrapper(filteredIndexes, this._dataList),
});
}
getSize() and getObjectAt() can only be called on a data object which implements these methods, such as the DataListWrapper object.
If you pass a plain data array to render() then it does not offer the getSize() and getElementAt() methods and the call to the methods will fail.
The original demo works because the FakeObjectDataListStore data is an object (a 'FakeObjectDataListStore') which implements the getSize and getObjectAt methods).
So easiest integration is to make sure the data passed in is an object that offer these methods. Based in my case on the 'examples/FilterExample' I found the easiest integration (after struggling with many bad ones) was to turn the existing 'helpers/FakeObjectDataListStore.js' into my own helpers/ObjectDataListStore.js (or chose your name) thus retaining the existing method wrapping structure and size params throughout the design. I then simply replaced the calls to the 'fake' component with references to my own non-wrapped local arrays of list rows. You can arrange your local data to be static, or dynamically loaded from whatever database environment you use. It was then easy to modify the _setFiltered() method to filter on something else than 'firstName'.
The cool thing with FixedDataTable is its ability to browse large lists,
and that the developer can write own custom cell renderers for example displaying a progress bar, button or menu anywhere in a list row.

Categories

Resources