angular: asynchronously fill an array - arrays

I have an array of elements, a big one, rendered in a dropdown with *ngFor. Whenever I click to open the dropdown, the UI is blocked for a certain amount of time. Finally the dropdown opens and the items are all there.
It's not a big problem but I am trying to make the dropdown to instantly open on click, and the list to be filled and rendered asynchronously.
The original list is synchronously available, in a variable (it's not given from async call to server).
I think I should create a mirror array, starting from an empty, and write an asynchronous function to push elements from the original list to the mirror one.
What's a way to do something like that? I can't grab how to use observables for doing so.
This is my simplified setup
Component
source = [...]; // a thousand of objects
mirror = []; // progressively filled with source elements
async onDropdownOpen(open: boolean) {
if (open) {
// async fill of mirror
} else {
// empty mirror to boost next time dropdown opening
mirror = [];
}
}
Template (I am using ngBootstrap)
<div
ngbDropdown
#dd="ngbDropdown"
(openChange)="onDropdownOpen($event)">
<button class="btn btn-outline-primary" id="dropdownBasic1" ngbDropdownToggle>Toggle dropdown</button>
<div ngbDropdownMenu aria-labelledby="dropdownBasic1">
<div *ngFor="let el of mirror">
... async el rendering ...
</div>
</div>
</div>

It looks like you could use the Angular CDK Virtual Scroll directive.
<div ngbDropdownMenu aria-labelledby="dropdownBasic1">
<div *cdkVirtualFor="let el of mirror">
... async el rendering ...
</div>
</div>
https://material.angular.io/cdk/scrolling/overview

Related

Save information on div and retrieve that information onClick Event

Let's say that i have an array of 5 integers
[1,2,3,4,5]
I iterate the array and i create 5 divs. On each div i want to have a onClick handler. So when i click on a div i want to retrieve the number.
What is the best way to store the info, so later i can retrive it from the handler function?
I have tried the following and it's working, but i am not sure if this is the best solution because in my app i will have a large amount of these divs.
const array = [1,2,3,4,5];
// make a closure
function onDivClick(id) {
return () => {
console.log(id);
}
}
return (
<div>
{array.map( id => {
return <div onClick={onDivClick(id)}> ... </div>
})
</div>
)
I have seen also the the html data attribute:
So i can store and i can retrive it after with event.target.getAttributes('data-id').
Is there other ways, and which is the best?
What you have right now is not okay. {onDivClick(id)} executes on every render.
Instead, add the id attribute directly to the div, and access it in the click handler.
Rewrite your click handler like so:
function handleClick(event) {
let id = event.target.id;
console.log(id);
}
and attach it to the div like so:
<div id={id} onClick={handleClick} />

Appending value using React.js

I made todo app using jquery and now using react making same app. I made general layout of the app using react but facing problem in appending element by clicking button. The code i made in jquery was.
function entervalue()
{
var text = $('#inputext').val();
$("#list").append('<li class="item"> <input type="checkbox" class="option-input checkbox"> ' + text + '<button class="destroy"></button><li>');
$("#inputext").val('');
}
Here the value of input field is stored in var text.
List is appended in having checkbox, 'text' and a button.
I want same code to be written in react, so that when i click on button the following list gets appended.
Use state to store the new value. Save it as an array. React does a re-render when the state changes. During that render, map() through the value in the state and generate the HTML out of it.
this.setState({ //put this inside the 'onChange' handler of <input/>
value: e.target.value
})
This will set the new value to the state.
var todo = this.state.value.map(function(value){
return <li class="item"> <input type="checkbox" class="option-input checkbox">{value}<button class="destroy"></button><li>
})
todo will have the latest list of user inputs.
[UPDATE]
Check the example https://jsfiddle.net/Pranesh456/1veb7bdg/1/
jQuery is fundamentally different than React... you need to think of how you store data in state.. not actual DOM elements (jQuery like). You'll want to define your DOM elements in your render method and have it iterate through your state to generate the elements.
I made this repo to help teach a developer the transition from jQuery to React.. it might be helpful to checkout: https://github.com/bradbumbalough/react-tunes.
I also highly reccomend this doc if you're new to React: Thinking in React.

Why is there a MDL spinner residue after my promise is resolved in react?

I'm trying to display a spinner while fetching my API, then replace this spinner by the results I receive from my API call.
Basically I have a isFetching state set to true until my promise is resolved, so my render function looks like this:
render: function() {
if (this.state.isFetching) {
return <div className="mdl-spinner mdl-js-spinner is-active" />;
} else {
return <div>This is where I display my results.</div>;
}
}
It's working well and seems logic to me but, as the rendered function gets more complex, the spinner is appended to the returned html.
<div>This is where I display my results.</div>
<!-- The appeneded spinner, each div corresponding to a color (yellow, green, blue, red) -->
<div class="mdl-spinner__layer mdl-spinner__layer-1"></div>
<div class="mdl-spinner__layer mdl-spinner__layer-2"></div>
<div class="mdl-spinner__layer mdl-spinner__layer-3"></div>
<div class="mdl-spinner__layer mdl-spinner__layer-4"></div>
Why is this spinner residue appended after my promise is resolved?
Am I doing something conceptually wrong? Is it related to React or MDL?
I made a JS fiddle where I managed to reproduce the issue.
Note that if you simplify the <SubComponent />'s render function (such as by removing the img tag, the issue doesn't appear).
Edit
Thanks to Garbee's answer I ended up with this solution:
To ensure the node (responsible for creating internal nodes for styling) is properly destroyed after being upgraded, wrap it (in this case the spinner) in another element node.
render: function() {
if (this.state.isFetching) {
return (
<div>
<div className="mdl-spinner mdl-js-spinner is-active" />
</div>
);
} else {
return <div>This is where I display my results.</div>;
}
}
The updated JS fiddle.
This is because the spinner is creating internal nodes for styling as it is initialized. I'd assume react is just creating then destroying the node (which makes me question how it is upgraded in the example case provided since I don't see that.) Which, depending on how it destroys it could leave some cruft around.
Nothing can be done about this in MDL 1.x. We are working in 2.x to do no magic node creation for developers which should make things more clear.

