I am currently working on a Vue-Website that supports multiple languages. The selected Language is stored in a vuex-store and i have the the computed property lang like this
lang(){
return this.$store.state.lang
}
Now i use this property in v-ifs, shows, in other computed properties and so and it works pretty well. Theres only one thing where i run into problems and that's the multiselect (https://vue-multiselect.js.org/). the computed options property looks somthing like this
mappedOptions(){
return this.options.map(
(o, ind) => {
return {name: o.title[this.lang], code: ind}
}
);
}
Now the problem is, that this does not work. Probably the options are being initialized in the created lifecycle hook and therefore are not beeing updated later on. Do you have any clever ideas how I could use multiple languages with this vue-multiselect? I pretty much like the component apart from this issue so I would not like to switch.
Put a v-if="computedOption" on the multiselect, should solve the issue
Related
I'm a fan of Vue which a try to use on some occasions. Anyway, there is something I always found not so handy with it: reactivity lies within $data. Well not always, as external data can be tracked by Vue, as in computed properties, in templates… But I found this way uncomfortable and not always consistent (see another question about it, here Reactivity on Variables Not Associated With Data, Computed, etc). So my decision now is use $data as the main source of reactivity and stop trying to find other ways.
However, reactivity within $data poses me a problem in what is a common case for me: many pieces of data here and there in other imported objects. This makes even more sense as I consider Vue as the View end not the business logic. Those imported objects are sometimes complex and within Vue components, I found no way to cherry pick pieces of information and kind of ask Vue to bind to them. The only way was to declare entire objects in the $data section which makes tracking very heavy: loads of setters/getters when only one would be enough in a simple component, for example.
So I designed a class called 'Reactor' whose instances role is to install getter/setters on any piece data of my wish in a complex object (or more than one). Those instances are imported into Vue components and then $watchers of Reactor instances have properties which can contain as many functions as I wish which are called when pieces of data are altered through the Reactor. To make things simple by default is filled with the same property name as the data it bounds to. This precisely those function which will update $data when external data change.
class Reactor {
constructor() {
this.$watchers = {};
}
addProperty(originalObject, keyString, aliasKeyString) {
if(aliasKeyString === undefined) {
aliasKeyString = keyString;
}
if(this[aliasKeyString] !== undefined || originalObject[keyString] === undefined) {
const errorMessage = `Reactor: cannot add property '${aliasKeyString}'!`;
console.error(errorMessage);
throw errorMessage;
}
this.$watchers[aliasKeyString] = [];
Object.defineProperty(this, aliasKeyString, {
set(newValue) {
const oldValue = originalObject[keyString];
originalObject[keyString] = newValue;
this.$watchers[aliasKeyString].forEach((f) => {
if(typeof f === "function") {
f(newValue, oldValue, aliasKeyString);
}
});
},
get() {
return originalObject[keyString];
},
});
}
}
An example can be seen in the codepen here: https://codepen.io/Djee/pen/gyVZMG
So it's sort of an 'inverted' Vue which allows updating $data on external conditions.
This pattern also helped me resolve a case which was rather difficult before: have a double-bind on an input with a filter in-between which will set the input and its attached external value straight upon #change event only. This can be seen in the same codepen given above.
I was a little surprised to have found nothing taking this in charge in Vue itself. Did I miss something obvious? This is mainly the purpose of this somewhat long introduction. I had no time to check whether Vuex would solve this nicely.
Thanks for any comments as well.
I have a template called 'orientation' that contains arguments for a series of sliders whose values depend on an input from a different template called 'selector'.
When I change the input value on the selector template I would like orientation template to rerender with the new values for my sliders which are calculated using the new input value.
What would be the best way to do this, should I completely rerender the template or is there a more efficient way?
You can use a Session variable. You need to use
Session.set('variablename', value)
to set the variable in your selector template, and
Session.get('variablename')
to retrieve its value it in the template that needs it for stuff. Meteor reactivity should do the rest ;-)
Mathieu's session suggestion works great - Michel also mentioned reactive variables. You can use the ReactiveVar package, to achieve much the same result as using Session, as long as you define, export and import the variable accordingly (assuming you're using Meteor 1.3 or later).
e.g. in Template A:
//... set something to the Reactive Var in an event handler, e.g.
import { ReactiveVar } from 'meteor/reactive-var';
export let selector = new ReactiveVar();
Template.templateA.events({
'click .something':(event)=>{
selector.set('somethingImportant');
}
})
And in Template B:
// ... making sure you import the reactive var
import { selector } from '/path/to/templateA';
Template.templateB.helpers({
getSelector:()=>{
return selector.get()
}
});
More on ReactiveVar can be read over on Meteor's docs. One key thing from the docs to note is that:
ReactiveVars can hold any value, while Session variables are limited
to JSON or EJSON.
Which might be worth bearing in mind depending on how you're handling the data driving the reactivity of the sliders.
You would need to implement overservables which you can use to behave in reactive mode when the value changes.
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.
I have a form that uses ReactJS along with LinkStateMixin to implement two-way bindings between the form and the underlying model.
This works well, however there are some scenarios where I want additional changes to state to happen when specific field values change - i.e. if I change field 'A', I want to reset field 'B' to something else.
The obvious choice for this is to use a normal onChange handler, however since LinkStateMixin sets this internally, I cannot override it without breaking the mixin.
Obviously I could simply not use valueLink for the relevant components and implement the two-way bindings manually in my onChange handler along with the custom logic, however it would be a lot cleaner to be able to do both.
Is this possible in ReactJS - Or will I need to ditch using LinkStateMixin for anything that requires custom event handling logic?
Edit - Added really simple code example
fieldAChanged: function(){
this.setState({ fieldB: '' });
}
render: function(){ return (
<input valueLink={linkState('fieldA')} onChange={this.fieldAChanged} />
<input valueLink={linkState('fieldB')}/>
)}
My real scenario is somewhat more complex, but in a nutshell this is what I'm trying to do - have onChange handler fire when the field is changed, while still using two-way bindings to keep my viewmodel in sync without additional boilerplate. Unfortunately 'valueLink' overrides 'onChange' preventing this from working.
I should also clarify - I'm not asking "what is a possible way to do this", since there's plenty of obvious ways to do it. I'm asking "What is the idiomatic way to do this" in react.
I realize this is an old question, but I wanted to make you aware of a little open source library I wrote to solve this very issue: reactlink-pipe
You can easily have robust functions run before getting or setting values via valueLink (whatever you would have done in onChange), while at the same time you don't have to give up the syntactic sugar of ReactLink (i.e. you don't have to write a whole bunch of boilerplate onChange functions).
For example:
var pipeLink = require('reactlink-pipe');
function caps(text) { return text && text.toUpperCase(); }
var WithLink = React.createClass({
mixins: [LinkedStateMixin],
getInitialState: function() {
return { name: 'foo' };
},
render: function() {
// Will display "FOO", while this.state.name will still be "foo"
return (
<input type="text" valueLink={pipeLink(caps, this.linkState('name'))} />
);
}
});
I hope this helps you, or anyone else with a similar problem.
I am trying to use Ember.select to create a drop down list, my question is when it renders first time, I would like to set a default value. And once I change the selection, it won't check for default value until refresh the page.
Here is the code:
ET.selectedAppYearController = Ember.Object.create({
appYear: null,
isChanged: function () {
if (this.appYear != null) {
LoadETByAppYearETTypeID(this.appYear.text, ET.selectedEmailTypesController.emailType.email_template_type_id);
}
} .observes('appYear'),
setDefault: function() {
if (this.appYear.text == 2012) {
this.set('selection',2012);
}
} .observes('appYear')
});
The View:
{{view Ember.Select
contentBinding = "ET.appYearController.content"
selectionBinding="ET.selectedAppYearController.isDefault"
optionLabelPath="content.text"
optionValuePath="content.value"
}}
I guess i need to set something on selectionBinding...but what kind of value I should bind to?
1. the drop down list values are JSON type.
I was going to make a jsfiddle for this but it seems down at the moment. Will edit one in if it comes back up later as they usually make things clearer.
If you set the view to
{{view Ember.Select
contentBinding = "ET.appYearController"
selectionBinding="ET.selectedAppYearController.appYear"
optionLabelPath="content.text"
optionValuePath="content.value"
}}
and then set up the selectedAppYearController with something like:
ET.selectedAppYearController = Ember.Object.create({
appYear: ET.appYearController.get('lastObject')
}
then you should have the last element in your appYearController set as default, and when you change the selection ET.selectedAppYearController.appYear will reflect this.
Obviously if you want something other than the last element of your appYearController then you can set the value of appYear to whatever you want.
I'm way late to this party, but in the latest version of ember 0.9.7.1 there is a "prompt" attribute you can set in your Ember.Select. It looks something like,
{{view Ember.Select
prompt="This is selected by default"}}
Hope that helps some one.
It's hard to tell from your code example. One thing you could do is extract all your domain specific requirements and set up a live jsfiddle example that tries to accomplish what you are trying to do in a more generic way. http://jsfiddle.net/ You can see examples of using jsfiddle in other emberjs posts here.
But one thing I remarked is that your selectionBinding appears to be wrong. This should bind to the value you are trying to set, not a default. You could set a default in the controller if you like (just by assigning it to the value bound by selectionBinding). So I think your selectionBinding should be "ET.selectedAppYearController.appYear" if I understand your example correctly.
Not sure if anyone still needs that, but the way I did it is binding to the value with something like
{{view Ember.Select content=templates optionValuePath="content.id" optionLabelPath="content.name" value=selectedTemplateId class="form-control"}}
then in controller implement selectedTemplateId as computed property, working both as setter and getter:
selectedTemplateId: ( (key, value)->
# setter code:
if arguments.length > 1
#set('selTemplateId', value)
return value
# getter code
else
if #get('selTemplateId')?
return #get('selTemplateId')
else
return #get('templates')?.objectAt(0).id
).property()
Sorry for CoffeeScript instead of js. JS version at: http://goo.gl/5FZHFh
A bit of docs for computed properties: http://emberjs.com/api/classes/Ember.ComputedProperty.html