React - Trying to Changing <img> src attribute - reactjs

I'm trying to change the src attribute of an element after an AJAX call to a database. I have a default image url defined in getDefaultProps(), but after the AJAX call the image doesn't change.
PictureWidget is a child component of a Section component that controls the state (it also passes dataSource and dataSourceParams to PictureWidget). I'm not sure if I can use a local state for PictureWidget so I'm trying to do it through props.
This is my code:
var PictureWidget = React.createClass({
getDefaultProps: function() {
return {
url: 'https://d2o57arp16h0eu.cloudfront.net/echo/img/no_image_available.png'
}
},
componentDidMount: function() {
this.componentDidUpdate();
},
componentDidUpdate: function() {
// Grab img URL from DB
var postData = {
dataSource: this.props.params.dataSource,
dataSourceParams: this.props.dataSourceParams
};
$.ajax({
type: 'POST',
url: ajax_endpoint,
cache: false,
data: postData,
dataType: 'json',
success: function(response) {
if (response.data.length > 0) {
this.updateImage(response.data[0][0]);
}
}.bind(this),
error: function(response) {
this.render();
}
});
},
updateImage: function(url) {
console.log("Updating props.url with " + url);
this.props.url = url;
this.render();
},
render: function(imageURL) {
console.log("Rendering img " + this.props.url);
return React.createElement('div', {className: ' pure-u-' + this.props.columns},
React.createElement('div', {className: 'picture-widget'},
React.createElement('img', {src: this.props.url})
)
)
}
});
And this is my console log (forgive me for the poor formatting, still new to Overflow):
Rendering img https://d2o57arp16h0eu.cloudfront.net/echo/img/no_image_available.png
Updating props.url with http://vignette3.wikia.nocookie.net/animalcrossing/images/4/49/Tumblr_lvrcmvCpsS1qbeyouo1_500.jpg/revision/latest
Rendering img http://vignette3.wikia.nocookie.net/animalcrossing/images/4/49/Tumblr_lvrcmvCpsS1qbeyouo1_500.jpg/revision/latest
The initial render() grabs the default URL, but after the AJAX call this.props.url does get updated to the new value, so I would suspect React.createElement('img', {src: this.props.url}) is the trouble maker. Can I not update the src attribute this way?

This is what state is for. Try using getInitialState instead of getDefaultProps and bind url to this.state using setState().
getInitialState: function() {
return {
url: 'https://d2o57arp16h0eu.cloudfront.net/echo/img/no_image_available.png'
};
},
updateImage: function(url) {
console.log("Updating state.url with " + url);
this.setState({ url: url });
}

Related

React Dependent from API

I am searching a lot about this topic but can’t seem to find a answer that clarifies me.
I am trying to make my first react app. I have started with create react app to understand the basics and then moved my app to server side rendering. As I was developing I got in a question. How can I fetch some data from my API before the app gives a answer (server side) so I can put some stuff in there that I really need?
You could use an ajax request to pull the data from API and set that to the state variable of react. So that the state variable will be used during render. You could define a method for this and, you can call that method from componentDidMount() to set the values to the state variable.
var CommentBox = React.createClass({
getInitialState: function() {
return {data : []};
},
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
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)
});
},
handleCommentSubmit: function(comment){
var comments = this.state.data;
var newComments = comments.concat([comment]);
this.setState({data: newComments});
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: comment,
success: function(data) {
console.log(data);
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
componentDidMount: function() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer(), this.props.pollInterval);
},
render: function() {
return (
<div className='commentBox'>
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
});
In the above code sample, I wrote a method loadCommentsFromServer() specifically to pull data from an API. And I call that from componentDidMount() and stored the output from API into state variable. And, I used the state variable during render.
Note: The above code sample may not work on its own. Since, it's dependencies are not resolved.

Force update on rzModule sliders in AngularJS

