React interaction between components - javascript

I have a parent component which looks like the following
UploadView.js
getInitialState: function () {
return {
file: null,
mappingType: null
}
},
setMappingType: function(){
this.setState({
mappingType: this.state.mappingType
});
},
render: function(){
<FileDropdown mappingType={this.setMappingType}/>
<FileUpload mappingType={this.state.mappingType}/>
}
FileDropDown is a child component that presents the user with a dropdown feature and looks as the following
FileDropDown.js
pickedOption: function(event){
var option = event.nativeEvent.target.selectedIndex;
var value = event.nativeEvent.target[option].text;
this.props.mappingType = value;
},
render: function(){
return (
<select name="typeoptions" id="mappingTypes" onChange={this.pickedOption}>.....</select>
)
}
I am trying to understand, if the above the right way to update the state and will updating/setting the value this.props.mappinType in FileDropDown will set the updated value to the state mappingType in the parent UploadView

If I understand it correctly, you send a function (or method) called setMappingType from UploadView.js to FileDropDown.js. When picking an option in FileDropDown.js at the method pickedOption you will call this function, causing the parent component to update.
Instead of doing that, you actually assigned the value to the props. In other words, change this
this.props.mappingType = value;
into
this.props.mappingType(value);
Then change the method setMappingType to a function which actually receives this value
setMappingType: function(value){
this.setState({
mappingType: value
});
},
Oh and something else, unrelated to your question, you can retrieve your value using refs like so:
pickedOption: function(event){
var value = this.refs._myRef.value;
},
render: function(){
return (
<select ref='_myRef' onChange={this.pickedOption}>
<option value='1'>One</option>
<option value='2'>Two</option>
</select>
)
}
I hope it helps ;)

Short answer, no, it won't work this way.
You need to change your setMappingType function to:
setMappingType: function(value){
this.setState({
mappingType: value
});
},
As you currently have it, it will just always be set to null because you are setting the state variable mappingType to the value of the state variable mappingType which is initially null. So it will just stay that way.
The proper way to pass the value to the parent function is like this:
pickedOption: function(event){
var option = event.nativeEvent.target.selectedIndex;
var value = event.nativeEvent.target[option].text;
this.props.mappingType(value);
},
Assigning it as you have, won't work.
I''ll admit I'm not an expert in React, but to my understanding this is how things need to be done. Hope this helps.

Related

AngularJs: ng-if reacting too late

Im using ui.router and including my navigation like this in my main html file:
<header ng-if-start="logedin()"></header>
<navigation ng-if-end="logedin()"></navigation>
the logedin() boolean will be set through the angular.module().run() in this function:
$rootScope.$on('$stateChangeStart', function(e, to)
If i click logout in one of the navigation, the controller of the navigation will trigger this function:
$scope.logout = function() {
store.remove('jwt');
$state.go('login');
}
The Problems is after the $state.go the navigation is not hiding, but after refreshing the page.
Do i have to rerender the main index template/view (and then how)? Or how could I solve this problem?
So I solved it myself. Sorry for not providing the logedin() method.
The problem was:
$scope.logedin = function() {
return $rootScope.logedin
}
The $rootScope.logedin was set in the angular.module().run()-function.
To solve it i had to create a simple getter/setter service.
angular.module('sample')
.service('sharedLogedIn', function () {
var property = true;
return {
getProperty: function () {
return property;
},
setProperty: function(value) {
property = value;
}
};
});
Good to know the issue was resolved. What might be happening is your values are not propagated... I might do this to troubleshoot:
<header ng-if="loggedinBool"></header>
<navigation ng-if="loggedinBool"></navigation>
1) Assign loggedin() value to a scope model or service property (preferably) loggedinBool and see if the values are propagated after logout which changes loggedinBool value.
2) If that does not work try $broadcast/$emit in loggout and capture that to change value of loggedinBool. This should automatically provide two-way-binding else try $scope.digest() or $scope.apply() method to see if values propagates.

meteor: recalculating helper value after typing some input values

