How to override event handler function of child component from parent component in react.js - event-handling

/** #jsx React.DOM */
var Button = React.createClass({
handleClick: function(){
console.log(' FROM BUTTON')
},
render: function() {
return <input type='button' onClick={this.handleClick} value={this.props.dname}/>;
}
});
var Text = React.createClass({
render: function() {
return <input type='text' onClick={this.handleClick} value={this.props.ival}/>;
}
});
var search = React.createClass({
handleClick: function() {
console.log('searching')
},
render: function(){
return (
<div>
<Text/>
<Button dname={this.props.dname} onClick={this.handleClick} />
</div>
);
}
});
React.renderComponent(<search dname='Look up' fname='Search'/> , document.body);
I have created a button and text component and included them in a search component now i want to override the default handleClick event of button with search component's handler.
But this.handleClick is pointing to button component's event handler.. please help..
i need FROM SEARCH on click instead i got FROM BUTTON..

You are 99% percent there.
React uses a one-way data-flow. So, events on nested components will not propagate to their parents.
You must propagate events manually
Change your <Button>s handleClick function to call the this.props.handleClick function passed in from it's <Search> parent:
var Button = React.createClass({
handleClick: function () {
this.props.onClick();
},
...
});
Attached is a fiddle of your original post, with the required change. Instead of logging FROM BUTTON, it will now alert searching.
http://jsfiddle.net/chantastic/VwfTc/1/

You need to change your Button component to allow such behaviour:
var Button = React.createClass({
handleClick: function(){
console.log(' FROM BUTTON')
},
render: function() {
return (
<input type='button'
onClick={this.props.onClick || this.handleClick}
value={this.props.dname} />
);
}
});
note the onClick={this.props.onClick || this.handleClick}.
That way if you pass an onClick prop when instantiating Button it will have a preference over the Button's handleClick method.