I have a few sliders in an angularJS app. What I try to achieve is when the user change the option on a select dropdown, the sliders should get an update with new values from a RestAPI.
This example is for one of the sliders, but the rest are the same.
Creating the slider on controller
myCtrl.ageSlider = {
value: 17,
options: {
showSelectionBar: true,
ceil: 38,
hideLimitLabels: true,
floor: 17,
onChange: myCtrl.getResults,
getPointerColor: function(value) {
return '#FFCF00'
},
getSelectionBarColor: function(value) {
return '#FFCF00'
}
}
};
The update function on controller which is called on ng-change of the select
myCtrl.updateSliders = function () {
//other sliders here that don't need a call on API
StaffServices.getMedic(myCtrl.selected.id,
function(response) {
myCtrl.ageSlider.value = parseInt(response.data.age);
myCtrl.getResults();
},
function(response) {
console.log('Something went wrong on medic process');
});
}
And the getResults() function which call a service
myCtrl.getResults = function() {
myCtrl.results = myService.myUpdatesCalc(passSomeValues);
}
When I manually change the slider from the user interface, the onChange fires the getResults function. Spending hours on this and cannot find the reason. Any help?
Edit: This is the service getMedic to avoid any confusion
service.getMedic = function(id, onSuccess, onError) {
$http.get(API_Base + 'api/staff/medic?id='+id,
{
headers: { 'Authorization': 'Bearer '+$cookies.get('token')}
}).
then(function(response) {
onSuccess(response);
}, function(response) {
onError(response);
});
}
Angular $http return promises, and hence your values are not updated. You will need to handle the promises, and then update it at the callback function:
myCtrl.getResults = function() {
myService.myUpdatesCalc(passSomeValues) //this is your REST API, will return a promise
.then(function(response) { //this is the callback function to handle the successful ajax
myCtrl.results = response.data; //update your results here!
}, function(error) { //this is the callback function to handle the failed ajax
console.log('error')
})
}

net::ERR_INVALID_URL when setting <img> src from $http.get result

I have a angular directive that works on img tags and loads the image dynamically as a base64 string. I use $http.get to load the image and set it into the img.src like this:
var onSuccess = function(response) {
var data = response.data;
element.attr("src", "data:image/png;base64," + data);
};
var onError = function(response) {
console.log("failed to load image");
};
scope.$watch('authSrc', function(newValue) {
if (newValue) {
$http({
method: "GET",
url: scope.authSrc,
data: ""
}).then(onSuccess, onError)
}
});
after i set the src attribute, i get the net::ERR_INVALID_URL error.
The string that returns from the request looks like this:
IHDR����^tiDOT#(##AMȯz�#IDATx��uw\�ٷ�o����G�B["...
Any ideas?
thanks
Got it to work will the help of This link.
Trick was to use responseType: "blob", and use URL/webkitURL createObjectURL()
Here's the full directive:
'use strict';
app.directive('authSrc',function ($http) {
return {
restrict: 'A',
scope: {
authSrc: "="
},
link: function (scope, element) {
var onSuccess = function(response) {
var data = response.data;
var url = window.URL || window.webkitURL;
var src = url.createObjectURL(data);
element.attr("src", src);
};
var onError = function(response) {
console.log("failed to load image");
};
scope.$watch('authSrc', function(newValue) {
if (newValue) {
$http({
method: "GET",
url: scope.authSrc,
responseType: "blob"
}).then(onSuccess, onError)
}
});
}
}
});
Specify the responseType in your request, and you may have to disable default transforms that $http attempts on the data.
see here
My problem was specifying a zoom level after the base64 string '#zoom=90'

ReactJS pass data to child

