I'm trying to do a post of multiple points of data from a form input.
However, the form data doesn't reach json output payload (I checked the network output). It seems to never get triggered.
If there's a better way to re-write this- I am open to it
I've put it all into Codepen- http://codepen.io/yarnball/pen/LRVgpo?editors=1011
The data needs to be posted in this exact way:
{
"title": "SAMPLE",
"tag": [
{
"name": "Movie",
"taglevel": 1,
}
],
"info": []
}
Post method
var Postapi = React.createClass({
componentWillMount () {
var form = document.querySelector('form')
return fetch('http://localhost:8000/api/Data/', {
method: 'POST',
body: JSON.stringify({
title: this.state.itemtitle,
tag:[
{name:this.state.tagtitle,
taglevel:this.state.taglevel}
],
info:[]
})
})
},
Sample return
<form onSubmit={this.handleSubmit}>
...
<input
placeholder="Item Title"
type="text"
itemtitle={this.state.itemtitle}
onChange={this.handleChange}
/>
Initial state & submit
getInitialState: function() {
return {
itemtitle: [],
tagtitle: [],
taglevel: [],
tagoptions: Exampledata
};
},
handleChange: function(event) {
this.setState({itemtitle: event.target.itemtitle});
this.setState({tagtitle: event.target.tagtitle});
this.setState({tagname: event.target.tagname});
},
handleSubmit: function(e) {
e.preventDefault();
var itemtitle = this.state.itemtitle
var tagtitle = this.state.tagtitle
var taglevel = this.state.taglevel
this.setState({itemtitle: '', text: ''});
},
you must call your fetch function in handlesubmit function...according to this link , componentWillMount : is executed before rendering, on both server and client side. so your form is empty.
you need read more about react life cycle.
Related
Trying to implement pagination, Initially I'm trying to load datatable with few records, once page loaded trying to click pagination buttons like next or any pagination buttons to update the new set of records. I'm able to get the iStart, iEnd records but unable to update the url for every pagination click. Trying to print the console but function is not calling and console.log is not updated with new params. Could you please suggest me how to do the update the params for API. Here is the sample code,
Sample Demo datatatble is not work with pagination, for verification printing the console for the updated querystring.
ngOnInit(): void {
this.dtOptions = {
processing: true,
destroy: true,
columns: [
{ title: '<input type="checkbox" />' },
{ data: 'index' },
{ data: 'firstname' },
{ data: 'lastname' }
],
infoCallback: (oSettings, iStart, iEnd, iMax, iTotal, sPre) => {
pageStartNo = iStart;
pageEndNo = iEnd;
console.log(pageStartNo, pageEndNo);
// this.loadTable();
}
};
}
loadTable(){
let params = new HttpParams()
.set('param1', '123')
.set('param2', '456')
.set('minNumber', pageStartNo)
.set('maxNumber', pageEndNo);
console.log('params >>>>>>>>>>>>>' + params.toString());
this.http
.get<any[]>(
'https://raw.githubusercontent.com/l-lin/angular-datatables/master/demo/src/data/data.json',
{
params
}
)
.subscribe(response => {
this.persons = response.data;
this.dtTrigger.next();
});
}
HTML code:
<button (click)="loadTable()">Load Table</button>
Sample Demo Stackblitz
If I understand your question correctly, you wanted to apply server-side pagination right?
Here is an official documentation for this.
Add ajax method in dtOptions.
this.dtOptions = {
pagingType: 'full_numbers',
pageLength: 10,
serverSide: true,
processing: true,
ajax: (dataTablesParameters: any, callback) => {
console.log('Params', dataTablesParameters);
//If you have different key for page number/size change it
dataTablesParameters.minNumber = dataTablesParameters.start + 1;
dataTablesParameters.maxNumber =
dataTablesParameters.start + dataTablesParameters.length; this.http
.post<any[]>(
'YOUR_API_NAME_HERE',
dataTablesParameters,
{}
)
.subscribe(resp => {
this.persons = resp.data;
//Once API fetched data successfully inform datatable by invoking the callback
callback({
recordsTotal: resp.recordsTotal,
recordsFiltered: resp.recordsFiltered,
data: []
});
});
},
columns: [{ data: 'id' }, { data: 'firstName' }, { data: 'lastName' }]
};
Working Stackbliz Demo
I'm working on a Vue project which has a component for loading content into a modal via an ajax call:
<load-content target="foo"></load-content>
<load-content target="bar"></load-content>
<load-content target="oof"></load-content>
<load-content target="rab"></load-content>
Here's an example template:
<template>
<span class="load-content-wrapper" v-on:click="load">
Click
</span>
</template>
<script>
export default {
name: 'load content',
props: {
target: {
type: String,
required: true
}
},
methods: {
load() {
$('#load-content-modal').modal('show');
this.$store.dispatch('loadContent', this.target);
},
}
};
</script>
Which would trigger this example action:
const actions = {
loadContent ({ commit }, target) {
$.ajax({
url: '/api/fetch-content/' + target,
}).then((data) => {
// Load Modal Window
});
},
};
This all works well, except we cannot guarantee that the Ajax call will always return content. Depending on the target it could return 404.
Ideally I want to automatically disable individual load-content components if '/api/fetch-content/' + target isn't available to prevent users from trying to select unavailable content.
What is the correct/ most efficient way to do this?
You should make your "target" field not required and instead add a default value empty string.
And add an "if" condition to your load method. If "target" is empty, it will not proceed.
export default {
name: 'load content',
props: {
target: {
type: String,
default: ''
}
},
methods: {
load() {
if (!this.target) return;
$('#load-content-modal').modal('show');
this.$store.dispatch('loadContent', this.target);
},
}
};
Create a store variable loading and mutate it in your actions as follows:
loading: false
const actions = {
loadContent ({ commit }, target) {
$.ajax({
url: '/api/fetch-content/' + target,
}).then((data) => {
// Load Modal Window
commit(setLoading)
});
},
};
Then in muatations ->
setLoading (state, loading) {
state.loading = true
}
Now in your vue file use this store variable and check if it is true then load the component.You may check this created or mounted events of the component.
Option 1
Preemptively load the content, and disable the ones that return an error.
This is what the parent component will look like
<template>
<load-content
v-for="(target, index) in loadedTargets"
:key="index"
target="target"
/>
</template>
<script>
export default {
name: 'load content parent',
data: function() {
return {
targets: [
{link: 'foo', data: null, loaded: false, error: null},
{link: 'bar', data: null, loaded: false, error: null},
{link: 'oof', data: null, loaded: false, error: null},
{link: 'rab', data: null, loaded: false, error: null},
]
}
},
computed: {
loadedTargets() {
return this.targets.filter(t => t.loaded)
}
},
methods: {
load(target) {
const self = this;
$.ajax({
url: '/api/fetch-content/' + target.link,
}).then((data) => {
self.targets[indexOf(target)].data = data
self.targets[indexOf(target)].loaded = true
}).catch((error) => {
self.targets[indexOf(target)].error = error
});
},
},
mounted() {
this.targets.forEach(target => this.load(target))
}
};
</script>
Option 2
Preemptive loading is expensive (and since I don't know how many targets you might have), you could also show success/error in the modal. Proper UX would dictate that an explicit action by the user should lead to a result (i.e. if the user clicks a link, he should either see data in the modal, or an error)
This is what your action will look like:
const actions = {
loadContent ({ commit }, target) {
$.ajax({
url: '/api/fetch-content/' + target,
}).then((data) => {
// Load Modal Window
}).catch((error) => {
// Load Modal Window, and show the error
});
},
};
Using React.js I have written a simple app that gets json and uses some of that data returned to build html.
Although, when the JSON changes, the html does not. Am I missing something here?
Here is my code -
<script type="text/jsx">
var classNames = ({
'auditNumber': "auditNumber",
'audit-overview-box': "audit-overview-box"
});
var AuditOverviewBox = React.createClass({
render: function () {
return (
<div className="audit-overview-box">
<h1 className={classNames.auditNumber}>{this.props.auditNo}</h1>
<span>{this.props.creationDate}</span>
</div>
)
}
});
var AuditBoxes = React.createClass({
getInitialState: function () {
return {
data: []
}
},
componentWillMount: function () {
this.dataSource();
},
componentWillReceiveProps: function (nextProps) {
this.state.data(nextProps);
},
dataSource: function (props) {
props = props || this.props;
return $.ajax({
url: '../json.php',
dataType: 'json',
cache: false,
success: function (data) {
this.setState({data: data});
}.bind(this),
error: function (xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function () {
var data = this.state.data;
console.log("data");
var photos = data.map(function (audit) {
return <AuditOverviewBox key={audit.auditNo.toString()} auditNo={audit.auditNo}
creationDate={audit.creationDate}/>
});
return (
<div className='myAudits'>
{photos}
</div>
)
}
});
ReactDOM.render(<AuditBoxes />, document.getElementById('audits-div'));
</script>
And the JSON -
[{
"auditNo": "a1201",
"creationDate": "21/10/2016"
},
{
"auditNo": "a1221",
"creationDate": "21/10/2016"
},
{
"auditNo": "a1211",
"creationDate": "21/10/2016"
}]
You cannot push changes from the server to the browser (unless you use websockets). If you just need to update once in a while you should setup your code around the ajax request in such a way that it will perform a request every n seconds. The simplest solution would be using setInterval()
setInterval(
function () {
// your ajax code
},
5000
)
that way the request to the server will be done every 5 seconds. Please be aware that you can overload your server if you set the interval to short / have a lot of visitors.
There are only two ways to change the data. You can use .setState method or directly set data to .state property and call .forceUpdate method of component, but this method is stritly unrecommended.
You can read more about it here: https://facebook.github.io/react/docs/state-and-lifecycle.html
I am building my first application with React.js.
The application uses external movie API (https://www.themoviedb.org/documentation/api) to search and list movies.
The request takes parameters "query" and optionally "page". The response already contains total number of results and total number of existing pages.
The sample response looks something like this:
{
"page": 1,
"results": [
{
"adult": false,
"backdrop_path": "/8uO0gUM8aNqYLs1OsTBQiXu0fEv.jpg",
"genre_ids": [18],
"id": 550,
"original_language": "en",
"original_title": "Fight Club",
"overview": "A ticking-time-bomb insomniac and a slippery soap salesman channel primal male aggression into a shocking new form of therapy. Their concept catches on, with underground \"fight clubs\" forming in every town, until an eccentric gets in the way and ignites an out-of-control spiral toward oblivion.",
"release_date": "1999-10-14",
"poster_path": "/811DjJTon9gD6hZ8nCjSitaIXFQ.jpg",
"popularity": 4.39844,
"title": "Fight Club",
"video": false,
"vote_average": 7.8,
"vote_count": 3527
},
{
"adult": false,
"backdrop_path": "/qrZssI8koUdRxkYnrOKMRY3m5Fq.jpg",
"genre_ids": [
27
],
"id": 289732,
"original_language": "zh",
"original_title": "Zombie Fight Club",
"overview": "It's the end of the century at a corner of the city in a building riddled with crime - Everyone in the building has turned into zombies. After Jenny's boyfriend is killed in a zombie attack, she faces the challenge of surviving in the face of adversity. In order to stay alive, she struggles with Andy to flee danger.",
"release_date": "2014-10-23",
"poster_path": "/7k9db7pJyTaVbz3G4eshGltivR1.jpg",
"popularity": 1.621746,
"title": "Zombie Fight Club",
"video": false,
"vote_average": 3.5,
"vote_count": 2
}
],
"total_pages": 1,
"total_results": 2
}
The structure, that I have picked is the following:
MovieSearchPage
- SearchForm
- SearchResults
--MovieBox
--Pagination
---Page
Here's my solution, which almost works fine:
var SearchResults = React.createClass({
handlePageChange: function(pageNum) {
this.props.onPageChange(pageNum);
},
render: function() {
var movies = [];
this.props.data.results.forEach(function(movie) {
movies.push(<MovieBox movie={movie} key={movie.id}/>);
});
return (
<div>
<h3>Search results ({this.props.data.total_results} found)</h3>
<Pagination onPageClick={this.handlePageChange} currentPage={this.props.data.page} totalPages={this.props.data.total_pages} totalResults={this.props.data.total_results}/>
<div>{movies}</div>
</div>
);
}
});
var SearchBar = React.createClass({
getInitialState: function() {
return {query: ''};
},
handleQueryChange: function(e) {
this.setState({query: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var query = this.state.query.trim();
if (!query || query.length < 3) {
return;
}
this.props.onSearch(query);
},
render: function() {
return (
<form className="searchForm" onSubmit={this.handleSubmit}>
<input
type="text"
name="query"
placeholder="Search for a movie..."
value={this.state.query}
onChange={this.handleQueryChange}
/>
<input type="submit" value="Search Movie"/>
</form>
);
}
});
var MovieBox = React.createClass({
render: function() {
var m = this.props.movie;
return (
<div className="movieBox">
<div>{m.original_title} ({m.release_date})</div>
</div>
);
}
});
var Pagination = React.createClass({
render: function() {
var pages = [];
var total = this.props.totalPages;
for (var i = 1; i <= total; i++) {
pages.push(<Page key={i} onPageClick={this.props.onPageClick} pageNum={i} active={(this.props.currentPage == i) || false}/>);
}
return (
<div className="pagination">{pages}</div>
);
}
});
var Page = React.createClass({
handleClick: function(e) {
var pageNum = e.target.innerHTML;
this.props.onPageClick(pageNum);
},
render: function() {
return (
<a href="#" onClick={this.handleClick} className={this.props.active ? 'active' : ''}>{this.props.pageNum}</a>
)
}
});
var MovieSearchPage = React.createClass({
getInitialState: function() {
return {query: '', pageNum: 1, data: {results: [], total_pages: 0, total_results: 0}};
},
initSearch: function(query) {
this.setState({query: query});
this.doSearch(query, this.state.pageNum);
},
nextPage: function(pageNum) {
this.setState({pageNum: pageNum});
this.doSearch(this.state.query, pageNum);
},
doSearch: function(query, pageNum) {
$.ajax({
url: this.props.url,
dataType : 'json',
type: 'POST',
data: {query: query, api_key: this.props.apiKey, page: pageNum},
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function() {
return (
<div>
<SearchBar onSearch={this.initSearch}/>
<SearchResults onPageChange={this.nextPage} data={this.state.data}/>
</div>
);
}
});
ReactDOM.render(
<MovieSearchPage url="http://api.themoviedb.org/3/search/movie" apiKey=""/>,
document.getElementById('container')
);
The problem, that I am having here, is that initial search is performed within SearchForm, using "query" state, however, I want to perform search requests to API from clicking on pagination (same query string, but different page number) without reloading the page.
I managed to achieve this by passing relevant parent method from MovieSearchPage all the way down to Page component.
This works fine, but I firmly believe, that this is not the most elegant solution to do this - the same method is passed multiple times plus I had to duplicate "query" state in both MovieSearchBox and SearchForm.
Please forgive me being a noob, but this is really the first time I am using a complex front-end framework. What is the proper way to do it in order to achieve this?
Despite situation being very generic, I was not able to find anything on the web.
Thanks in advance!
I am getting this error can anyone please tell me how I can debug this further?
Warning: setState(...): Can only update a mounted or mounting
component. This usually means you called setState() on an unmounted
component. This is a no-op.
Can anyone help?
This is my component which is causing the error:
var postal = require('postal'),
contactChannel = postal.channel("contact"),
React = require('react');
var ContactSelector = React.createClass({
getInitialState: function() {
return {
selectedContacts:[]
};
},
handleChange: function(e) {
var id = e.target.attributes['data-ref'].value;
if (e.target.checked === true){
contactChannel.publish({
channel: "contact",
topic: "selectedContact",
data: {
id: id
}});
} else{
contactChannel.publish({
channel: "contact",
topic: "deselectedContact",
data: {
id: id
}
});
}
},
render: function() {
var id = this.props.data.id;
var isSelected = this.props.data.IsSelected;
return (
<div className="contact-selector">
<input type="checkbox"
checked={isSelected} data-ref={id}
onChange={this.handleChange} />
</div>
);
}
});
module.exports = ContactSelector;
The contactChannel is a channel I've setup using postal.js, https://github.com/postaljs/postal.js
contactChannel.subscribe("selectedContact",function (data, envelope) {
page.setPersonIsSelectedState(data.id, true);
basketChannel.publish({
channel: "basket",
topic: "addPersonToBasket",
data: {
personId: data.id
}
});
});
I suscribe to the publish in componentDidMount on my parent page:
componentDidMount: function() {
var page = this;
this.loadContacts();
page.subscribeEvents();
},
Listeners:
subscribeEvents: function() {
var page = this;
page.subscribeToChannel(filterChannel, "searchFilterChange", this.listenerForSearchFilterChanged);
contactChannel.subscribe("pageSizeChanged", this.listenerForSizeChanged);
page.subscribeToChannel(filterChannel, "genderFilterChange", this.listnerForGenderFilterChange);
page.subscribeToChannel(filterChannel, "rollModeFilterChange", this.listnerForRollModeFilterChange);
page.subscribeToChannel(filterChannel, "attendanceModeFilterChange", this.listnerForAttendanceModeFilterChange)
page.subscribeToChannel(filterChannel, "messageToFilterChange", this.listnerForMessageToFilterChange);
contactChannel.subscribe("selectAll", function (data) {
page.loadContacts();
});
contactChannel.subscribe("selectedContact",function (data, envelope) {
page.setPersonIsSelectedState(data.id, true);
basketChannel.publish({
channel: "basket",
topic: "addPersonToBasket",
data: {
personId: data.id
}
});
});
contactChannel.subscribe("selectAll", function (data, envelope) {
basketChannel.publish({
channel: "basket",
topic: "selectAll",
data: {
selectAll: data.selectAll
}
});
});
contactChannel.subscribe("refreshContacts", function (data, envelope) {
page.loadContacts();
});
},
Add a ref attribute to your root div, and check that ref value before calling setState . This will make sure the component is mounted.
render: function() {
var id = this.props.data.id;
var isSelected = this.props.data.IsSelected;
return (
<div ref='some_ref' className="contact-selector">
<input type="checkbox"
checked={isSelected} data-ref={id}
onChange={this.handleChange} />
</div>
);
}
then call setState like below
this.refs.some_ref ? this.setState({yourState:value}): null;
You're going about using react incorrectly. React is built to be componentized, so you'll want to be doing everything in components.
When you're setting up your app, you'll want to use postal's subscribe inside of each of your components' getInitialState. Then, unsubscribe from the postal channels in the componentWillUnmount functions.
It seems that the offending code is missing from the snippets in your question, if you post all your code on I could look at it and tell you specifically where you are still "subscribed" to a postal event on a component that is no longer mounted.