Or if you can execute both of them, you can put
class Button extends React.Component {
handleClick = () => {
console.log("from buttom");
if (this.props.hasOwnProperty('onClick')){
this.props.onClick();
}
};
You would check whether the object has the specified property and run it

Related

Is there a way to programme bingmap's infobox close button?

In the bingmaps documentation, you can add custom actions to the infobox. I would like to know if there's a similar way to program the default closeButton?
Ideally, I would like to be able to do something like this:
const infobox = new Microsoft.Maps.Infobox(selectedTipCoordinates, {
title: selectedTip.title,
description: selectedTip.description,
closeButton: () => console.log('hello')
});
Unfortunately close event handler could not be customized via InfoboxOptions object, so you could consider either to implement a custom HTML Infobox or override info window click handler. The following example demonstrates how to keep info window opened once close button is clicked and add a custom action:
Microsoft.Maps.Events.addHandler(infobox, 'click', handleClickInfoBox);
function handleClickInfoBox(e){
var isCloseAction = e.originalEvent.target.className == "infobox-close-img";
if(isCloseAction){
//keep info window open..
e.target.setOptions({visible: true});
//apply some custom actions..
console.log("Close button clicked");
}
}
function loadMapScenario() {
var map = new Microsoft.Maps.Map(document.getElementById("myMap"), {
center: new Microsoft.Maps.Location(47.60357, -122.32945)
});
var infobox = new Microsoft.Maps.Infobox(map.getCenter(), {
title: "Title",
description: "Description",
actions: [
{
label: "Handler1",
eventHandler: function() {
console.log("Handler1");
}
},
{
label: "Handler2",
eventHandler: function() {
console.log("Handler2");
}
},
{
label: "Handler3",
eventHandler: function() {
console.log("Handler3");
}
}
]
});
infobox.setMap(map);
Microsoft.Maps.Events.addHandler(infobox, 'click', handleClickInfoBox);
}
function handleClickInfoBox(e){
var isCloseAction = e.originalEvent.target.className == "infobox-close-img";
if(isCloseAction){
//keep info window open..
e.target.setOptions({visible: true});
//apply some custom actions..
console.log("Close button clicked");
}
}
body{
margin:0;
padding:0;
overflow:hidden;
}
<script type='text/javascript' src='https://www.bing.com/api/maps/mapcontrol?key=&callback=loadMapScenario' async defer></script>
<div id='myMap' style='width: 100vw; height: 100vh;'></div>
No, I don't think there's a way to wire the behavior of default close button differently. That said, you can approximate the desired outcome with a little more work: creating a custom infobox with the same style and then you'll have 100% control:
e.g. (notice the onClick handler on the close button div):
var center = map.getCenter();
var infoboxTemplate = '<div class="Infobox" style=""><a class="infobox-close" href="javascript:void(0)" onClick="function test(){ alert(\'test!\'); } test(); return false;" style=""><img class="infobox-close-img" src="data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE0cHgiIHdpZHRoPSIxNHB4IiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2ZXJzaW9uPSIxLjEiPjxwYXRoIGQ9Ik03LDBDMy4xMzQsMCwwLDMuMTM0LDAsN2MwLDMuODY3LDMuMTM0LDcsNyw3YzMuODY3LDAsNy0zLjEzMyw3LTdDMTQsMy4xMzQsMTAuODY3LDAsNywweiBNMTAuNSw5LjVsLTEsMUw3LDhsLTIuNSwyLjVsLTEtMUw2LDdMMy41LDQuNWwxLTFMNyw2bDIuNS0yLjVsMSwxTDgsN0wxMC41LDkuNXoiLz48L3N2Zz4=" alt="close infobox"></a><div class="infobox-body" style="max-width: 256px; max-height: 126px; width: 125px;"><div class="infobox-title" >{title}</div><div class="infobox-info" style=""><div>{description}</div></div><div class="infobox-actions" style="display: none;"><ul class="infobox-actions-list"><div></div></ul></div></div><div class="infobox-stalk" style="top: 73.8px; left: 55.5px;"></div></div>';
var infobox = new Microsoft.Maps.Infobox(center, {
htmlContent: infoboxTemplate.replace('{title}', 'myTitle').replace('{description}', 'myDescription'),
offset: new Microsoft.Maps.Point(-64, 16)
});

React setState from multiple input boxes

I'm working on a Recipe Box project and I have a program that allows the user to click a button which then displays input boxes that allows the user to add a new recipe to a list.
I have two inputs in the form. One for the name of the recipe being added, and one for the ingredients. The input for the name of the recipe works and allows the user to add a name, but the second input box is not updating the ingredients in state as it should.
Why isn't ingredientVal being updated in state? Why can't I enter text in the second input box?
import React, { Component } from 'react';
import RecipeList from './RecipeList';
import './App.css';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
items: ["Pumpkin Pie", "Spaghetti", "Onion Pie"],
ingredients:[
["Pumpkin Puree", "Sweetened Condensed Milk", "Eggs", "Pumpkin Pie Spice", "Pie Crust"],
["Noodles", "Tomato Sauce", "(Optional) Meatballs"],
["Onion", "Pie Crust"]
],
inputVal: '',
ingredientVal: '',
showRecipe: false
};
}
// Get text user inputs for recipe
handleChange = (event) => {
this.setState({inputVal: event.target.value});
};
handleIngredientChange = (event) => {
this.setState({ingredientVal: event.target.value});
}
// When user submits recipe this adds it
onSubmit = (event) => {
event.preventDefault()
this.setState({
inputVal: '',
items: [...this.state.items, this.state.inputVal],
ingredientVal: '',
ingredients: [...this.state.ingredients, this.state.ingredientVal]
});
}
onIngredientSubmit = (event) => {
event.preventDefault()
this.setState({
ingredientVal: '',
ingredients: [...this.state.ingredients, this.state.ingredientVal]
});
}
// Shows recipe
AddRecipe = (bool) => {
this.setState({
showRecipe: bool
});
}
render() {
return (
<div className="App">
<h3>Recipe List</h3>
<RecipeList items={this.state.items} ingredients={this.state.ingredients} />
<button onClick={this.AddRecipe}>Add New Recipe</button>
{ this.state.showRecipe ?
<div>
<form className="Recipe-List" onSubmit={this.onSubmit}>
<div className="Recipe-Item">
<label>Recipe Name</label>
<input
value={this.state.inputVal}
onChange={this.handleChange} />
</div>
<div className="Recipe-Item">
<label>Ingredients</label>
<input
value={this.state.ingredientVal}
onChange={this.state.handleIngredientChange} />
</div>
<button>Submit</button>
</form>
</div>
:null
}
</div>
);
}
}
You're calling this.state.handleIngredientChange as your onChange when it should be this.handleIngredientChange

Knockout event binding with condition

I want to bind some events to an element , using the knockout "event" binding
But I want all of the listed events to be bound only with a specific case.
The viewmodel:
function vm(){
var self = this;
self.isEditMode = ko.observable(false);//can be changed to true
self.events = ko.observable({
down: function () {
console.log("down")
},
up: function () {
console.log("up")
},
hover: function () {
console.log("hover")
}
});
}
and the Html:
<div style="border:1px solid red;width:50px;height:50px"
data-bind="event:{mousedown:events().down,mouseup:events().up,mouseover:events().hover}:null"></div>
<button data-bind="click:function(){isEditMode(!isEditMode())}">change </button>
I tried:
<div data-bind="event:isEditMode()?{mousedown:events().down,mouseup:events().up,mouseover:events().hover}:null"></div>
But it did not work for me.
I think the best way to do it is by using custom bindingHandlers, but I dont know how.
Thank you very much for your help!
You can simplify the the binding by moving some logic into the view model
<div style="border:1px solid red;width:50px;height:50px"
data-bind="event: {
mousedown: down,
mouseup:up,
mouseover:hover }" > </div>
and view model like this
function vm() {
var self = this;
this.isEditMode = ko.observable(true);
down = function() {
if(this.isEditMode())
{
console.log("down")
}
};
up = function() {
if(this.isEditMode())
{
console.log("up")
}
};
hover = function() {
if(this.isEditMode())
{
console.log("hover")
}
};
}
var viewModel = new vm();
ko.applyBindings(viewModel);
Another option is to place the condition in the markup itself as two separate blocks using an "if" binding to determine which ones gets shown and bound.
function vm() {
var self = this;
self.isEditMode = ko.observable(false); //can be changed to true
self.events = ko.observable({
down: function() {
console.log("down");
},
up: function() {
console.log("up");
},
hover: function() {
console.log("hover");
}
});
}
ko.applyBindings(new vm());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<!--ko if: isEditMode()-->
<div style="border:1px solid red;width:50px;height:50px" data-bind="event:{
mousedown:events().down,
mouseup:events().up,
mouseover:events().hover
}">
Edit Mode
</div>
<!--/ko-->
<!--ko if: !isEditMode()-->
<div style="border:1px solid red;width:50px;height:50px">
Read Only
</div>
<!--/ko-->
<button data-bind="click:function(){isEditMode(!isEditMode())}">change </button>

