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?
Related
I made a rating as in the author's video: https://www.youtube.com/watch?v=dsRJTxieD4U
const rateStars = document.querySelectorAll('.js-rate');
rateStars.forEach((rateStar, clickedIdx) => {
rateStar.addEventListener('click', () => {
rateStars.forEach((otherRateStar, otherIdx) => {
if (otherIdx <= clickedIdx) {
otherRateStar.classList.add('rate__item_active');
};
});
});
});
Everything works perfectly, but my task is to create a js-class (OOP)
//rate.pug
mixin rate(params = {})
-
const {
rating= "",
} = params;
let i = 0;
.rate
ul.rate__list
while i < 5
if (i < rating)
li.rate__item(class= "js-rate rate__item_active")
else
li.rate__item(class= "js-rate")
- i++
I got the following code structure:
// index.js
import Rate from './Rate';
const rateStars = document.querySelectorAll('js-rate');
rateStars.forEach((rateStar, clickedIdx) => new Rate(rateStar, clickedIdx));
// Rate.js
class Rate {
constructor(rateStar, clickedIdx) {
this.rateStar = rateStar;
this.clickedIdx = clickedIdx;
this.bindEventListeners();
}
bindEventListeners() {
this.rateStar.addEventListener('click', this.handleRateClick.bind(this));
}
handleRateClick() {
this.rateStar.classList.add('rate__item_active');
}
}
export default Rate;
However, I have no idea how to proceed from here.
Sorry I'm a complete noob. This is my first time asking a question here.
the task is complicated by the fact that you need to place several ratings on one page.I write some mixins:
When there are several ratings on the page, then the stars are added to all the ratings.
1)this is the default state
After click
So the problem of your change to class, is that you're not adding rate__item_active to the previous stars as you're doing in the first code example.
So for your class to work like the first code example, this is how you should do it:
// index.js
import Rate from './Rate';
rateStars.forEach((rateStar, index) => new Rate(rateStar, index, rateStars));
// Rate.js
class Rate {
constructor(rateStar, index, rateStars) {
this.rateStar = rateStar;
this.clickedIdx = index;
this.rateStars = rateStars;
this.bindEventListeners();
}
bindEventListeners() {
this.rateStar.addEventListener('click', this.handleRateClick.bind(this));
}
handleRateClick() {
for(let i = 0; i <= this.clickedIdx; i++) {
this.rateStars[i].classList.add('rate__item_active');
}
}
}
export default Rate;
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)}/>
}
EDIT:
It seems that this warning has something to do with my input range component. I will search for the problem and ask here, if I can`t find the problem. Thanx
I searched here and some other threads, but could`t find a solution.
I am creating a questionnaire with some dynamic pages, where I can answer radio buttons, checkboxes etc.
When I click the next button, current values will be stored and the new Page comes.
I realized it with a counter for the pages.
If the counter reaches the and of the Pages a result Page will be shown.
When I click the next buttons all seems to work, but I get the warning:
Warning: Cannot update during an existing state transition (such as within render or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to componentWillMount.
I reduced the code for showing here. I hope this is ok.
this is the constructor:
constructor(props) {
super(props);
this.state = {
startDate: moment(),
values: {}
};
}
render function:
render() {
const {show_result} = this.props;
if(!show_result) {
return (
<div className="applicationbody">
{this.renderActiveForm()}
</div>
);
} else {
const {children} = this.props;
return (
<div className="applicationbody">
{children}
</div>
);
}
}
renderActivForm function:
renderActiveForm = () => {
const {page_now, count_cats, count_pages, categories_pages_quantity, language, catValuesCountAll, cat_values} = this.props;
return (
<div className="applicationbody__form">
{page_now.block.map((oneblock, i) => {
return (
this.renderReturnInput(oneblock, i)
);
})}
<div className="ButtonBar">
<a className="button"
onClick={this.save}>weiter</a>
</div>
</div>
);
}
save function:
save = () => {
const {page_now, count_cats, count_pages, categories_pages_quantity, catValuesCountAll} = this.props;
const cat_new = (count_pages + 1) === categories_pages_quantity.cats[count_cats].pages ? (count_cats + 1) : count_cats;
const page_new = (count_pages + 1) === categories_pages_quantity.cats[count_cats].pages ? 0 : (count_pages + 1);
if (page_now.evaluate === "1") {
var cat_value_of_this_page = sum(this.state.values);
function sum( obj ) {
var sum = 0;
for( var el in obj ) {
if( obj.hasOwnProperty( el ) ) {
sum += parseFloat( obj[el] );
}
}
return sum;
}
catValuesCountAll(page_new);
} else {
}
}
the catValuesCountAll action only increments the pagenow, so the new page with ne questions is shown. But why I get the warning?
I would be very happy if somebody could help me here.
EDIT:
It seems that this warning has something to do with my input range component. I will search for the problem and ask here, if I can`t find the problem. Thanx
Ok, I solved the problem now. The input-range component want to have a state for the min and max values.
So I setted the state during the rendering, cause it had to be dynamic. But now I set the state in componentWillMount and componentWillReceiveProps and the warning is gone.
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);
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.