My template looks like this:
<input type="text" name="inputValue" id="inputVal">
{{#each group}}
<section>
<h2>{{title}}</h2>
{{#each element}}
<p>{{description}} {{numberValue}} {{unit}}</p>
{{/each}}
</section>
{{/each}}
So far this is working. But now I want to do a little calculating by multiplicate the value of the inputfield with the numberValue (group.element.numberValue) and use this with {{calculatedValue}} in the template:
<p>{{description}} {{calculatedValue}} {{unit}}</p>
Therefore I need a helper:
Template.usedTemplate.helpers({
'calculatedValue': function() {
return document.getElementById("#inputVal").value * this.numberValue;
}
});
I hope this is the correct coding for creating the helper as the values are used in an each-loop.
But my problem is, that the user types the inputValue after the site has been loaded. So what do I have to do, to recalculate the rows after the user typed some values in the input field?
(Also it would be great if there would be a kind of output info text at the beginning as there is no value given by default. After typing some value the result is been shown.)
I think you can simply use Session or ReactiveVar to solve your problem
Session.setDefault("input-value", "")
Template.usedTemplate.events({
'keypress #inputVal': function (event) {
Session.set("input-value", Template.instance().$("#inputVal").val());
}
});
Template.usedTemplate.helpers({
'calculatedValue': function() {
return Session.get("input-value") * this.numberValue;
}
});
or use ReactiveVar
Template.usedTemplate.onCreated(function(){
this.inputValue = new ReactiveVar("")
})
Template.usedTemplate.events({
'keypress #inputVal': function (event) {
Template.instance().set(Template.instance().$("#inputVal").val());
}
});
Template.usedTemplate.helpers({
'calculatedValue': function() {
return Template.instance().get() * this.numberValue;
}
});
I suggest using ReactiveVar, because Session is global val.
And you should add ReactiveVar to your app before using.
meteor add reactive-var
I haven't tested this code, but try this:
var userInput = new Tracker.Dependency;
Template.usedTemplate.events({
'keypress #inputVal': function (event) {
userInput.changed();
}
});
Template.usedTemplate.helpers({
'calculatedValue': function() {
userInput.depend();
return Template.instance().$("#inputVal").val() * this.numberValue;
}
});
Side note: document.getElementById() already expects an ID as an argument, so you don't need the # prefix.
Normally, the calculatedValue helper will not update automatically because it contains no reactive method calls, such as someCollection.find() or Session.get('xyz'). But using Tracker.Dependency, you can make anything reactive.

How to reload input value in React / Javascript with Virtual DOM?

I have a problem with reload input value.
<input type="email" ref="email" id="email" value={this.props.handlingAgent.email}/>
after that i use
this.props.handlingAgent.email = "asd"
In debugger value of this.props.handlingAgent.email is actually asd, but in input is still old value. How to refresh that value without JQuery? Shouldn't it refresh automatically?
First, props are what's been passed onto you. View them as function parameters. The child really shouldn't go modify them since it breaks whatever assumption the parent has and makes your UI inconsistent.
Here, since the prop's passed onto you, you want to get a handler from parent that you can call to notify your change:
var App = React.createClass({
getInitialState: function() {
return {inputValue: ''};
},
handleChange: function(value) {
console.log('Value gotten back from the child: ' + value);
this.setState({
inputValue: value
});
},
render: function() {
return <Field onChange={this.handleChange} inputValue={this.state.inputValue} />;
}
});
var Field = React.createClass({
handleChange: function(event) {
// Make sure the parent passes the onChange to you as a prop
// See what I did here? It's not the actual DOM onChange. We're manually
// triggering it based on the real onChange fired by the `input`
this.props.onChange(event.target.value);
},
render: function() {
// I named the value prop `inputValue` to avoid confusion. But as you can
// see from `onChange`, it'd be nicer to name it just `value`
return <input value={this.props.inputValue} onChange={this.handleChange} />;
}
});
So yes, it does refresh "automatically", if you tell the parent to change. Instead of modifying what's been passed onto you, you use vanilla callbacks to the parent by passing it your new value. Then it flushes down the same value (or different, if fits) down to you.

KnockoutJS select fires onChange after setting default value

I have set up the following select for changing semesters on page. When the select detects a change, the changeSemesters function is fired, which runs an AJAX that replaces the current data on the page with data specific to the selected semester.
View
<select data-bind="options: semestersArr, optionsText: 'name', optionsValue: 'id', value: selectedSemester, event: { change: changeSemesters }"></select>
ViewModel
this.selectedSemester = ko.observable();
//runs on page load
var getSemesters = function() {
var self = this, current;
return $.get('api/semesters', function(data) {
self.semestersArr(ko.utils.arrayMap(data.semesters, function(semester) {
if (semester.current) current = semester.id;
return new Model.Semester(semester);
}));
self.selectedSemester(current);
});
};
var changeSemesters = function() {
// run ajax to get new data
};
The problem is that the change event in the select fires the changeSemester function when the page loads and sets the default value. Is there a way to avoid that without the use of a button?
Generally what you want to do in these scenarios is to use a manual subscription, so that you can react to the observable changing rather than the change event. Observables will only notify when their value actually changes.
So, you would do:
this.selectedSemester.subscribe(changeSemesters, this);
If selectedSemester is still changing as a result of getting bound, then you can initialize it to the default value.
this.selectedSemester = ko.observable(someDefaultValue);

Ember.js checkbox for array controller computed property

I have a checkbox that I would like to trigger a simple 'select all' functionality. The problem is that I can't figure out how to connect the checkbox's action to an action in my controller so that I can actually update the records.
App.LanguagesController = Ember.ArrayController.extend({
actions: {
toggleAllVisibility: function() {
var newVisibility = !this.get('allAreVisible');
var needingVisibilityChange = this.filterBy('visible', !newVisibility);
needingVisibilityChange.setEach('visible', newVisibility);
}
},
allAreVisible: function(param) {
return this.filterBy('visible', false).get('length') === 0;
}.property('#each.visible'),
});
In my template, I have the following input helper
{{input type='checkbox' checked=allAreVisible}}
This properly updates the checkbox when I change the elements manually (i.e. if all of them are selected, then checkbox updates), but no actions fire when I toggle the checkbox.
It looks like in older versions of Ember.js I could simply add an action parameter to the input helper but that doesn't work anymore. I'm assuming I need to setup something that observes when the computed property attempts to change, but I couldn't find anything in the docs or other help.
I also tried extending checkbox to send the click event:
App.AllLanguagesCheckbox = Ember.Checkbox.extend(Ember.ViewTargetActionSupport, {
click: function() {
this.triggerAction({
action: 'toggleAllVisibility'
});
}
});
And then loaded that in my template with
{{view App.AllLanguagesCheckbox checkedBinding=allAreVisible}}
That allows the checkbox to trigger the action, but does not update based on the computed property in the controller.
I feel like I'm missing something obvious here.
EDIT
Based on kingpin2k's answer below, here's the working controller code:
App.LanguagesController = Ember.ArrayController.extend({
toggleAllVisibility: function() {
var newVisibility = !this.get('controller').get('allAreVisible');
var needingVisibilityChange = this.get('controller').filterBy('visible', !newVisibility);
needingVisibilityChange.setEach('visible', newVisibility);
},
allAreVisible: function(param) {
return this.filterBy('visible', false).get('length') === 0;
}.property('#each.visible'),
});
It's not called with the normal scope so you have to explicitly go through the controller to get the model array, but it works as expected now.
Here's the accompanying input helper:
{{input type='checkbox' checked=allAreVisible change=toggleAllVisibility}}
The problem is your checkbox is connected to a computed property, the computation should derive the value (aka you shouldn't be setting it), which is what would be happening when someone tries to check.
_allAreVisible:false,
allAreVisible: function(param) {
if(this.filterBy('visible', false).get('length') === 0){
// set to true;
// else set to false
}.observes('#each.visible'),
http://emberjs.jsbin.com/abODIKoj/1/edit

Categories