changeRequest in Alloy 2.5 and Liferay 6.2 cannot be called

I am trying to migrate a portlet from Liferay 6.1 to 6.2 and forced to adapt the Alloy code to 2.5 version and the aui-pagination part:
pagination = new A.Pagination({
circular: false,
containers: '.pagination',
on: {
changeRequest: function(event) {
var newState = event.state;
this.setState(newState);
}
},
total: 10,
});
But whenever I call the changeRequest() of the pagination instance from other functions I get errors:
this._pagination.changeRequest();
Is there any solution for this?
Your question is a little strange. How would you call changeRequest() without passing an event in your example? And why set the state from the event when that's already happening automatically?
To answer the more generic question that you are asking, there are several potential solutions to calling the changeRequest() function programmatically:
Define a named function and set it to be the changeRequest() function:
function changeRequest() {
console.log('changeRequest function called!');
}
var pagination = new Y.Pagination({ /* ...your code here... */ });
pagination.on('changeRequest', changeRequest);
// OR if you don't need to access the pagination component
// in your changeRequest() method
new Y.Pagination({
/* ...your code here... */
on: {
changeRequest: changeRequest
}
});
This method will only work if you do not need to use the event parameter, or if you only use the event parameter when the actual event occurs, or if you construct the event parameter yourself.
Runnable example using your code:
YUI().use('aui-pagination', function(Y) {
var pagination = new Y.Pagination({
circular: false,
containers: '.pagination',
total: 10,
});
function changeRequest(event) {
if (event) {
alert('changeRequest called with event');
var newState = event.state;
pagination.setState(newState);
} else {
alert('changeRequest called without event');
}
}
pagination.after('changeRequest', changeRequest);
pagination.render();
Y.one('#button').on('click', function() {
changeRequest();
});
});
<script src="http://cdn.alloyui.com/2.0.0/aui/aui-min.js"></script>
<link href="http://cdn.alloyui.com/2.0.0/aui-css/css/bootstrap.min.css" rel="stylesheet"></link>
<br />
<button id="button">call <code>changeRequest()</code></button>
Call pagination.next() or pagination.prev():
YUI().use('aui-pagination', function(Y) {
// ...your code here...
pagination.next();
});
Runnable example using your code:
YUI().use('aui-pagination', function(Y) {
var pagination = new Y.Pagination({
circular: false,
containers: '.pagination',
total: 10,
on: {
changeRequest: function(event) {
alert('changeRequest called with event');
var newState = event.state;
pagination.setState(newState);
}
}
}).render();
Y.one('#button').on('click', function() {
pagination.next();
});
});
<script src="http://cdn.alloyui.com/2.0.0/aui/aui-min.js"></script>
<link href="http://cdn.alloyui.com/2.0.0/aui-css/css/bootstrap.min.css" rel="stylesheet"></link>
<br />
<button id="button">call <code>changeRequest()</code></button>
Simulate a click event on one of the pagination items:
YUI().use('aui-pagination', 'node-event-simulate', function(Y) {
// ...your code here...
pagination.getItem(1).simulate('click');
});
Runnable example using your code:
YUI().use('aui-pagination', 'node-event-simulate', function(Y) {
var pagination = new Y.Pagination({
circular: false,
containers: '.pagination',
total: 10,
on: {
changeRequest: function(event) {
alert('changeRequest called with event');
var newState = event.state;
pagination.setState(newState);
}
}
}).render();
Y.one('#button').on('click', function() {
pagination.getItem(1).simulate('click');
});
});
<script src="http://cdn.alloyui.com/2.0.0/aui/aui-min.js"></script>
<link href="http://cdn.alloyui.com/2.0.0/aui-css/css/bootstrap.min.css" rel="stylesheet"></link>
<br />
<button id="button">call <code>changeRequest()</code></button>

How to make the form submit with normal button?

Here is the code I used.
With a click function, I made the POST action to the controller..
$('#btn1').click(function (e) {
$.post($('#frmLogin').attr('action'), $('#frmLogin').serialize(), function (data) {
});
});
#using (Html.BeginForm("Login", "Login", new { Model }, FormMethod.Post, new { id = "frmLogin" }))
{
<input type="button" id="btn1"/>
});
Call this function on clicking your normal button
function form_submit()
{
document.getElementById('formID').submit();
}
or use this jquery
$( "#btn1" ).click(function() {
$( "#frmLogin" ).submit();
});