ReactJS + Flux setState not working on single click

I'm writing my first reactjs + flux application and I have managed to get the basic code working. However, there is a list, which populates another list depending on which list item is clicked. This was working fine until I had my list data hard-coded in the store. But when I started getting the data for the list items from the server, the second list does not get populated on a single click - it needs two clicks to do that. I don't think that getting data from server is a problem because the default data is populated correctly. After debugging, I found out that the store is also returning the data correctly, but the setState() is not setting the data. What am I doing wrong?
Here's my code:
function getInitialData(){
return{
items:DataStore.getFirstItemData()
}
}
var ListItems = React.createClass({
getInitialState:function(){
return getInitialData();
},
_onChange:function(){
this.setState(getInitialData);
},
componentDidMount:function(){
DataStore.addChangeListener(this._onChange);
},
componentWillReceiveProps:function(){
this.setState({items:this.props.itemsData});
},
handleClick:function(e)
{
var data = DataStore.getSubItemsFor(e);
this.setState({items:data});
},
render:function(){
return(
<div>
<div className="col-md-3 dc-left" id="main-list">
<div className="dc-main-list-parent">
{this.props.listData.map(function(list_items){
return(
<div key={list_items.id} onClick = {this.handleClick.bind(this,list_items.id)}>
</div>
);
},this)}
</div>
</div>
<div className="col-md-9 dc-center" id="sub-list">
<SubList sublistData = {this.state.items} />
</div>
</div>
)
}
});
P.S. This code has been modified a little for posting here. Please overlook any syntactical errors - it works!
Probably things break down because updates to the DataStore trigger 2 changes:
The complete component gets rerendered by the parent component, which fires the componentWillReceiveProps + fires a render cycle.
You also have a listener to changes in the DataStore, so the update in the DataStore also fires the _onChange event, which tries to update the state as well
PS: the code has a typo:
this.setState(getInitialData);
should read:
this.setState(getInitialData());
I would suggest you remove the change listener part, and see if that helps..

React.js get checked inputs

I am quiet new to React and I wasn't able to figure out how to manipulate DOM.
I have a set of components with checkboxes, and I have a delete button, I want to delete the checked elements when delete button is clicked.
here is a snippet of code I am using :
...
deleteMessage: function(event) {
this.refs.select_message.getDOMNode(); // I get only the last element
},
...
...
render: function() {
var Messages = this.props.messages;
return (
<div>
<button onClick={this.deleteMessage}>Delete</button>
{Messages.map(function(message) {
return (
<div>
<input type='checkbox' className='select_message' ref='select_message'/>
</div>
);
})}
</div>
);
Am I doing it the right way?
What you should do is, in your deleteMessage, call a handler which you have passed from the parent. That, in turn, modifies the messages array from the outside. Then the new messages array will be passed in as props. The main insight you need is props are not only data passed for rendering, but also functions to be called when user interaction happens inside the component.

Resources