I'm currently making the change from Angular routing & client-side rendering to React & Flux for isomorphic builds. I've just started learning React so bear with me.
Here's my problem. To render a simple list, it would look like this.
HTML, component & render in React:
<!--html-->
<div id="mount-point"></div>
<script type="text/jsx">
//component
var List = React.createClass({
getInitialState: function(){
return {
items: [
"Apples",
"Broccoli",
"Chicken",
"Duck",
"Eggs",
"Fish",
"Granola",
"Hash Browns"
]
}
},
render: function(){
return (
<ul>
{
this.state.items.map(function(item) {
return <li key={item}>{item}</li>
})
}
</ul>
)
}
});
//render
React.render(<List/>, document.getElementById('mount-point'));
</script>
Or in Angular, with a controller and HTML:
<script>
//controller
app.controller("MyController", ["$scope", function($scope){
$scope.items = [
"Apples",
"Broccoli",
"Chicken",
"Duck",
"Eggs",
"Fish",
"Granola",
"Hash Browns"
]
}]);
</script>
<!--html-->
<ul>
<li ng-repeat="item in items">{{item}}</li>
</ul>
Now to me, the Angular code looks a lot shorter and more readable. The HTML where data is injected is not infiltrated by javascript code. I'm not trying to start a React v Angular debate. Instead, I'm wondering if there is a way to combine the best properties of both? i.e. have the stateful component concept from React but instead of using raw javascript in the JSX, using things like Angular's ng-repeat. If not, is there another way to approach writing the JSX in my example?
IMHO, the React code is better and more readable because it is just javascript. It doesn't require (hardly) any extra knowledge to understand what's going on. You read the angular one, and if you're not familiar at all with angular, you're immediately wondering: what's $scope? what's app? what's controller do? why do i pass it an array and why is the second argument a function? what calls this function? what's an ng-repeat? this list goes on ...
Now, on to your question: No.
The JSX you've written is perfect (well, sort of, I'll add my modifications below). Why is it perfect? It's perfect because nearly any programmer on the planet can figure out what it does: it maps over a list of items.
Now, if I were to rewrite it, I'd do something like this:
var List = React.createClass({
...
renderItem: function(item, key) {
return <li key={key}>{item}</li>;
},
render: function() {
return <ul>{this.state.items.map(this.renderItem)}</ul>;
}
}
Creating a separate renderItem function to render the item will make this a lot more future-proof (and readable IMO). Also, you shouldn't put your key as the item itself, because (in this example) that would prevent two items from having the same value (ie, two "Apple"s)
It seems like maybe what you're really asking is, "directives in angular are cool! why doesn't react have directives? and can/should I add directives to React?"
If I were to address that, I'd say... directives do seem cool in angular. Although, there's clearly some big problems with them, even the angular team abandoned them (custom ones anyway) in angular 2. There is some react library on github floating around right now that adds ng-like directives to JSX, but I highly advise against it. It's really just a bad idea. I'm going to repeat this for double extra super clarity: It's a bad idea. What's wrong with writing plain old javascript and rendering it with JSX handlebars? Do you really need an ng-repeat because writing list.map is too hard? The added complexity and learning curve, however slight, far outweighs the benefits. render() is a beautiful pure function, let's leave it that way.
You can actually interoperate Angular and React using https://github.com/bcherny/ngimport. We've used it successfully on a 10k file hybrid Angular+React project - let me know if I can answer any questions!
Related
I'm working in a project with vue-cli and they want me to generate a listlike view (pun intended) onto data from the database.
I've never really worked with vue. 4 months ago I was given some time to find my way into it, but then I had to work on our lumen-driven backend api and so I forgot most of the stuff. And besides that, I really find vue utterly confusing.
So I need to do this step by step since I dont have loads of time at hand to throughly learn the framework before actually producing usable results.
I have the following template:
<template>
<div>
<h1>myList</h1>
<table id="testTable"></table>
<button class="button" v-on:click='fetchList'>myButton</button>
</div>
</template>
<script>
import store from "../store/store";
import { USER_FETCHLIST } from "../store/actions/user";
export default {
data () {
return {
}
},
methods: {
fetchList: function(){
var test = store.dispatch(USER_FETCHLIST).then((res)=> {
console.log(res)
document.getElementById("testTable").InnerHTML = res
})
}
}
}
</script>
<style>
</style>
The fetchList() successfully fetches the data from the DB. But I cant insert anything into my table element, at least not this "javascript-way" which I tried in the above code.
For example, when I try to input this string: <tr><td>1</td><td>2</td></tr> into the table element, nothing happens.
I learnt about v-bind and all this stuff, but I really don't know how to implement it here.
I also must emphasize that I absolutely want to avoid building this component from subcomponents.
The "inheritance" in vue.js is pretty different from the usual inheritance in programming and I really tried to get my head around it, but I just cant make anything useful with it in my relatively short timeframe.
So I need to have all the JS and html in place in this component.
Can anyone give me a hand in propery binding the prefabricated HTML-String to the table element?
Thank you!
Problem
I'm implementing translations for my Angular 6 application. It needs to support static texts of the app in multiple languages. I don't need dynamic translations during runtime - I just need producing an app with texts in a language chosen during build.
Angular has the whole page on internationalization, but it focuses solely on situations where all the texts are hard coded in the templates, like in the code below:
<p>This is a hard coded text</p>
That's hardly a situation in my template. Additionally it seems to me impossible or extremely dirty to write the whole application with all the texts hard-coded in templates, so if that's the only mechanism available for translation, then it seems completely useless to me.
In my components I very often have texts bound to some JavaScript variables (or to be precise object properties), like in the code below:
<li *ngFor="let navigationEntry of navigationEntries">
<a [href]="navigationEntry.url">
{{ navigationEntry.text }}
</a>
</li>
Let's say the variable is still static, just being stored in the component or a service, e.g.:
navigationEntries: Array = [
{
url: "/",
text: "Home",
},
{
url: "/login",
text: "Login",
},
{
url: "/admin",
text: "Admin panel",
},
];
Question
Is there a way to use Angular native translation (internationalization) mechanisms to translate the anchor texts (or actually the text properties of the navigationEntries members) during build time? The structure of the JavaScript data and the HTML of the template can change.
If Angular native internationalization mechanisms can't handle that (but then I wonder what's their use at all), then what are other solutions that could help with implementing such translations? I found ngx-translate library, but it's providing translations which can be dynamically changed at runtime and ideally I wouldn't like to add that unnecessary overhead of dynamic solution watching on all the translated texts.
Yes, you can use Angular native translation mechanim to do this. We use the following pattern a lot to get around the missing dynamic translations in angular i18n:
You have an array of messages, and then use it with ngFor and ngSwitch to template the messages, something like this for your example:
<li *ngFor="let navigationEntry of navigationEntries">
<ng-container [ngSwitch]="navigationEntry.text">
<a [href]="navigationEntry.url" *ngSwitchCase="'Home'" i18n="##home">
Home
</a>
<a [href]="navigationEntry.url" *ngSwitchCase="'Login'" i18n="##login">
Login
</a>
<a [href]="navigationEntry.url" *ngSwitchCase="'Admin panel'" i18n="##adminPanel">
Admin panel
</a>
<ng-container>
</li>
It's somewhat verboose - but it works for a surprising amount of use cases.
Note, the i18n id (the string following the ##) is just an unique id, which is used in the xlf translation file. The string can be anything, I use the message itself, if possible, for readability and reuse.
I'm using React and created a small page that has 4 components (React classes, what is the preferred term? I'll call them components in this post):
Component Breakdown
a parent "App" component that includes and manages the other components
a "Form" component that lets the user interact with the page
a "String View" component that displays the input from the form as text
a "Visual View" (I know, bad name...) component that interprets the string view and performs actions to adjust the visual.
Dataflow
The communication of these components using states and props is as follows:
The Form has onChange handlers that pass the new state to the App
The App funnels the state data to the String View
The String View updates and passes the updated state to the App
The App funnels the new state data to the Visual View
Finally, the Visual View now updates based on the new state.
Sample Code
var App = React.createClass({
handleFormChange: function(formData) {
this.setState({formData:formData});
},
handleStringChange: function(stringData) {
this.setState({stringData:stringData});
},
render: function() {
return (
<div className="app">
<FormView onFormChange={this.handleFormChange}/>
<StringView formData={this.state.formData} onStringChange={this.handleStringChange}/>
<VisualView stringData={this.state.stringData}/>
</div>
);
}
});
var FormView = React.createClass({
handleFormChange: function(e) {
this.props.onFormChange(e.target.value);
}
render: function() {
return(
<div className="formView">
<select onChange={this.handleFormChange}>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</select>
</div>
);
}
});
var StringView = React.createClass({
componentDidUpdate: function() {
this.props.onStringChange({newString:'newStringState'});
},
render: function() {
this.props.formData;
// process formData and update state
return (
<div className="stringView">
{this.props.formData}
</div>
);
}
});
var VisualView = React.createClass({
render: function() {
var selection = this.props.stringData,
output = '';
if (selection === 1) {
output = 'Hooray, 1!';
} else {
output = 'Yes! 2!';
}
return (
<div className="stringView">
{output}
</div>
);
}
});
Questions
Is this the correct dataflow paradigm that React is trying to enforce (components only talk to parents, not siblings)?
Compared to how I would have written this in just regular JavaScript, this seems terribly constrained. Am I missing the big picture? Is this dataflow paradigm designed to prevent future problems (if so, which ones? Any that can't be solved with disciplined regular JavaScript?), or is there some other purpose that I'm missing?
I'm getting a lot of repeated function names (handleFormChange for example, it's used in App and Form View), is there a good way to make these distinguishable? Or, are repeated function names across components desirable?
When the components actually build, the JSX stuff gets transpiled down into real JavaScript. Is there an advantage to using JSX? Would writing components in the already transpiled JavaScript have an advantage?
To start, I think it is ok to call "components", and I've seen lot of people call that way. I will answer your questions below, in an order that I think is better to make my answers make sense.
When the components actually build, the JSX stuff gets transpiled down into real JavaScript. Is there an advantage to using JSX? Would writing components in the already transpiled JavaScript have an advantage?
JSX kinda mixes JavaScript and HTML, so, it makes your code "friendly". You will create your components, and just "call" them as HTML tags. Below you can see the difference between writing JSX and pure JavaScript.
return <div className="my-component"><p>Awesome</p></div>;
return ReactDOM.div({
className: 'my-component'
}, ReactDOM.p({}, "Awesome"));
I don't know you, but I would get tired to write this amount of code just to render a div with a paragraph.
You can check more benefits of using it here:
https://hchen1202.gitbooks.io/learning-react-js/content/benefits_of_jsx.html
I'm getting a lot of repeat function names (handleFormChange for example, it's used in App and Form View), is there a good way to make these distinguishable? Or, are repeated function names across components desirable?
It is not bad, also, your app is a "demo" one, if it would be a "real" one, it would have some better names for the components (i.e. <FormView> would be <ContactForm>) and maybe your method names would be different. But it is not bad at all. For example, inside <ContactForm> you may call the submit handler as onSubmit, but outside (the prop that you pass), you may call onContactFormSubmit, or, in a more semantic way, onContactFormFilled.
If your application starts to grow and you have lots of things repeated in the same component (that is the case of your <App>), you may try to split your components, therefore, each of your component will "know" about a "domain", and it would not appear to have lots of repeated stuff.
Is this the correct dataflow paradigm that React is trying to enforce (components only talk to parents, not siblings)?
First of all, React doesn't "enforce" anything, as some people say, React is the "v" in MVC, so, you have your "presentation" layer described as components, and the data may flow in the way you want.
But you got a point when you say "components only talk to parents, not siblings", because that is the way you can "communicate" between your components when you have multiple components. Since a component can't see its sibling, you need someone to orchestrate this communication, and, in this case, this is the parent's job.
There are other ways to make components "talk" to each other (i.e. using refs), but having a parent to orchestrate is, IMO, the most reliable (and better testable) one.
Compared to how I would have written this in just regular JavaScript, this seems terribly constrained. Am I missing the big picture? Is this dataflow paradigm designed to prevent future problems (if so, which ones? Any that can't be solved with disciplined regular JavaScript?), or is there some other purpose that I'm missing?
I decided to answer that as the last one, to sum up some things.
IMO, React is just great, you start to have your "logic" in the right place (a component), and you can just compose things in order to make your page work well (and by well I mean it is orchestrated correctly).
React also makes it easier to "think" about how you will build your interfaces. This Pete Hunt's blog post is amazing, and you should check it out:
https://facebook.github.io/react/docs/thinking-in-react.html
If you would be writing your code with plain JavaScript, you would have to handle DOM in some way (i.e. using a template engine) and your code would end up mixing DOM manipulation with your application logic. React just abstracts that for you. You can only care about presenting stuff. Another advantage is that, when everything is a component, you can reuse those components, it doesn't matter where they are located. If you pass the props correctly, your component will work as expected.
I know it seems exhaustive to write those components, but as you start to write more components you start to see lots of benefits. One of them is to nevermore wonder about how to present your data (no more concatenating HTML strings or calling template functions). Another one is that it is easy to "split" your interfaces, what makes your code easier to maintain (and that is not straightforward when using plain JavaScript).
To be honest, this application you wrote is really simple, and you may not see lots of advantages of using React for building it. I think you should try to create a more "complex" one, and compare it with plain JavaScript. By "complex", I mean "user interface" complex. For example, create a form that allows user to submit multiple "people". And "people" should have "name" and multiple "pet" (which also have a name). You will see how hard is it to handle "add" and "remove" operations in this case, and how easy React handle that kind of thing.
I think that is it, I hope you and React "click". It changed my mind about how to create complex user interfaces.
Looking at the Virtual DOM in React.js and by doing a few performance tests, I'm very interested in this library. It seems like the perfect add-on to Backbone's awesome model, router and collection structure.
However, due to the lack of quality tutorials and courses out there, I'm left with a few questions I hope someone here will be able to answer:
HTML templates
Does React completely do away with the notion of HTML templates? I'm talking about having your view markup in a separate HTML file (Or on the same page in a <script type=text/template> tag). You know, like you do with underscore.js Handlebars etc ...
The Starter kit examples all seem to have the JSX or React.DOM functions right inside your view classes, which seems a little messy to me, and I can see this getting a little out of hand, as your views grow in complexity.
Here's an example that renders 2 values and the sum of them, using a basic Twitter Bootstrap panel element with a nested table.
var CustomView = React.createClass({
render: function() {
var x = this.props.x;
var y = this.props.y;
return (
<div className="panel panel-primary">
<div className="panel-heading">
<h1 className="panel-title">Should I put all this markup somewhere else?</h1>
</div>
<div className="panel-body">
<table className="table">
<thead>
<tr>
<th>X</th>
<th>Y</th>
<th>Combined val</th>
</tr>
</thead>
<tbody>
<tr>
<td>{x}</td>
<td>{y}</td>
<td>{x + y}</td>
</tr>
</tbody>
</table>
</div>
</div>
);
}
});
I'm not interested in knowing whether it's possible or not to move this stuff to a separate file, rather, I'm trying to understand what's considered the best practise when working with React.
Updating and setting data
The Why React page states the following:
Simply express how your app should look at any given point in time, and React will automatically manage all UI updates when your underlying data changes.
I'm not fully understanding how this works. For instance, take the React component from before <CustomView x="20" y="10">. Initially I would render it like so:
var x = 20;
var y = 10;
React.renderComponent(
<CustomView x={x} y={y} />,
document.getElementById('view-container')
);
Now, when I want to update CustomView any time x changes, how should I proceed? React is supposed to be an alternative to the data binding you find in Angular and Ember, without doing a 2-way binding, so how do I make this happen? How do I tell CustomView to keep an eye on x and automatically re-render when it changes?
Naturally, just assigning x a new value does nothing.
I know there's the setState method, but I still manually have to call that, right? So if I was working with a React view and a Backbone model, the code could look something like this:
// Data model: Backbone.js
var model = new Backbone.Model({text: "Please help! :)"});
// Create view class
var View = React.CreateClass({
render: function() {
return (
<p>{this.props.text}</p>
);
}
});
// Instantiate new view
var view = React.renderComponent(
<View text={model.get("text")}>,
document.getElementById('view-container')
);
// Update view
model.on("change:text", function(model, newValue) {
view.setState({
text: newValue
});
});
// Change data
model.set("text", "I do not understand this ...");
That seems like a really strange setup, and I'm almost sure this can't be the way you're supposed to do it.
I would love some pointers to help me move in the right direction here.
Thank you in advance for any feedback and help.
Does React completely do away with the notion of HTML templates?
Yes, in favor of declaring your views with JavaScript. It also allows the Virtual DOM structure to work efficiently.
The Starter kit examples all seem to have the JSX or React.DOM functions right inside your view classes, which seems a little messy to me, and I can see this getting a little out of hand, as your views grow in complexity.
You shouldn't allow your view to grow in complexity. Make big components from small components, and you won't have an issue. If you feel it's getting complex, you can always reorganize it.
I know there's the setState method, but I still manually have to call that, right? So if I was working with a React view and a Backbone model [...]
You should search for "react backbone", and you'll find some blog posts and code examples. They're often used together. Feel free to add any links you found helpful here.
You're on the right path, however there are two things to fix. One is a bug, the other is a preferred pattern.
The bug: In the View, you are using this.props.text (good!), but you are using setState in the model listener. This sets the this.state.text value, which you are not using, so it won't work. setState should 'only' be used from inside the component itself - for all intents and purposes, think of it as a protected method. Instead, there is the setProps function, which is intended to be used only from outside the component.
The preferred pattern: The usage of setProps will soon be deprecated, as it causes a number of subtle issues. The better thing to do is just re-render the whole component each time. The right code in your case is:
// Data model: Backbone.js
var model = new Backbone.Model({text: "Please help! :)"});
// Create view class
var View = React.CreateClass({
render: function() {
return (
<p>{this.props.text}</p>
);
}
});
function rerender() {
React.renderComponent(
<View text={model.get("text")}>,
document.getElementById('view-container')
);
}
// Update view
model.on("change:text", function(model, newValue) {
rerender();
});
rerender();
Thank you for the replies guys,
So, am I correct in assuming that if I want the views to observe the data models, what I end up with is actually pretty close to Backbone view code, where you hook up event listeners in the intialize method? Here's a quick example that works:
var model = new Backbone.Model({
text: "Hey there :)"
});
var TextBox = React.createClass({
getInitialState: function() {
return this.props.model.toJSON();
},
componentWillMount: function() {
this.props.model.on("change:text", function(model, newText) {
this.setState({
text: newText
});
}, this);
},
render: function() {
return (
<p>{this.state.text}</p>
);
}
});
React.renderComponent(
<TextBox model={model} />,
document.getElementById('view-holder')
);
As I said this does work as intended. The view re-renders whenever the model's text property changes. Would this be considered "Good" React code, or should I hook this up differently?
I am mostly wondering how to organize things like modal windows, and dynamic pages like profiles. Should the viewModel only contain one profile view or contain all profiles loaded? This here just doesnt seem very "clean".
viewModel = {
profile: ko.observableArray([
new ProfileViewModel()
//... any others loaded
])
, createPostModal: {
input: ko.observable()
, submit: //do something to submit...
}
}
<div data-bind="foreach: profile"><!-- profile html --></div>
<div data-bind="with: createPostModal"></div>
This way doesn't seem very consistent. Is there anybody who has built a single page app with knockout that can offer some advice? Code samples would be appreciated.
We are just starting down this path at work, and so are not quite sure what we're doing. But here's the idea we have.
The page should be composed of any number of "components," possibly nested. Each component has a view model and one public method, renderTo(el), which essentially does
ko.applyBindings(viewModelForThisComponent, el)
It also could have the ability to render subcomponents.
Constructing or updating a component consists of giving it a model (e.g. JSON data from the server), from which it will derive the appropriate view model.
The app is then created by nesting a bunch of components, starting with a top-level application component.
Here is an example for a "hypothetical" book-managing application. The components are LibraryUI (displays a list of all book titles) and DetailsUI (a section of the app that displays details on a book).
function libraryBookViewModel(book) {
return {
title: ko.observable(book.title),
showDetails: function () {
var detailsUI = new BookDetailsUI(book);
detailsUI.renderTo(document.getElementById("book-details"));
}
};
}
function detailsBookViewModel(book) {
return {
title: ko.observable(book.title),
author: ko.observable(book.author),
publisher: ko.observable(book.publisher)
};
}
function LibraryUI(books) {
var bookViewModels = books.map(libraryBookViewModel);
var viewModel = {
books: ko.observableArray(bookViewModels);
};
this.renderTo = function (el) {
ko.applyBindings(viewModel, el);
};
}
function BookDetailsUI(book) {
var viewModel = detailsBookViewModel(book);
this.renderTo = function (el) {
ko.applyBindings(viewModel, el);
};
}
Note how we could make the book details appear in a jQuery UI dialog, instead of in a singleton #book-details element, by changing the showDetails function to do
var dialogEl = document.createElement("div");
detailsUI.renderTo(dialogEl);
$(dialogEl).dialog();
There are 3 frameworks out there that help with creating SPAs using Knockoutjs.
Durandal
Pagerjs
KnockBack
I have used Durandal and I really like it. Easy to use and has a lot of nice configurations so you can plug-in your own implementations. Also, Durandal is created by the same creator of Caliburn which was an very popular framework for building Silverlight/WPF applications.
Now in 2014, you probably want to use Knockout's component feature and Yeoman to scaffold your initial ko project. See this video: Steve Sanderson - Architecting large Single Page Applications with Knockout.js
[update april 5, 2013] at time of writing this answer was valid. Currently I would also suggest the Durandal JS approach as the way to go. Or check John Papa's Hot Towel or Hot Towelette SPA templates if you are using ASP.NET MVC. This also uses Durandal.
Original answer below:
I would like to point out Phillipe Monnets 4 part series about Knockout.js to you. He is the first Blogger I encounterd who splits up his example project in multiple files. I really like most of his ideas. The only thing I missed, was how to handle ajax / rest retrieved collections by using some kind of Repository / Gateway pattern. It's a good read.
Link to part 1: http://blog.monnet-usa.com/?p=354
Good luck!
I just open-sourced the mini SPA framework I put together with Knockout being the major component.
knockout-spa
A mini (but full-fledged) SPA framework built on top of Knockout, Require, Director, Sugar.
https://github.com/onlyurei/knockout-spa
Live Demo:
http://knockout-spa.mybluemix.net
Features
Routing (based on Flatiron's Director): HTML5 history (pushState) or hash.
Highly composable and reusable: pick modules/components for a page in the page-specific JS and they will be auto-wired for the page's HTML template
SEO ready (prerender.io)
Fast and lightweight (85 KB of JS minified and gizpped)
Two-tier bundle build for JS for production: common module that will be used by most pages, and page-specific modules that will be lazy-loaded
Organized folder structure to help you stay sane for organizing and reusing JS, CSS, HTML
Using Knockout 3.3.0+ so ready for Knockout's flavor of web component and custom tags (http://knockoutjs.com/documentation/component-overview.html)
All documentation are in the major dependencies' own homepages, so that you don't need to completely learn a new framework
Knockout http://knockoutjs.com
Require http://requirejs.org
Director https://github.com/flatiron/director
jQuery http://jquery.com
Sugar http://sugarjs.com