I am developing a web application on Perl Catalyst and using ReactJS for the view, jQuery for AJAX, and JSX added as a script in the header.
I am able to fetch AJAX JSON data on a page and refresh the data every 10 seconds. We found this option we create on load on server to fetch data on every 10 seconds. This application is going to be used by a number of users together, so we autogenerate a key which will be incremented if any data is updated on that database table. We set this key on the rest, and it can be accessed by AJAX JSON.
I want to implement a React component which will check this autogenerated AJAX JSON key and will compare it to its previous value every 10 seconds. If they are not equal then it will call the other AJAX function or React component which will update the data on the view page, and older values will be replaced by new.
I have searched a lot but don't get the logic to implement this in ReactJS. Any logic or reference link will be helpful.
Think about the "Refresh_token" component as your controller. It will handle all of the checking token and get the new order when token has changed. The "Order list" should not know when a token has changed, its job is to re-render its view when a new list has arrived.
//Section 1 Starts Here
var Orderlist = React.createClass({
render: function(){
var orderlist11 = [];
for(var i = 0; i < this.props.list.length; i++){
var orderItem = this.props.list[i];
orderlist11.push(
<div className="card">
<div className="title">
Order Number {orderItem.ordernumber}
</div>
<div className="content">
Date & Time : {orderItem.bizorderdate} <br />
Username : {orderItem.userid}
</div>
</div>
);
}
return (
<div> {orderlist11} </div>
);
}
});
////// Section 1 ends here
var RefreshToken = React.createClass({
getInitialState : function(){
return {
token : -1 , //Or anything that is not a token.
orderlist : []
}
},
refreshTimer : null,
componentDidMount : function(){
this.get_order_list(); //Get the intial order list
this.refreshTimer = setInterval(this.refresh_token, 10000); //Call to check the new token every 10s
},
refresh_token : function(){
$.ajax({
type: 'GET',
url: "/order/refresh",
headers: {
Accept : "application/json","Content-Type": "application/json"
},
success: function (resp){
var newToken = resp;
console.log(newToken); //it give the value of refresh eg. ["20150925313"] //Assume this will give the new value of the order list changed.
if(newToken != this.state.token){
this.setState({ token: newToken});
this.get_order_list() //Get the new list when token has changed.
// console.log(this.state.resp);
}
}.bind(this)
});
},
get_order_list : function(){
$.ajax({
type: 'GET',
url: "/order/list/10/1",
headers: {
Accept : "application/json", "Content-Type": "application/json"
},
success: function(orderlist) {
// console.log(orderlist);
// console.log(this.state.orderlist);
this.setState({orderlist: orderlist});
}.bind(this),
error: function(xhr, status, err) {
}
});
},
render: function(){
return <Orderlist list={this.state.orderlist} />
}
});
React.render(
<RefreshToken />,
document.getElementById('list_of_orders')
);
Related
I am new to Vue and Axios and trying to use it in Salesforce Marketing Cloud - Cloud pages. Basically there are 3 parts,
HTML + vue page : this is a form page, where the user is asked to input the automation name and click on send button
App.js : this is build using axios and Vue.
Form-hander.js (backend) : SSJS code that runs the automation.
I referred this document to build this setup -https://ampscript.xyz/how-tos/how-to-start-status-of-automation-from-marketingcloud-form/. I understand the Form-hander.js (ssjs) code and this can be skipped.
What I am not able to understand is the flow of App.js, could anyone please explain me what is happening here.
I understand that on click of send button, the function in App.js - validateForm is called. Here after I don’t understand the flow of the code.
From App.js is the form-handler code called ? OR the post method used in the HTML page is directly called the form-handler page and staring the automation?
Here is the code of app.js. Can some explain to me in simple terms the flow of this code, would be really helpful.
new Vue({
el: '#app',
data: {
status: 100,
form: {
name: 'My Test Automation',
context: 'perform'
},
endpoint: '',
message: ''
},
watch: {
status: function () {
if(this.status == 201 || this.status == 102) {
this.form.context = 'check';
} else {
this.form.context = 'perform';
}
}
},
mounted: function() {
this.endpoint = this.$refs.form.getAttribute('action');
},
methods: {
sendFormData: function() {
this.status = 101;
var $this = this;
axios({
method: 'POST',
url: $this.endpoint,
data: $this.form,
validateStatus: function() { return true }
}).then(function(result) {
$this.status = result.data.Status;
$this.message = result.data.Message;
$this.checkStatus();
}).catch(function(error) {
console.error(error);
});
},
checkStatus: function() {
var $this = this;
var intervalID = setInterval(function() {
axios({
method: 'POST',
url: $this.endpoint,
data: $this.form,
validateStatus: function() { return true }
}).then(function(result) {
$this.status = result.data.Status;
$this.message = result.data.Message;
if($this.status == 200 || $this.status == 500) {
clearInterval(intervalID);
}
}).catch(function(error) {
console.error(error);
});
}, 10000);
},
validateForm: function() {
if (this.$refs.form.checkValidity() !== false) {
this.sendFormData();
}
this.$refs.form.classList.add('was-validated');
}
}
})
Let me explain you the flow of the code you posted :
Once component mounted, The first method which is getting called is mounted(). In this method you are fetching the endopint binded to the action attribute in your form html element and binding that in a data variable via this.endpoint.
Now, you are calling validateForm() method on click of submit button to validate the input fields. If validation pass, you are calling sendFormData() method to make an POST API call.
After getting the response, you added a watcher on status to update the form.context value based on the status code you received from an API response.
At the end, you are calling a checkStatus() method on success of axios call and in this checkStatus() method you are again making an POST API call after every 10 seconds and following step 3.
When the components is mounted, you run the form binded action on (submit?)
The action is probably binded to the sendFormData function(in methods)
Inside sendFormData, there is the setup of the axios request, followed be a then callback which handles the response from the request
The checkStatus function is called inside the "then" block
Sends the same data back to the server every 10 seconds if the previous response
doesn't have status code other than 200 or 500.
ValidateForm is may binded to some onInput or onChange event on the template
** The watcher is always looking for the status code and updates a form context
I am trying to set a dialog to busy using setBusy() but its not working in my controller. I use setBusy() in chrome develper tools console and it works fine. I also get an error when I try clicking the dialog twice. This is the error message:
XMLTemplateProcessor-dbg.js:98 Uncaught Error: Error: adding element with duplicate id 'inactivedialog'
at onDuplicate (Element-dbg.js:169)
at f.register (ManagedObjectRegistry-dbg.js:44)
at ManagedObject-dbg.js:528
at f.constructor (ManagedObject-dbg.js:558)
at f.constructor (Element-dbg.js:151)
at f.constructor (Control-dbg.js:172)
at new f (Metadata-dbg.js:463)
at a1 (XMLTemplateProcessor-dbg.js:1063)
at XMLTemplateProcessor-dbg.js:1070
at SyncPromise-dbg.js:308
Here is my code in controller.
var oView = this.getView();
Fragment.load({
name: "ariba.so.kaakbatransfer.view.InactiveEmployee",
controller: this
}).then(function (oDialog) {
oView.addDependent(oDialog);
oDialog.open();
}.bind(this));
var inactiveDialog = sap.ui.getCore().byId("inactivedialog");
inactiveDialog.setBusy(true);
$.ajax({
url: "private",
type: "GET",
contentType: "application/json",
success: function (data) {
this.setModel(new JSONModel(data), "inactiveemployee");
}.bind(this),
error: function (e) {
var bCompact =
!!this.getView().$().closest(".sapUiSizeCompact").length;
MessageBox.error(
"Data error. Please correct and try again. Refresh the page, if needed.", {
styleClass: bCompact ? "sapUiSizeCompact" : ""}
);
}
});
inactiveDialog.setBusy(false);
Here is my code for the fragment.
<core:FragmentDefinition id="inactivefragment" xmlns="sap.m" xmlns:core="sap.ui.core">
<SelectDialog id = "inactivedialog" noDataText="No Employees Found" title="Select Employee" search="handleSearch" confirm="InactiveEmployeeClose"
cancel="InactiveEmployeeClose" showClearButton="false"
items="{path :'private', sorter:{ path : 'name', descending : false }}">
<StandardListItem title="{private}" info="{private}" type="Active"/>
</SelectDialog>
</core:FragmentDefinition>
The inactivedialog should be busy while the ajax call runs. I should be able to do this in the controller.
For the error with the duplicat ID:
When you first click the button it adds the Fragement (with its ID which should be unique) to the view. When you click the button again it tries to do it again, but the ID already exists. To prevent this check if your Fragment does already exist:
onBtnPress: function (oEvent) {
var oEventSource = oEvent.getSource();
if (!this._oPopover) {
// Fragment does not yet exist, load and then open it
Fragment.load({
name: "com.namespace.view.CancelPopover",
controller: this
}).then(function (oPopover) {
// Persist reference to the fragment
this._oPopover = oPopover;
this.getView().addDependent(this._oPopover);
this._oPopover.openBy(oEventSource);
}.bind(this));
} else {
// Fragment does already exist, open it
this._oPopover.openBy(oEventSource);
}
}
For busy not working:
You are using
var inactiveDialog = sap.ui.getCore().byId("inactivedialog");
wich is not working, look here.
Looking for a way for React to process some json, and load views based on the response.
For example:
1- React has a form, response goes out to external API
2- API processes the input, returns a success code unless there was validation issues, and send a response back to the React app
3- React gets the json response, loads a "Success" view, or reloads the form and outputs the erros
Is there a simple way for React to handle this? Thanks in advance!
Very simple...
Basically, you need to track when you initiate request (sending data) and when request is completed (receiving response).
Based on data returned, you decide what to render...
Take a look at this example (working fiddle)
// In this example, we are using JSONPlaceholer service do real
// request and receive response;
const root = 'http://jsonplaceholder.typicode.com';
const Success = () => (<div>Success!</div>);
const Error = () => (<div>Error! Fix your data!</div>);
const Form = React.createClass({
getInitialState() {
return {
processing: false,
result: undefined,
};
},
submit(event) {
this.setState({ processing: true });
event.preventDefault();
fetch(`${root}/posts`, {
method: 'POST',
data: {
// your data here...
}
})
.then(response => {
// We simulate succesful/failed response here.
// In real world you would do something like this..
// const result = response.ok ? 'success' : 'error';
const processing = false;
const result = Math.random() > 0.5 ? 'success' : 'error';
this.setState({ result, processing });
});
},
render() {
const { result, processing } = this.state;
if (result === 'success')
return <Success />;
return (
<form>
Form content here<br/>
<button onClick={this.submit}>
{ processing ? 'Sending data...' : 'Submit' }
</button>
{ result === 'error' && <Error /> }
</form>
);
},
});
render(<Form />, document.getElementById('root'));
The easy way would be to trigger the new state with setState() from the API callback function such as in the example below, although I recommend using a library such as Redux for state management.
var MainComp = React.createClass({
getInitialState: function() {
return {someProp: ""}
},
callAPI: function() {
var root = 'http://jsonplaceholder.typicode.com';
$.ajax({
url: root + '/posts/1',
method: 'GET'
}).then(function(data) {
this.setState({someProp: data.body})
}.bind(this));
},
render: function(){
return (
<div>
<h2>{this.state.someProp}</h2>
<button onClick={this.callAPI}>Async</button>
</div>
)
}
});
React.render(<MainComp/>, document.getElementById("app"));
Please note this is a naive example, you should still cover up error cases and build a logic to trigger different views based on state.
I am trying to build a simple blog with React, Express, MongoDB and Node. But I am still confused on (1) how to correctly make the ajax request to my database and how do I set state and (2) how to properly update the state.
I've tried setting getInitialState by making an AJAX request, but it won't work. Also I don't know if that is best practice. Also, once someone adds a new post, where am I supposed to place the POST and then how do I properly update state?
var React = require('react');
var List = React.createClass({
render: function() {
return (
<div>
<h2>{this.props.postbody}</h2>
</div>
)
}
})
// I'll break this up into smaller components later, but for now I just want
// to know where to put my database entries into the posts array.
// The only field in MongoDB right now is postbody.
var Home = React.createClass({
getInitialState: function() {
return {
posts: []
}
},
handleClick: function() {
$.ajax({
type: 'GET',
url: '/api/blogPosts',
success: function(data) {
this.setState = data;
console.log(this.setState);
}
})
},
render: function() {
return (
<div>
{this.state.posts.map(function(post) {
return (
<List postbody={post.postbody}></List>
)
})}
</div>
)
}
})
setState is a function, not a property to be set on this. You should do this.setState(data)
I've set things up so the HomePage component renders UserShow for the current logged in user. For example, if a user with an ID of 2 is logged in and visits the HomePage page, it will render their UserShow.
The "normal" UserShow works correctly. For example if you type in /users/18, it will properly render. However it's not working when HomePage renders it.
I'm new to React (especially its lifecycle methods), so my debugging has been to throw alerts in at various steps. I'd say the most important findings to share are:
currentUserID( ) is functioning and returns the correct ID
Hard-coding the value of state.userID within componentDidMount causes things to work correctly
These two points lead me to believe that Render is being called before it can update state.userID with its (correct) return value. Even more specific is that it's rendering before the .success portion of the this.currentUserID() ajax call returns. If this is so, what's the best way to go about not doing an initial render until an ajax call like this completes?
My code is in a state of spaghetti - it's my first time doing front-end routing with JavaScript. I'm also managing sessions via using the user's email as the token in localStorage - I'm new to sessions in JS as well. Please bear with me.
HomePage component:
var HomePage = React.createClass({
getInitialState: function(){
return{
didFetchData: false,
userID: null,
}
},
componentWillMount: function(){
newState = this.currentUserID()
this.setState({userID: newState})
// this.setState({userID: 2}) //hard-coding the value works
},
currentUserID: function(){
if(App.checkLoggedIn()){
var email = this.currentUserEmail()
this.fetchUserID(email)
}else{
alert('theres not a logged in user')
}
},
currentUserEmail: function(){
return localStorage.getItem('email')
},
fetchUserID: function(email){ //queries a Rails DB using the user's email to return their ID
$.ajax({
type: "GET",
url: "/users/email",
data: {email: email},
dataType: 'json',
success: function(data){
this.setState({didFetchData: 'true', userID: data.user_id})
}.bind(this),
error: function(data){
alert('error! couldnt fetch user id')
}
})
},
render: function(){
userID = this.state.userID
return(
<div>
<UserShow params={{id: userID}} />
</div>
)
}
})
UserShow component:
var UserShow = React.createClass({
getInitialState: function(){
return{
didFetchData: false,
userName: [],
userItems: [],
headerImage: "../users.png"
}
},
componentDidMount: function(){
this.fetchData()
},
fetchData: function(){
var params = this.props.params.id
$.ajax({
type: "GET",
url: "/users/" + params,
data: "data",
dataType: 'json',
success: function(data){
this.setState({didFetchData: 'true', userName: data.user_name, userItems: data.items, headerImage: data.photo_url})
}.bind(this),
error: function(data){
alert('error! couldnt load user into user show')
}
})
},
render: function(){
var userItem = this.state.userItems.map(function(item){
return <UserItemCard name={item.name} key={item.id} id={item.id} description={item.description} photo_url={item.photo_url} />
})
return(
<div>
<Header img_src={this.state.headerImage} />
<section className="body-wrapper">
{userItem}
</section>
</div>
)
}
})
So what you want to do is to avoid rendering anything until your ajax-request returns your result.
You can do a check in the render method if the state is how you want it. If it's not, then return null, or a loader or some other markup. When the componentDidMount then sets the state, it will trigger a re-render, since the userID then is set, it will return the userShow component
Example:
render(){
if(this.state.userID === null){
return null; //Or some other replacement component or markup
}
return (
<div>
<UserShow params={{id: userID}} />
</div>
);
}
Fetching the data in the userShow component could be done like this:
componentWillReceiveProps(nextProps){
//fetch data her, you'll find your prop params in nextProps.params
}
You can also avoid doing this here, by kicking the data-fetch in the render-method.
Your initial data set what your doing in componentWillMount will not help to set data because you want to fetch data from ajax. Debug fetchUserID and
currentUserID function whether your getting correct email from localstorage and user id from server. Others is fine.