I'm new to ReactJS and I fell I'm missing some fundamental information.
I am working on simple TODO list, where you click on <li> and it gets transfered to Finished section.
http://jsbin.com/gadavifayo/1/edit?html,js,output
I have 2 arrays that contain list of tasks, when you click on one task <li> it is removed from array and transferred to other array. After that clicked <ul> is updated but not the one where task went.
When using it you may notice that <ul> is updated only when clicked.
How can I update both <ul> when clicking on only one?
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Testing</title>
<link rel="stylesheet" href="bootstrap.css">
</head>
<body>
<div id="react-app"></div>
<script src="https://fb.me/react-0.14.3.js"></script>
<script src="https://fb.me/react-dom-0.14.3.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
<script type="text/babel">
/*
* Components
*/
var pendingItems = [
'Clean the room',
'Get milf',
'Sellout and stuff'
];
var finishedItems = [
'Clean the room',
];
var TodoList = React.createClass({
getInitialState: function() {
return { items: this.props.list };
},
handleClick: function(i) {
console.log('You clicked: ' + i + ':' + this.props.listString);
if (this.props.listString == "pendingItems") {
var removed = this.state.items.splice(i, 1);
finishedItems.push(removed);
};
if (this.props.listString == "finishedItems") {
var removed = this.state.items.splice(i, 1);
pendingItems.push(removed);
};
this.forceUpdate()
},
render: function() {
return (
<ul>
{this.state.items.map(function(item, i) {
return (
<li onClick={this.handleClick.bind(this, i)} key={i}>{this.state.items[i]}</li>
);
}, this)}
</ul>
);
},
});
var Layout = React.createClass({
render: function (){
return (
<div className='col-xs-12'>
<div className='col-xs-6'>
<TodoList list={pendingItems} listString="pendingItems"/>
</div>
<div className='col-xs-6'>
<TodoList list={finishedItems} listString="finishedItems"/>
</div>
<div className='col-xs-6'></div>
</div>
)
}
});
ReactDOM.render(<Layout />, document.getElementById('react-app'));
</script>
</body>
</html>
You need to use the states. In getInitialState you put your two list, onclick do whatever transformationyou want (you then have for example updated_list1 and updated_list2 and then you set the list like that:
this.setState({ list1: updated_list1, list2: updated_list2 }); in your case this.setState({ pendingItems: pendingItems ... after the .push
the setState function will automatically rerender, no need to call forceupdate.
The second thing important here is that you have to make the two list communication kinda, so my advise would be to put your both ul in the same component (so you can manage the lists in the same component state as mentionned above).
However this is not the only way to go and you may choose the put the states of your two list in the parent component (Layout). In this case you should use this way to go. https://facebook.github.io/react/tips/expose-component-functions.html
In any case you need (if you want to keep it simple and without external model management like backbone or flux pattern) to put lists states in the same component state. (reminder: method 1 => ul in the same componenet so the states too, method 2 => keep state in the parent component)
Related
I'm trying to update a property in a polymer element with data from an ajax api call. I have something similar working elsewhere in the app where users are able to add packages dynamically.
Anyone know what I'm doing wrong here?
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="address-input.html">
<link rel="import" href="package-list.html">
<link rel="import" href="../bower_components/iron-ajax/iron-ajax.html">
<dom-module id="step-one">
<style>
</style>
<template>
<section id="addresses">
<div class="container">
<div class="row">
<h5>Addresses</h5>
<address-input></address-input>
</div>
</div>
</section>
<section id="packages">
<div class="container">
<div class="row">
<h5>Packages</h5>
<package-list></package-list>
</div>
</div>
</section>
<section id="submit-shipping-info">
<div class="container">
<div class="row">
<a class="waves-effect waves-light btn col s12 m12 l12" id="submit" on-click="submitInfo">Submit</a>
<template is="dom-repeat" items="{{options}}">
<p>{{item.rates}}</p>
</template>
</div>
</div>
</section>
</template>
</dom-module>
<script>
Polymer ({
is: 'step-one',
properties: {
options: {
type: Object,
notify: true,
value: []
}
},
submitInfo: function(e) {
e.preventDefault();
//add dimensions of all packages to the dimensions array
var dimensions=[];
$('#packages .package-card').each(function(){
var weight= $(this).find('.weight').val();
var length= $(this).find('.length').val();
var height= $(this).find('.height').val();
var width= $(this).find('.width').val();
var dimension={width:width,length:length,height:height,weight:weight};
dimensions.push(dimension);
});
//capture address data
var from = $('#fromAddress').val();
var to = $('#toAddress').val();
//URL that processes getting a URL
var getQuoteURL = '../v2/API/get_rates.php';
var stuff = [];
jQuery.ajax({
type: "POST",
dataType: "json",
cache: false,
url: getQuoteURL,
data:{
from:from,
to:to,
dimension:dimensions
}
}).done(function(data){
$.each(data['rates'], function(i, rate ) {
stuff.push({carrier:rate.carrier});
return stuff;
});
//show step two when ajax call completes
$('.step-two').removeClass('hide').addClass('show');
console.log(stuff);//I can see all objects I need to pass to the 'options' property
return stuff;
});
this.push('options',stuff);//doesn't seem to update the 'options' property with these as a value
}
});
</script>
I'm able to console.log the array i'm trying to use, but when I try to push it to the 'options' property, it won't update.
Consider using Polymer built in methods instead of jQuery.
1. A button to submit a request.
<paper-button on-click="handleClick">Send a package</paper-button>
2. AJAX requests using <iron-ajax> element!
<iron-ajax id="SendPkg"
url="my/api/url"
method="POST"
headers='{"Content-Type": "application/json"}'
body={{packageDetails}}
on-response="handleResponse">
</iron-ajax>
3. Handle the on-click event,
On click, select <iron-ajax> by ID and call <iron-ajax>'s generateRequest()
Use either data binding or Polymer's DOM API to get the package's width, height ...etc
handleClick: function() {
this.packageDetails = {"width": this.pkgWidth, "height": this.pkgHeight };
this.$.SendPkg.generateRequest();
},
4. Handle the response
handleResponse: function() {
//Push data to options...
},
return stuff;
});
this.push('options',stuff);//doesn't seem to update the 'options' property with these as a value
should be
return stuff;
this.push('options',stuff);//doesn't seem to update the 'options' property with these as a value
)};
otherwise
this.push('options',stuff);
is executed before data has arrived
The solution ended up being to put this into a variable:
var self = this;
then in the ajax .done() replace the value of the object with the new object from the ajax call.
self.options = stuff;
I guess you have to put "this" into a variable before you can overwrite it's values. Then the other issue was that I was trying to use .push() to add to it, but really all I needed to do was replace it. (Using self.push('options',stuff); didn't seem to work as far as adding to an object)
This is my main page:
<head>
<script type="text/javascript">
// Hide e2 and e3
$(document).on('pagechange', function(){
$("#e2").hide();
$("#e3").hide();
});
// Ajax call to insert e1-e3
$(document).on('pageshow', function(){
$("#course").bind("change", function() {
$.ajax({
type: "GET",
url: "includes/db/ajax.php",
data: "course="+$("#course").val(),
success: function(html) {
$("#insert-es").html(html);
$("#page").trigger('create');
}
});
});
});
</script>
</head>
<body>
<div data-role="page" id="insert-quests">
<div id="insert-es"></div>
</div>
</body>
Ajax.php will generate a list of divs based on a database table, for example...
<div id="e1">...</div>
<div id="e2">...</div>
<div id="e3">...</div>
Prior to adding these divs through jQuery and AJAX, I was able to hide 2 and 3 using this script below. (I need to do this because 2 and 3 are only supposed to be shown when the user selects a certain response in 1.)
$(document).on('pageinit', function(){
$("#2").hide();
$("#3").hide();
});
So I have two questions:
I tried pagechange and some other events but no result. How can I use jQuery mobile on content after I have brought it in using AJAX?
The amount of divs is related to the amount of entries in a table of the database. How can I get the amount of entries, so that I can just use a loop there? For example...
for ( var i = 0; i < amount_of_entries ; i++ ) {
$("#e" + i).hide();
}
So I am making a test app using RequireJs, Mustache and Backbone.js. I had some success with rendering the collection of models with the Mustache template. But my Mustache template has a button and when I try to bind click event on the button in the view, the button click doesn't invoke the callback function. I am really stuck, can someone tell me where I am not doing right?
Here is my code:
ItemView.js:
define(['jquery', 'backbone', 'underscore', 'mustache', '../../atm/model/item'], function ($, Backbone, _, Mustache, Item) {
var ItemView = Backbone.View.extend({
initialize: function() {
},
tagName: 'li',
events: {
'click .button': 'showPriceChange'
},
render: function() {
var template = $('#template-atm').html();
var itemObj = this.model.toJSON();
itemObj['cid'] = this.model.cid;
var rendering = Mustache.to_html(template, itemObj);
this.el = rendering;
return this;
},
showPriceChange: function(event) {
alert('Changing...');
$('#' + elemId).empty();
$('#' + elemId).append(document.createTextNode('Changed'));
},
});
return ItemView;
});
atm.html:
<!DOCTYPE html>
<html>
<head>
<title>Elevator</title>
<script data-main="scripts/main" src="scripts/require-jquery.js"></script>
<style type="text/css">
</style>
</head>
<body>
<h1>Vending Machine</h1>
<div id="atm-items">
</div>
<script id="template-atm" type="html/template">
<li>
<p>Item: {{name}}</p>
<label for="price-{{cid}}">Price:</label>
<input id="price-{{cid}}" type="text" value="{{price}}"/>
<button class="button">Change</button>
<p id="status-{{name}}-{{cid}}">- -</p>
</li>
</script>
</body>
</html>
You're replacing the view's el inside render:
render: function() {
//...
this.el = rendering;
//...
}
When you do that, you're losing the jQuery delegate that is attached to this.el, that delegate handler (which Backbone adds) is responsible for the event routing.
Usually, you add things to this.el rather than replacing this.el. If your template looked like this:
<script id="template-atm" type="html/template">
<p>Item: {{name}}</p>
<label for="price-{{cid}}">Price:</label>
<input id="price-{{cid}}" type="text" value="{{price}}"/>
<button class="button">Change</button>
<p id="status-{{name}}-{{cid}}">- -</p>
</script>
then you would this.$el.append(rendering) in your view's render; this would give you an <li> in this.el since you've set your view's tagName to li.
Alternatively, if you really need to keep the <li> in the template, you could use setElement to replace this.el, this.$el, and take care of the event delegation:
this.setElement(rendering);
Presumably you're wrapping all these <li>s in a <ul>, <ol>, or <menu> somewhere else; if you're not then you're producing invalid HTML and the browser might try to correct it for you, the corrections might cause you trouble elsewhere as your HTML structure might not be what your selectors think it is.
I have external html, where i have create the jcorousal (images are loading through ajax). but that external page is not loading in my current div:
<div class="corousal_content" id="MyDivName"> <!-- External html will load here--> </div>
This is my external page which consist jcarousal:
<script type="text/javascript">
alert("load ajax");
function mycarousel_itemLoadCallback(carousel, state)
{
// Since we get all URLs in one file, we simply add all items
// at once and set the size accordingly.
if (state != 'init')
return;
jQuery.get('dynamic_ajax.txt', function(data) {
mycarousel_itemAddCallback(carousel, carousel.first, carousel.last, data);
});
};
function mycarousel_itemAddCallback(carousel, first, last, data)
{
// Simply add all items at once and set the size accordingly.
var items = data.split('|');
for (i = 0; i < items.length; i++) {
carousel.add(i+1, mycarousel_getItemHTML(items[i]));
}
carousel.size(items.length);
};
/**
* Item html creation helper.
*/
function mycarousel_getItemHTML(url)
{
return '<img src="' + url + '" width="75" height="75" alt="" />';
};
jQuery(document).ready(function() {
jQuery('#mycarousel').jcarousel({
itemLoadCallback: mycarousel_itemLoadCallback
});
});
</script>
</head>
<body>
<div id="wrap">
<div id="mycarousel" class="jcarousel-skin-ie7">
<ul>
<!-- The content will be dynamically loaded in here -->
</ul>
</div>
</div>
please solve my problem.....
How do you load your external html?
Because, I think it failed to fire jQuery.ready event on external html.
I have many elements on page and all of which i want to translate to some language. The language is not the same for all fields, that is, for 1st field it may be fr and for third field it may be en then again for 7th field it may be pa.
Basically i wrote the code and it's working :-
<script type="text/javascript">
//<![CDATA[
google.load("language", "1");
window.onload = function(){
var elemPostTitles = document.getElementsByTagName("h4");
var flag = true;
for(var i = 0 ; i < elemPostTitles.length ; i++){
while(flag == false){
}
var postTitleElem = elemPostTitles[i];
var postContentElem = document.getElementById("postContent_" + i);
var postTitle = postTitleElem.innerHTML;
var postContent = postContentElem.innerHTML;
var languageCode = document.getElementById("languageCode_" + i).value;
google.language.detect(postTitle, function(result) {
if (!result.error && result.language) {
google.language.translate(postTitle, result.language, languageCode,
function(result) {
flag = true;
if (result.translation) {
postTitleElem.innerHTML = result.translation;
}
});
}
});
flag = false;
}
As you can see, what i am trying to do is restrict the loop from traversing until the result of previous ajax call is receieved. If i don't do this only the last field gets translated. My code works nicely, but because of the infinite loop, i keep getting errors from Mozilla to "stop executing scripts". How do i get rid of this? Also, is my approach correct? Or some inbuilt function is available which can ease my task? Thanks in advance :)
Why don't you call the function to check the next h4 recursively from within the detect/translate completed callbacks. Send the next recursion the next h4 using something like JQuery's next() function.
What you're doing is running the endless loop on the same thread as the outer loop.
I suggest you post a more complete question and code next time to prevent people who like to provide working answers from having to spend time guessing what you are trying to do.
Here is a working example using recursion. Unless you have thousands of items, the tail should be tolerable.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript" src="http://www.google.com/jsapi">
</script>
<script type="text/javascript">
google.load("language", "1");
function initialize() {
var elemPostTitles = document.getElementsByTagName("h4");
var index = elemPostTitles.length - 1;
foo(index);
function foo(index) {
var postTitleElem = elemPostTitles[index];
var postTitle = postTitleElem.innerHTML;
var postContentElem = document.getElementById("postContent_" + index);
var postContent = postContentElem.innerHTML;
var languageCode = document.getElementById("languageCode_" + index).value;
google.language.detect(postTitle, function(result) {
if (!result.error && result.language) {
google.language.translate(postTitle, result.language, languageCode,
function(result) {
if (result.translation) {
postTitleElem.innerHTML = result.translation;
}
if (--index > -1) {
foo(index);
}
});
}
});
};
}
google.setOnLoadCallback(initialize);
</script>
</head>
<body>
<h4>
this is some text</h4>
<h4>
this is some text</h4>
<h4>
this is some text</h4>
<h4>
this is some text</h4>
<h4>
this is some text</h4>
<input type="text" id="languageCode_0" value="en" />
<div id="postContent_0">
</div>
<input type="text" id="languageCode_1" value="hi" />
<div id="postContent_1">
</div>
<input type="text" id="languageCode_2" value="es" />
<div id="postContent_2">
</div>
<input type="text" id="languageCode_3" value="fr" />
<div id="postContent_3">
</div>
<input type="text" id="languageCode_4" value="ar" />
<div id="postContent_4">
</div>
</body>
</html>