I have two components BookInfo and AuthorLink. I want to fetch the book info and its author id from server and pass it to AuthorLink to use in componentDidMount to fetch the author name . However, the componentDidMount of AuthorLink run before the componentDidMount of BookInfo so authorid is null when the time it pass to AuthorLink.
How can I fetch the data and the data is ready before I pass it to the child?
var BookInfo = React.createClass({
.
.
.
componentDidMount: function() {
var id = this.props.params.id;
$.ajax({
url: '/api/books/' + id,
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error('#GET Error', status, err.toString());
}.bind(this)
});
},
render: function() {
return (
<AuthorLink authorid={this.state.data.authorid} />
);
}
});
var AuthorLink = React.createClass({
.
.
.
componentDidMount: function() {
$.ajax({
url: '/api/author/' + this.props.authorid,
dataType: 'json',
success: function(data) {
this.setState({authorname:data.authorname});
}.bind(this),
error: function(xhr, status, err) {
console.log('#GET Error', status, err.toString());
}.bind(this)
});
}
},
render: function() {
return (
<a href='#' onClick={this.linkOnClick} >
{this.state.authorname}
</a>
);
}
});
componentDidMount is just ran once per component on the initial render, hence it wont work in this case when we have to wait for the authorID in the BookInfo component.
You would be better off using componentWillReceiveProps in the AuthorLink component to fetch the author data, which is ran before render when, obviously, new props are received in the component. A tip is to also handle the case where the authorname isn't set in the render function, which will most likely be the case on the first render.
More on lifecycle methods her: https://facebook.github.io/react/docs/component-specs.html#updating-componentwillreceiveprops
You would have to move the ajax call into componentWillUpdate or componentWillReceiveProps and fire it when this.props.autherid changes (from null to an actual value)
Or you could delay the rendering of <AuthorLink/> itself by checking in the render if this.state.authorname is set:
render: function() {
var content = <div/>;
if(!!this.state.authorname) {
content = <a href='#' onClick={this.linkOnClick} >
{this.state.authorname}
</a>;
return content;
}

How to fetch a Backbone.js model by something other than the ID?

Backbone.js's default, RESTful approach to fetching a model by the ID is easy and straight-forward. However, I can't seem to find any examples of fetching a model by a different attribute. How can I fetch a Backbone.js model by a different attribute?
var Widget = Backbone.Model.extend({
urlRoot: '/widgets',
fetchByName: function(){ ... }
});
var foowidget = new Widget({name: 'Foo'});
foowidget.fetchByName();
You can try doing something like this on your base model definition or on demand when calling fetch.
model.fetch({ data: $.param({ someParam: 12345}) });
In your case, along the lines of.
var Widget = Backbone.Model.extend({
initialize: function(options) {
this.name = options.name;
},
urlRoot: '/widgets',
fetchByName: function(){
this.fetch({ data: $.param({ name: this.name }) })
}
});
var foowidget = new Widget({name: 'Foo'});
foowidget.fetchByName();
One approach is to override Backbone.sync() method, either for all classes or for just your class. However, presumably your goal is to override fetch for just a single model. One way to do that is to directly call jQuery.ajax(...), and on success, take the response and set that, e.g.
fetchByName: function() {
var self = this;
$.ajax({
url: self.urlRoot+ "?name="+this.get('name'),
type: 'GET',
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(data) {
self.set(data);
}
});
}
If the model is part of a collection you can use where() to pull out the models matching some criteria.
See http://backbonejs.org/#Collection-where
I really like the approach suggested by 'user645715'. I have adjusted the code to be more versatile. If you add this to a Backbone Model it will allow you to search the server by one or more attributes, and should work as a direct drop-in replacement for fetch.
fetchByAttributes: function(attributes, callbacks) {
var queryString = [];
for(var a in attributes){
queryString.push( encodeURIComponent(a)+'='+encodeURIComponent(attributes[a]) );
}
queryString = '?'+queryString.join('&');
var self = this;
$.ajax({
url: this.urlRoot+queryString,
type: 'GET',
dataType: "json",
success: function(data) {
self.set(data);
callbacks.success();
},
error: function(data){
callbacks.error();
}
});
}
It can be used like this:
var page = new Page();
page.fetchByAttributes({slug:slug}, {
success: function(){
console.log('fetched something');
},
error: function(){
console.log('nothing found');
}
});
this is simple model.fetch is same as $.ajax in some way
model = Backbone.Model.extend({
urlRoot: "/root/"
});
var Model = new model();
Model.fetch({
beforeSend: function () {
console.log("before");
},
data: {
param1: "param1",
param2: "param2"
},
success: function () {
console.log("success");
},
error: function () {
console.log("failure");
}
});

Resources