I am trying to use inline styles with React.js, but I keep running into errors:
In my render function, I have:
render: function() {
var style = this.state.submitted ? {{"backgroundColor": "#1abc9c", "opacity": "0.6"}} : {{}};
return (
<div>
<h1 className="home-two-question" style={style}>{text}</h1>
</div>
)
},
Basically I want to toggle this style on click. However, when I run this, I get an error from React.js. What is the correct syntax for inline styles in React.js? Thank you!
In this line:
var style = this.state.submitted ? {{"backgroundColor": "#1abc9c", "opacity": "0.6"}} : {{}};
you're just in plain JavaScript, not inside of a JSX tag. Thus, you just want to use single {}, not double {{}}:
var style = this.state.submitted ? {"backgroundColor": "#1abc9c", "opacity": "0.6"} : {};
In particular, when you do something like:
<div style={{"backgroundColor": "white"}}>
There is one set of {} to denote that the value of the style prop should be interpreted as JavaScript, and another set of {} to denote that you are constructing an object within that value.
Related
I'm very new to the language of react. I seem to continuously get an unexpected token error on the ":". What exactly is the syntax for putting multiple styles inside the Box component shown below? Alongside that, how does one go about putting multiple of these Box components, each one having its margin changed and put inside of an array, displayed on a website.
ncaught SyntaxError: /Inline Babel script: Unexpected token, expected "}" (51:73)
const Box = (props) => <div style={"margin-left" : props.spacing, "width" : props.width, "height" : props.height, "background-color" : props.color}></div>
|
^
The box component takes multiple sub-components such as margin-left (I'm not even sure if I can use this within React) and so on. I then have a for loop that continuously adds box components to an array each with a different margin so that it ends up really displaying a row of different elements inside the div.
class StoryPage extends React.Component {
render(){
const Box = (props) => <div style={"margin-left" : props.spacing; "width" : props.width; "height" : props.height; "background-color" : props.color;}></div>
const val = 0
const boxArray = []
for(let i = 0; i < 10; i++){
val += 100
boxArray.push(<Box spacing = {val} width = "100px" height = "100px" color = "black" />)
}
return(
<div>
{boxArray}
</div>
)
}
}
What I essentially expect to happen is have the array of box elements be displayed. However, I'm not really sure how I'm supposed to go about doing this.
Your “passing an object literal as a prop”-syntax is wrong — explanation below.
React props are passed as follows:
string literal
<Component strProp='hello' />
// or
<Component strProp={'hello'} />
variable
<Component strProp={this.props.foo} />
array literal
<Component arrProp={[1, 2, 3]} />
object literal
<Component objProp={{foo: 'bar'}} />
See the double curly-brackets? One pair is needed to enclose any non-string-literal expression passed as prop, and the other is simply part of the object literal notation.
In contrast, this:
<Component objProp={foo: 'bar'} /> // <- WRONG
would not work because foo: 'bar' is not an expression.
Hugo is right. Also you don't want to add larger margin lefts to each element, margin-left specifies distance from border to element on the left. https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Introduction_to_the_CSS_box_model
You can set the property display to "inline" on all your divs to change the layout of divs from block to inline. Or just not do anything and they'll still all display.
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flow_Layout
Your problem is this line:
const Box = (props) => <div style={"margin-left" : props.spacing; "width" : props.width; "height" : props.height; "background-color" : props.color;}></div>
You have provide wrong style attribute, you need to provide like this:
const Box = (props) => (
<div
style={{
marginLeft: props.spacing,
width: props.width,
height: props.height,
backgroundColor: props.color
}}
/>
);
Notice that style attribute should contain double curly braces and should separate properties with a comma ,
Demo
Note: In React, inline style name should be in camelCase.
For Example,
margin-left should be marginLeft.
background-color should be backgroundColor.
Could you please explain to me in a simple way what is the purpose of using Classnames utility in React code? I've just read Classnames docs, but I still can't grasp what is the main reason to use it in code like so:
import classnames from 'classnames';
[...]
render() {
const { className } = this.props
return (
<div className={classnames('search', className)}>
<div className="search-bar-wrapper">
<form className="search-form" onSubmit={this.onSearch.bind(this)}>
<div className="search-bar">
<label className="screen-reader-only" htmlFor="header-search-form">Search</label> [...]
Full version of this code (jsx):
https://jsfiddle.net/John_Taylor/j8d42m2f/2/
I don't understand what is going on this line of code:
<div className={classnames('search', className)}>
I've also read that (
how to use classnames library for React.js ) answer, but I still have problems with understanding my code snippet.
classnames library lets you join different classes based on different conditions in a simpler way.
Suppose you have 2 classes of which one is going to get used every time but the second one gets used based on some condition. So without classnames library you would something like this
render() {
const classnames = 'firstClass';
if (someCondition) {
classnames += ' secondClass'
}
return(
<input className={classnames} .. />
);
}
But with classnames library you would do that in this way
render() {
const classnames = {'firstClass', {'secondClass': someCondition}}
return(
<input className={classnames} .. />
);
}
In your case, <div className={classnames('search', className)}>is equivalent to <div className={`search ${className}`}>.
classnamesis mainly useful when you have to deal with conditional classes.
Classnames make it easy to apply class names to react component conditionally.
For example: Let create a state and apply a class to the button component when the button is clicked
const [isActive, setActive] = setState(false);
import classnames from "classnames"
var buttonClasses = classnames({
"btn": true,
"btn__active": isActive;
)}
<button className={buttonClasses} onClick={() => setActive(!isActive)}>Make me active</button>
This code will apply the "isActive" class to the button when it is clicked.
I hope this help answer your question
If used the way you use it in your script classnames simply joins the strings you give it with spaces.
const className = 'x';
const result = classnames('search', className);
// result == 'search x'
I think you should attempt reading the docs one more time, they are very clear. Specifically, this part:
So where you may have the following code to generate a className prop for a in React:
var Button = React.createClass({
// ...
render () {
var btnClass = 'btn';
if (this.state.isPressed) btnClass += ' btn-pressed';
else if (this.state.isHovered) btnClass += ' btn-over';
return <button className={btnClass}>{this.props.label}</button>;
}
});
You can express the conditional classes more simply as an object:
var classNames = require('classnames');
var Button = React.createClass({
// ...
render () {
var btnClass = classNames({
btn: true,
'btn-pressed': this.state.isPressed,
'btn-over': !this.state.isPressed && this.state.isHovered
});
return <button className={btnClass}>{this.props.label}</button>;
}
});
The sole purpose of the library here is to remove the need of writing ugly code to dynamically add classes to something, and just use a consistent syntax instead.
Another example:
var Button = React.createClass({
render () {
return <button className={this.props.foo ? 'bar' : 'baz' + this.props.bar ? 'baz' : 'foo'}>{this.props.label}</button>;
}
});
That is really hard to read. There is a lot of code out there that looks similar to that.
Here is a better way, using classnames:
var classes = classNames({
bar: this.props.foo,
baz: !this.props.foo,
active: this.props.bar,
hover: !this.props.bar
});
var Button = React.createClass({
render () {
return <button className={classes}>{this.props.label}</button>;
}
});
It's very clear there what's happening. If the values in the object are true, the key will be appended to the class. So in that example, the final class will be bar active, given this.props.foo is truthy and this.props.bar is truthy.
Your code snippet
<div className={classnames('search', className)}>
is equivalent to:
<div className={`search ${className ? className : ""}`}>
so in this particular case it's just encapsulates ternary operator from inside template string. Which is small but improvement - harder to produce bug in.
Probably it is simplest case of using classnames, however when you'll have more and more conditions around your class name manipulations, it is going to be more and more useful
I have some data that is accessible via:
{{ content['term_goes_here'] }}
... and this evaluated to either true or false. I'd like to add a class depending on the truthiness of the expression like so:
<i class="fa" v-bind:class="[{{content['cravings']}} ? 'fa-checkbox-marked' : 'fa-checkbox-blank-outline']"></i>
where true gives me the class fa-checkbox-marked and false would give me fa-checkbox-blank-outline. The way I wrote it above gives me an error:
- invalid expression: v-bind:class="[{{content['cravings']}} ? 'fa-checkbox-marked' : 'fa-checkbox-blank-outline']"
How should I write it to be able to conditionally determine the class?
Use the object syntax.
v-bind:class="{'fa-checkbox-marked': content['cravings'], 'fa-checkbox-blank-outline': !content['cravings']}"
When the object gets more complicated, extract it into a method.
v-bind:class="getClass()"
methods:{
getClass(){
return {
'fa-checkbox-marked': this.content['cravings'],
'fa-checkbox-blank-outline': !this.content['cravings']}
}
}
Finally, you could make this work for any content property like this.
v-bind:class="getClass('cravings')"
methods:{
getClass(property){
return {
'fa-checkbox-marked': this.content[property],
'fa-checkbox-blank-outline': !this.content[property]
}
}
}
<i class="fa" v-bind:class="cravings"></i>
and add in computed :
computed: {
cravings: function() {
return this.content['cravings'] ? 'fa-checkbox-marked' : 'fa-checkbox-blank-outline';
}
}
Why not pass an object to v-bind:class to dynamically toggle the class:
<div v-bind:class="{ disabled: order.cancelled_at }"></div>
This is what is recommended by the Vue docs.
the problem is blade, try this
<i class="fa" v-bind:class="['{{content['cravings']}}' ? 'fa-checkbox-marked' : 'fa-checkbox-blank-outline']"></i>
You could use string template like with backticks `` :
:class="`${content['cravings'] ? 'fa-checkbox-marked' : 'fa-checkbox-blank-outline'}`"
if you want to apply separate css classes for same element with conditions in Vue.js
you can use the below given method.it worked in my scenario.
html
<div class="Main" v-bind:class="{ Sub: page}" >
in here, Main and Sub are two different class names for same div element.
v-bind:class directive is used to bind the sub class in here.
page is the property we use to update the classes when it's value changed.
js
data:{
page : true;
}
here we can apply a condition if we needed.
so, if the page property becomes true element will go with Main and Sub claases css styles. but if false only Main class css styles will be applied.
I am using Titanium Alloy version 3.2. I have a collection of posts in a listview. My data looks like this:
[
{ username: 'dude', imageUrl: 'url', tags: ['tag1','tag2','tag3'] },
{ username: 'wheres', imageUrl: 'url', tags: ['tag1'] },
{ username: 'my', imageUrl: 'url', tags: ['tag1','tag2','tag3','tag4'] },
{ username: 'car', imageUrl: 'url', tags: ['tag1','tag2'] }
]
And here is the xml. This works only for username and image. I can't figure out how to add the tags to each post.
<ListView id="streamListview">
<Templates>
<ItemTemplate name="template" id="template">
<View class="item-container">
<ImageView bindId="pic" class="pic"/>
<Label bindId="username" class="username"/>
</View>
</ItemTemplate>
</Templates>
<ListSection id="section">
<ListItem template="template" class="list-item"/>
</ListSection>
</ListView>
And my controller code (without the tags)
var posts = [];
for (var i=0; i<data.length; i++){
var post = {
template : "template",
pic : { image : data[i].get("imageUrl") },
username : { text : data[i].get("username") }
};
posts.push(post);
}
$.section.setItems(posts);
How can I add tags (that are clickable) to the post if I am supposed to declare EVERY view in the template before hand? Each tags array in my example would need a different number of views depending on the array length. Each tag would ideally be its own UI.Label element. I believe this can be done using a TableView, but I would prefer using ListView for performance reasons.
I think I know what you need, in this case since you want to generate each item dynamically (for example, a scenario where you open your window with your ListView empty first and make an API call to get remote data and fill the ListView with said data) you will need to use ItemTemplates declared in their own controllers.
You just create a new controller like normal and in the view xml you put your ItemTemplate:
<Alloy>
<ItemTemplate name="template" id="template">
<View class="item-container">
<ImageView bindId="pic" class="pic"/>
<Label bindId="username" class="username"/>
</View>
</ItemTemplate>
</Alloy>
In your tss you put all of the styles referred to each element in your template, since you didn't provide a tss example I can't tell what are your style properties, but in the tss you need to define the style of the template, for example lets say something like:
"#template": // this is the id of your template in your xml
{
width : Ti.UI.FILL,
height : '44dp',
backgroundColor : '#FFFFFF'
}
To fill your ListView with ListItems dynamically, you will need to do something like this in your callback from your API:
function displayListItems(items)
{
var itemCollection = [];
for(var i=0; i < items.length; i++)
{
var tmp = {
pic : {
image : items[i].image
},
username : {
text : items[i].text
},
template : 'template' // here goes the name of the template in your xml, **do not confuse name with id, both are different and using one doesn't replace the other**
};
itemCollection.push(tmp);
}
$.ListView.sections[0].items = itemCollection;
}
And voila, you get your ListView filled dynamically. Now there are some extra steps you can do.
In your template controller you can leave it blank since the ListView can manage the itemclick event, but if you want different actions to take place when a certain element in the Listitem to trigger, you need to specify the functions to be called in your controller for each element.
For example lets say you passed a property called dataInfo to your ImageView and your Label in your template like this:
function displayListItems(items)
{
var itemCollection = [];
for(var i=0; i < items.length; i++)
{
var tmp = {
pic : {
image : items[i].image
dataInfo : items[i].fooA //lets pass to the ImageView the object fooA
},
username : {
text : items[i].text,
dataInfo : items[i].fooB //lets pass to the Label the object fooB
},
template : 'template' // here goes the name of the template in your xml, **do not confuse name with id, both are different and using one doesn't replace the other**
};
itemCollection.push(tmp);
}
$.ListView.sections[0].items = itemCollection;
}
And you want the ImageView and the Label to call different functions, you will need to change your xml like this:
<Alloy>
<ItemTemplate name="template" id="template">
<View class="item-container">
<ImageView bindId="pic" class="pic" onClick="imageFunction"/> <!-- added onClick event -->
<Label bindId="username" class="username" onClick="labelFunction"/> <!-- added onClick event -->
</View>
</ItemTemplate>
</Alloy>
In your controller you will declare each function:
function imageFunction(e)
{
var dataInfo;
if(Ti.Platform.osname === 'android')
{
var item = e.section.items[e.itemIndex];
var bindObject = item[e.bindId];
dataInfo = bindObject.fooA;
}
else
{
dataInfo = e.source.fooA;
}
}
function labelFunction(e)
{
var dataInfo;
if(Ti.Platform.osname === 'android')
{
var item = e.section.items[e.itemIndex];
var bindObject = item[e.bindId];
dataInfo = bindObject.fooB;
}
else
{
dataInfo = e.source.fooB;
}
}
Now you might ask, why do check for the operative system name, well that is because Android and iOS receive different e objects even if you use the same function. In iOS whatever property you pass to the source of the event can be accessed directly with e.source.propertyName while in Android you need to access to the item in e.section using e.itemIndex, after that you retrieve the view inside the item with the e.bindId associated to it.
One of the biggest restrictions on ListItems is updating the views inside a ListItem, to do this you need to update the whole item you want to change visually and assign it a different template, but the speed at which this is done you won't be able to notice any lag, seriously ListView's performance is something else, unlike ScrollView and let's not talk about the horrible and buggy TableView.
A warning, as of Titanium SDK 3.2.0.GA there's a bug in ItemTemplates that causes for views inside child views in the template to change their zIndex in Android with no way to control it, there are two known instances for this:
If you use a don't set the layout in a child view: this could cause for a view that should be displayed beneath another view to come on top of it.
If you use a vertical layout in a child view: this could cause for the positions of each view to be scrambled, this is because zIndex alters the order of display in a vertical layout.
This bug is triggered randomly and the Appcelerator team hasn't put much work on it, check the JIRA ticket here TIMOB-16704.
This can be avoided if you use a template with fixed positioned views and making sure no view comes on top of another, also remember no vertical layouts, haven't tested this with horizontal but personally I try to avoid horizontal layouts since there are other bugs related to it when used in scrollviews, normal views, etc.
EDIT
Another thing you might want to do with this is to assign a different look to the items you render, you have to options:
To apply the styles when you declare the ListItem.
To apply a different layout to each ListItem depending on a series of conditions.
For the first option you need to omit or overwrite the declaration of certain properties in your template:
For example, let's use a different background color where the property fooA exists and another color if it doesn't:
function displayListItems(items)
{
var itemCollection = [];
for(var i=0; i < items.length; i++)
{
var properties = {};
if(typeof items[i].fooA !== 'undefined')
{
properties = {
backgroundColor : 'red'
};
}
else
{
properties = {
backgroundColor : 'blue'
};
}
var tmp = {
properties : properties, // properties for the template
pic : {
image : items[i].image
dataInfo : items[i].fooA //lets pass to the ImageView the object fooA
},
username : {
text : items[i].text,
dataInfo : items[i].fooB //lets pass to the Label the object fooB
},
template : 'template' // here goes the name of the template in your xml, **do not confuse name with id, both are different and using one doesn't replace the other**
};
itemCollection.push(tmp);
}
$.ListView.sections[0].items = itemCollection;
}
You can change width, height, backgroundColor, layout, etc. according to your needs.
Now if you want each item to have a distinct look (meaning different views to display different content) and perhaps a different behavior, you'll need to use different templates.
This might sound bothersome but it is not, templates are fast to create once you get used to them which doesn't take long, another downer might be that if you want 11 different looks, that might mean you'll need 11 templates but that's a extreme case and you might want to rethink your UI if you're dealing with that many templates.
Although restrictive, item templates offer a wide array of options for you to use, a little of imagination is the only ingredient necessary to bring out all of the possibilities.
EDIT 2
I finally understood what was you problem, if you need to create a template whose content changes according to a x variable, then you should try declaring the template on your ListView controller, but this should be done before opening the window were you will be showing the ListView since the templates property can only be set on creation, you should add something like:
function createTemplate(items)
{
var template = {};
for(var i=0; i < items.length; i++)
{
template.childTemplates = [];
for(var j=0; items[i].tags.length; j++)
{
var childTemplate = {
type: 'Ti.UI.Label',
bindId: 'tag' + j,
properties : {
width : Ti.UI.SIZE, // Here you define how your style
height : Ti.UI.SIZE,
font : {
fontSize : '18dp'
},
text : items[i].tags[j].text // you can pass the text here or if you want to on the later for
}
};
template.childTemplates.push(childTemplate);
}
}
// After this you should end up with a template with as many childTemplates as tags each item have, so send the template to the controller with your ListView
Alloy.createController('ListViewWindow', {customTemplate: template});
}
And in your ListView controller you retrieve the template:
var args = arguments[0] || {};
var template = args.customTemplate;
$.ListView.templates = {'customTemplate' : template}; // before any window open call, do this
This should add the template to your ListView, you can also create the ListView in your controller instead of declaring it in your Alloy xml, use the one that fits your needs more.
This should be possible with a ListView if you create the template in the controller dynamically. You would also need to iterate through each "tags" object and generate a Ti.UI.Label "type" for each tag item. However, I'm not certain this method will be more efficient than using a TableView object because essentially every ListItem you create will contain a different template.
To generate a dynamic template it would be similar to this below: Keep in mind you will need to iterate over "tags" and generate x Ti.UI.Label types where x is the length of "tags". Also, the click event should work using Titanium SDK 3.2.1.
var plainTemplate = {
childTemplates: [
{
type: 'Ti.UI.Label',
bindId: 'username'
},
{
type: 'Ti.UI.ImageView',
bindId: 'pic'
},
{
type: 'Ti.UI.Label',
bindId: 'tags',
events: { click : handleTagClickEvent } // Binds a callback to click event
}
]};
function handleTagClickEvent(e) {
Ti.API.info('You clicked a tag label: ' + e.type);
}
var listView = Ti.UI.createListView({
templates: { 'plain': plainTemplate },
defaultItemTemplate: 'plain'
});
Hope this helps you in some way!
i am trying to store css values for transformations of multiple divs in a dictionary
to access them when the div is clicked (the div id being the key of the dict).
my dictionary:
css_dict = {
'keyA' : ['10px, 15px', '20px', '10px'],
'keyB' : ['20px, 30px', '10px', '20px'],
'keyA' : ['30px, 55px', '25px', '30px'],
}
i need to access it like this (variable instead of the key):
css_dict.var[0]
jsFiddle of what im trying to do:
http://jsfiddle.net/tKFka/25/
i cant figure out or find the right syntax for this to work...
Use [] instead of dot.
var var1 = 'keyA';
css_dict[var1][0];